Jiahong 的个人博客

凡事预则立,不预则废


  • Home

  • Tags

  • Archives

  • Navigation

  • Search

NLP——DeepSpeed框架介绍

  • 参考链接:
    • 官方文档:deepspeed.readthedocs.io

整体介绍

  • DeepSpeed 是由微软开发的开源的深度学习优化库,旨在提高大规模模型训练的效率和可扩展性
  • DeepSpeed 框架的核心技术有:
    • ZeRO 冗余优化技术 :通过分布式内存管理,将模型参数、梯度和优化器状态进行分区,大幅降低显存占用,是首次支持千亿级参数模型训练的框架
    • 3D 并行策略 :支持数据并行、流水线并行和张量切片模型并行,并可灵活组合
    • 混合精度训练 :自动混合精度训练(AMP)将单精度和半精度浮点数结合使用,降低内存需求的同时提升计算效率
    • 智能推理优化器 :支持张量并行与异构内存技术,提供低延迟高吞吐的分布式推理服务,可将成本降低 70%
    • 全链路内存管理 :集成 CPU 卸载与显存碎片整理技术,单卡即可训练百亿级模型,资源利用率提升 6 倍
  • DeepSpeed 框架的组件构成有:
    • Apis :提供易用的 API 接口
    • 运行时组件 :管理、执行和性能优化,基于 Python 语言实现,负责部署训练任务到分布式设备、数据分区、模型分区等
    • 底层内核 :用 C++ 和 CUDA 实现,优化计算和通信
  • DeepSpeed 生态兼容性极好 :原生兼容 PyTorch 与 Hugging Face 生态,通过简洁 API 可快速迁移项目,开发效率提升 300%

安装 DeepSpeed

  • 仅需一行安装命令即可:

    1
    pip install deepspeed
  • 暗转完成后,可以使用 ds_report 命令验证安装是否成功

    • 这个命令可以查看环境配置信息

DeepSpeed 的使用

  • 代码修改 :仅仅需要非常少的代码修改即可将原始训练代码切换到 DeepSpeed 框架上(DeepSpeed 与 PyTorch 无缝集成,只需少量修改即可启用加速)

    • 第一步:通过 deepspeed.initialize 将模型包装为 DeepSpeed 引擎,自动应用优化
    • 第二步:使用 model_engine.backward 和 model_engine.step 替换PyTorch原本的 loss.backward() 和 optimizer.step()
  • 配置 DeepSpeed :DeepSpeed 的优化行为通过 JSON 配置文件(ds_config.json)指定

    • 一般的项目都会自带一些配置好的 .json 文件示例,可直接修改使用
  • 运行训练 :使用 DeepSpeed 的命令行工具启动训练

    • 单节点训练命令为:

      1
      deepspeed train.py --deepspeed_config ds_config.json
    • 对于多节点集群,使用下面的命令:

      1
      deepspeed --num_gpus 8 --num_nodes 2 train.py --deepspeed_config ds_config.json
      • 其中--num_gpus 指定每节点使用的 GPU 数量,--num_nodes 指定集群中的节点数
    • 其他常用参数:

      • 使用--log_dir 参数启用日志记录,监控内存使用、训练速度等
  • 特别说明:使用了 DeepSpeed 框架的代码需要使用 deepspeed 命令来启动

    • 补充:直接使用 python 命令启动时会出现 deepspeed.initialize 调用的错误
    • 原因:DeepSpeed 作为一个分布式训练库,需要特殊的启动器来管理多个进程和 GPU 之间的通信和资源分配

DeepSpeed 使用示例(基于 PyTorch)

  • DeepSpeed 使用示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    import torch
    import torch.nn as nn
    from torch.utils.data import DataLoader, Dataset
    import deepspeed

    class DiyModel(nn.Module):
    def __init__(self, input_dim, output_dim):
    super(DiyModel, self).__init__()
    self.fc = nn.Sequential(
    nn.Linear(input_dim, 128),
    nn.ReLU(),
    nn.Linear(128, output_dim)
    )

    def forward(self, x):
    return self.fc(x)

    class RandomDataset(Dataset):
    def __len__(self):
    return 1000

    def __getitem__(self, idx):
    x = torch.randn(32)
    y = torch.randint(0, 10, (1,)).item()
    return x, y

    # 初始化模型和数据(无需为 DeepSpeed 特别处理)
    model = DiyModel(32, 10)
    train_dataset = RandomDataset()
    train_loader = DataLoader(train_dataset, batch_size=32)

    # 初始化 DeepSpeed 引擎(仅需使用 deepspeed.initialize 初始化模型得到 model_engine 即可)
    # 注:这一步会自动将模型参数移动到 GPU 上,下面使用的数据也需要将数据移动到对应的 GPU 上才能运行,否则会出现 设备不一致的错误(CPU vs GPU)
    # zero_optimization 字段的 stage 键值对应如下效果
    ## stage: 0:不使用 ZeRO 优化(默认值是 0)
    ## stage: 1:优化器状态分片
    ## stage: 2:优化器状态和梯度分片
    ## stage: 3:优化器状态、梯度和参数分片(最高内存效率)
    model_engine, optimizer, _, _ = deepspeed.initialize(
    args=None,
    model=model,
    model_parameters=model.parameters(),
    config={
    "train_batch_size": 32,
    "optimizer": {
    "type": "Adam",
    "params": {
    "lr": 0.001,
    "betas": [0.9, 0.999]
    }
    },
    "fp16": {
    "enabled": True,
    "loss_scale": 0,
    "loss_scale_window": 1000,
    "initial_scale_power": 16
    },
    "zero_optimization": {
    "stage": 2, # 这里指定Zero层级(0、1、2、3)
    "offload_optimizer": {
    "device": "cpu" # 可选:指定优化器卸载设备
    }
    }
    }
    )

    # 训练过程(训练时不再使用原来的模型,使用 model_engine)
    for epoch in range(10):
    for batch_idx, (data, target) in enumerate(train_loader):
    # 将数据挪到和模型相同的 GPU 上
    device = model_engine.device
    model_dtype = next(model_engine.parameters()).dtype # 通过模型的第一个参数获取dtype
    data = data.to(device, dtype=model_dtype) # 将数据移至模型所在设备并转换为与模型相同的dtype
    target = target.to(device)

    outputs = model_engine(data) # 使用(deepspeed.initialize 初始化得到的)model_engine 来进行前向过程
    loss = nn.CrossEntropyLoss()(outputs, target)
    model_engine.backward(loss) # 使用 model_engine 来进行后向过程
    model_engine.step() # 使用 model_engine 来更新模型参数(注:此时不再需要显示调用 optimizer)
    if batch_idx % 100 == 0:
    print(f"Epoch {epoch}, Batch {batch_idx}, Loss: {loss.item()}")

附录:如何指定目标 GPU

启动时指定 CUDA_VISIBLE_DEVICES 环境变量

  • 在运行命令前设置环境变量来限制 DeepSpeed 可见的 GPU:

    1
    CUDA_VISIBLE_DEVICES=0,1,2,3 deepspeed your_script.py --args ...
  • 该方案是最常用的方法 ,且适用于常见的很多框架

在 Python 脚本中设置环境变量

  • 也可以在Python脚本中通过os.environ设置这个环境变量:

    1
    2
    3
    4
    5
    6
    7
    import os
    os.environ["CUDA_VISIBLE_DEVICES"] = "0,1" # 指定使用GPU 0和1

    import deepspeed
    import torch

    # 训练逻辑
  • 注:需要在导入DeepSpeed或PyTorch之前设置

  • 该方案同样适用于常见的很多框架,但因为需要修改代码,不常用

使用 deepspeed 命令的 --include 参数

  • 如果使用的是 DeepSpeed 的 launcher,也可以通过--include参数指定使用的 GPU:
    1
    deepspeed --include localhost:0,1 your_script.py --args ...

多机多卡如何指定 GPU

  • 如果你使用的是单机多卡,以上方法都能很好地工作
  • 对于多机多卡训练,通常需要结合 deepspeed 命令的其他参数如 --hostfile 等一起使用

附录:数据加载位置管理

  • 由于 deepspeed 会自动将模型参数加载到指定 GPU 上,所以数据也要加载到指定 GPU,否则会出现设备不一致的错误
  • 加载命令如下(亲测解决方案):
    1
    2
    device = model_engine.device
    data = data.to(device)

附录:混合精度训练数据格式管理

  • 由于 deepspeed 在启动混合精度训练时,可能会按照指定格式来指定参数形式,此时数据也需要转换为指定类型
  • 解决方式如下(亲测遇到错误时的解决方案):
    1
    2
    model_dtype = next(model_engine.parameters()).dtype # 通过模型的第一个参数获取类型,注:写这么复杂的原因是,当前还不支持直接调用 model_engine.dtype()
    data = data.to(device, dtype=model_dtype)

NLP——DeepSeek-R1相关技术总结

本文主要介绍 DeepSeek-R1 相关的解读,笼统而简单的介绍,详情可查看本人的其他博客

  • 相关链接:
    • 开源技术报告:DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning, 20250120
    • 博客:DeepSeek-R1 技术报告解读 - 绝密伏击的文章 - 知乎

Background

2025年01月20日,deepseek 正式发布 DeepSeek-R1,并同步开源模型权重

  • 开源 DeepSeek-R1 推理大模型,与 o1 性能相近。‍‍(冷启动 SFT -> RL -> COT + 通用数据SFT(80W)-> 全场景RL)
  • 开源 DeepSeek-R1-Zero,预训练模型直接 RL,不走 SFT。(纯强化学习)
  • 开源用 R1 数据蒸馏的 Qwen、Llama 系列小模型,蒸馏模型超过 o1-mini 和 QWQ。(直接使用80W数据进行SFT)

报告核心说明

  • 首次验证了纯 RL 也可以训练出大模型的推理能力
  • aha moment:顿悟时刻,主要指DeepSeek-R1-Zero模型训练过程中,模型在某个关键时刻突然学会自我反省的情况

DeepSeek-R1-Zero

  • 预训练后直接进入RL阶段

DeepSeek-R1-Zero奖励模型

  • 直接采用了一种基于规则的奖励系统,包括两种奖励模型作为评估指标,分别是准确率奖励模型和格式奖励模型
    • 准确率奖励模型 :评估response是否准确
    • 格式奖励模型 :评估格式是否准确,具体来说,格式奖励要求模型将思考过程放在“和”标签之间

DeepSeek-R1-Zero演化过程


DeepSeek-R1

DeepSeek-R1 使用了冷启动 + 多阶段训练的方式:

  • 阶段1:使用少量高质量的 CoT 数据进行冷启动,预热模型。(相较于直接RL,冷启动预热能让模型快速进入稳定训练阶段)
  • 阶段2:进行面向推理的强化学习,提升模型在推理任务上的性能
  • 阶段3:使用拒绝采样和监督微调,进一步提升模型的综合能力
  • 阶段4:再次进行强化学习,使模型在所有场景下都表现良好

DeepSeek-R1之MoE

  • 参考链接:Deepseek-MOE架构图解(V1->V2->V3) - 假如给我一只AI的文章 - 知乎

普通的MoE

  • Mixture of Experts,混合专家模型。最早1991年的论文《Adaptive Mixtures of Local Experts》中提出了混合专家模型的雏形,架构图如下:

Switch Transformer中的MoE

  • 原始论文:Switch Transformers: Scaling to Trillion Parameter Models with Simple and Efficient Sparsity, Google

  • 架构图(后面会在和DeepSeekMoE比较时给出数学表达式)

  • Transformer-MoE的本质是对Transformer层的FNN进行改进,改为带MoE的FNN

DeepSeekMoE(DeepSeek-V1)

  • 原始论文:DeepSeekMoE: Towards Ultimate Expert Specialization in Mixture-of-Experts Language Models

  • 传统的Transformer和MoE

  • 问题:上图中说 \(\boldsymbol{e}_i^l\) 是每个专家的质心(Centroid),但是未给出这个质心是怎么来的,是否可训练?

    • 回答:在上述原始论文中确实没有说清楚,但是一些文章中有提到,比如 GSHARD: SCALING GIANT MODELS WITH CONDITIONAL COMPUTATION AND AUTOMATIC SHARDING 中 Algorithm1 提到输入门控网络的该值是可训练的权重,上图中的乘法实际上也就是一个线性层,实现可以如下:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      def __init__():
      ...
      # 门控网络
      self.gate = nn.Linear(input_dim, num_experts)

      def forward(self, x):
      # 计算门控权重
      gate_logits = self.gate(x)
      gate_probs = F.softmax(gate_logits, dim=-1)

      # 选择top-k专家
      topk_values, topk_indices = torch.topk(gate_probs, self.k)
      topk_gates = topk_values / topk_values.sum(dim=-1, keepdim=True)
  • 个人思考:以上被选中的FNN系数和不为1(小于1),但因为在每一个Transformer层中,FNN的结果和上一层的隐向量叠加后,都有LayerNorm存在(将每个token的隐向量分别归一化为均值为0,方差为1的向量),所以隐向量的值不会越来越小,在V3版本的公式中,会考虑在选择了 TopK FNN 后,再进行一次归一化

  • 改进一:Fine-Grained Expert Segmentation,更精细化的专家拆分

    • DeepSeekMoE的精细化MoE:可以看出DeepSeekMoE中将原始的 \(N\) 个FNN扩展为 \(mN\) 个(注意:只是拆分的更细,参数总量是相同的),选择的FNN数量也从 \(K\) 个扩展到 \(mK\) 个
  • 改进二:Shared Expert Isolation,独立的共享专家

    • 使用了 \(K_s\) 个固定的共享专家,需要路由的专家数量为 \(mN - K_s\)
    • 最终的MOE层输出由3部分组成,共享专家的输出结果 + Top_K个路由专家输出结果 + 残差
  • 改进三:Load Balance Consideration,负载均衡考量

    • Expert-Level Balance Loss(专家级别的负载均衡损失函数):
    • Device-Level Balance Loss(设备级别的负载均衡损失函数):
  • DeepSeekMoE结构图:

  • 其他说明:DeepSeek-R1共61个Transformer层,其中前三个层是正常的FNN层,后面的4-61层均用MoE取代FNN层

DeepSeek-V2

  • 参考链接:DeepSeek-V2: A Strong, Economical, and Efficient Mixture-of-Experts Language Model
  • 在DeepSeekV1的基础上,沿着负载均衡继续做了3个优化

DeepSeek-V3

  • 参考链接:DeepSeek-V3 Technical Report

  • 门控函数从SoftMax优化为了Sigmoid

    • 个人理解:为什么用Sigmoid更好?因为本来选择了topK就还需要再做一次归一化的(这次归一化是直按线性权重分配,不使用Softmax),使用Sigmoid速度更快,不影响选择topK且归一化后效果一致?

DeepSeek-R1之MLA

  • 详情参考:NLP——LLM-Attention优化之MLA
  • 其他参考链接:
    • 原始论文:DeepSeek-V2: A Strong, Economical, and Efficient Mixture-of-Experts Language Model
    • deepseek技术解读(1)-彻底理解MLA(Multi-Head Latent Attention) - 姜富春的文章 - 知乎
    • MLA(Multi-Head Latent Attention)—DeepSeek-V2/V3 Attention方案 - 浮生梦晓的文章 - 知乎
    • 缓存与效果的极限拉扯:从MHA、MQA、GQA到MLA——科学空间

DeepSeek-R1之GRPO

  • 原始论文:DeepSeekMath: Pushing the Limits of Mathematical Reasoning in Open Language Models
  • 核心思路:使用多次采样的归一化结果作为reward,放弃Critic Model,减少显存使用
  • 一个有趣的对比:DeepSeek GRPO在简单控制系统上和PPO的对比 - 王兴兴的文章 - 知乎
    • 对于整个系统中间过程和信息,比较清晰的问题(中间过程能被价值评价清晰),比如类似上面的控制系统(或者其他机器人系统),PPO还是最简单粗暴出效果很好的;但对于像DeepSeek用来搞数学RL推理,由于中间过程没法很好的描述和计算中间过程的价值,确实还是GRPO更快更方便(只看最终结果);

  • 注意,GRPO中使用了KL散度的近似形式,Approximating KL Divergence —— 来自:Deepseek的RL算法GRPO解读 - AIQL的文章 - 知乎
    • 估计形式为(注意以下式子中右边是左边的无偏梯度的前提是 \(o_{i,t} \sim \pi_\theta(\cdot \vert q,\mathbf{o}_{i,<t})\) ):
      $$
      \mathbb{D}_\text{KL}[\pi_\theta\Vert\pi_{\text{ref}}] \approx \frac{\pi_{\text{ref}}(o_{i,t}\vert q,\mathbf{o}_{i,<t})}{\pi_\theta(o_{i,t}\vert q,\mathbf{o}_{i,<t})} - \log \frac{\pi_{\text{ref}}(o_{i,t}\vert q,\mathbf{o}_{i,<t})}{\pi_\theta(o_{i,t}\vert q,\mathbf{o}_{i,<t})} - 1, \quad o_{i,t} \sim \pi_\theta(\cdot \vert q,\mathbf{o}_{i,<t})
      $$
      • 直观理解:上面的式子右边满足KL散度的基本特性
        • 当两个分布足够接近时,第一项趋近于1,第二项趋近于0,整体趋近于0;
        • 两个分布不相等时,上式右边取值总是大于0,可以通过求导证明:当 \(x>0\) 时,有 \(x - \log x - 1 \ge 0\)
  • 其他团队对GRPO的改进:阶跃&清华新论文:DeepSeek-R1的GRPO 可以更简洁 - 机器之心的文章 - 知乎

    阶跃星辰与清华大学近期的一项研究发现,只需使用带 GAE (λ= 1,γ= 1)的普通 PPO 以及基于规则的简单奖励函数,无需任何 KL 正则化,就足以扩展在推理任务上的响应长度和基准性能,类似于在 DeepSeek-R1-Zero 上观察到的现象
    使用这种极简方法,他们打造了 Open-Reasoner-Zero,这是首个面向大规模推理的强化学习训练的开源实现。并且该实现在 GPQA Diamond 基准上的表现优于 DeepSeek-R1-Zero-Qwen-32B,同时仅需使用 1/30 的训练步数。需要强调,该团队不仅开源了代码,还发布了参数设置、训练数据和模型权重


DeepSeek-R1之MTP

  • 参考链接:deepseek技术解读(2)-MTP(Multi-Token Prediction)的前世今生 - 姜富春的文章 - 知乎
  • 基本思想:
    • 预测阶段(Predict) :通过 K 个头一次生成 K 个 token 的预测
    • 验证阶段(Verify) :将 K 个 token 组装成 K 个 <input,label> 对,并行地利用输入 Main Model 作为评估验证,如果输出 label 与 Main Model 一致,则接受该 token
    • 接受阶段(Accept) :最终接受满足 Main Model 的最大长度 tokens 作为输出

Deepseek MTP实现细节

  • 原始报告:DeepSeek-V3 Technical Report内容如下

  • 问题:上面图中设计的MTP中,无法做到整整的并行,比如,仅知道 \(t_1\) 时,只能预测得到 \(t_2\) ,无法得到 \(t_3\) ,因为在任意一个Module中, \(t_2\) 都依赖着 \(t_3\) 作为输入(只是输入后不用再过 \(L\) 层Transformer Block了,仅过一层就行)

  • 训练时:使用多个MTP Module,综合大家的损失共同更新梯度
    $$\mathcal{L}_\text{MTP} = \frac{\lambda}{D}\sum_{k=1}^D \mathcal{L}_\text{MTP}^k$$

  • 推断时:Deepseek直接丢弃了MTP Module,仅使用第一个(相当于跟普通不使用MTP的时候一致,只是吃到了训练的红利),部分文章中提到最多使用2个

    Our MTP strategy mainly aims to improve the performance of the main model, so during inference, we can directly discard the MTP modules and the main model can function independently and normally. Additionally, we can also repurpose these MTP modules for speculative decoding to further improve the generation latency.


DeepSeek-R1 API远程调用

  • 参考链接:如何使用 Python 调用 DeepSeek-R1 API?超详细的图文教程

附录:Sparse MoE实现Demo

  • Sparse MoE 的简单实现示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    import torch
    import torch.nn as nn

    class Expert(nn.Module):
    def __init__(self, input_size, output_size):
    super(Expert, self).__init__()
    self.fc = nn.Linear(input_size, output_size)

    def forward(self, x):
    return self.fc(x)

    # Dense MoE实现,对每个Token,所有专家都参与计算
    class MoE(nn.Module):
    def __init__(self, num_experts, input_size, output_size):
    super(MoE, self).__init__()
    self.experts = nn.ModuleList([Expert(input_size, output_size) for _ in range(num_experts)])
    self.gate = nn.Linear(input_size, num_experts)

    def forward(self, x):
    gate_scores = torch.softmax(self.gate(x), dim=1)
    expert_outputs = [expert(x) for expert in self.experts]
    expert_outputs = torch.stack(expert_outputs, dim=1)
    output = torch.sum(gate_scores.unsqueeze(-1) * expert_outputs, dim=1)
    return output

    # SparseMoE实现,对每个Token,仅少量专家参与计算
    class SparseMoE(nn.Module):
    def __init__(self, num_experts, input_size, output_size, k=2):
    super(SparseMoE, self).__init__()
    self.experts = nn.ModuleList([Expert(input_size, output_size) for _ in range(num_experts)])
    self.gate = nn.Linear(input_size, num_experts)
    self.k = k

    def forward(self, x):
    gate_scores = self.gate(x)
    topk_scores, topk_indices = torch.topk(gate_scores, k=self.k, dim=1)

    batch_size = x.size(0)
    expert_outputs = []
    for b in range(batch_size):
    # 获取当前样本选中的K个专家的输出
    selected_outputs = [self.experts[idx](x[b].unsqueeze(0)) for idx in topk_indices[b]]
    # 堆叠输出,维度为 (1, k, output_size)
    selected_outputs = torch.stack(selected_outputs, dim=1)
    expert_outputs.append(selected_outputs)

    # 合并所有样本,维度为 (batch_size, k, output_size)
    expert_outputs = torch.cat(expert_outputs, dim=0)

    # 计算选中专家的权重 (batch_size, k)
    weights = torch.softmax(topk_scores, dim=1)

    # 加权求和,维度为 (batch_size, output_size)
    output = torch.sum(weights.unsqueeze(-1) * expert_outputs, dim=1)
    return output

    # 使用Demo
    num_experts = 4
    input_size = 10
    output_size = 5
    batch_size = 32
    moe = SparseMoE(num_experts, input_size, output_size)
    input_data = torch.randn(batch_size, input_size)
    output = moe(input_data)
    print(output.shape)

    # torch.Size([32, 5])

NLP——LLM-sentencepiece包的使用

本文主要介绍LLM的Tokenization,sentencepiece的使用

  • 源码:google/sentencepiece
  • 本文测试版本号sentencepiece=0.2.0

打印词表编码

  • 代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import sentencepiece as spm

    def export_vocab_to_file(model_path, output_file):
    # 加载 SentencePiece 模型
    sp = spm.SentencePieceProcessor()
    sp.load(model_path)

    # 打开文件准备写入
    with open(output_file, 'w', encoding='utf-8') as f:
    # 遍历词汇表中的每一个词汇和其索引
    for piece_id in range(sp.get_piece_size()):
    piece = sp.id_to_piece(piece_id)
    score = sp.get_score(piece_id)
    # 将词汇和其索引(或分数)写入文件
    f.write(f'{piece_id}\t{piece}\t{score}\n')

    export_vocab_to_file('chinese_sp.model', 'vocab.txt')
  • vocab.txt文件格式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    0	<unk>	0.0
    1 <s> 0.0
    2 </s> 0.0
    3 , -2.814912796020508
    4 ▁ -3.7144806385040283
    5 。 -3.715141534805298
    6 的 -3.7526743412017822
    7 、 -4.614748001098633
    ...

已有词表扩展

  • 代码如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    import sentencepiece as spm
    import os
    os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"]="python"
    from sentencepiece import sentencepiece_model_pb2 as sp_pb2_model

    chinese_sp_model_path = './chinese_sp.model'
    new_chinese_sp_model_path = './new_chinese_sp.model'
    chinese_sp_model = spm.SentencePieceProcessor()
    chinese_sp_model.Load(chinese_sp_model_path)

    print(len(chinese_sp_model))

    chinese_sp_model_mp = sp_pb2_model.ModelProto()
    chinese_sp_model_mp.ParseFromString(chinese_sp_model.serialized_model_proto())
    tokens_set=set(p.piece for p in chinese_sp_model_mp.pieces)

    ## 将特殊字符添加到词表中
    new_pieces = ['#_#_#', '$$%%##']
    for piece in new_pieces:
    if piece not in tokens_set:
    new_p = sp_pb2_model.ModelProto().SentencePiece()
    new_p.piece = piece
    # score越大,匹配的优先级越高,score为无穷小时约等于没有添加
    new_p.score = 0
    # 使用append添加词,token编码ID会累计+1
    chinese_sp_model_mp.pieces.append(new_p)
    # print(new_p)

    ## Save
    with open(new_chinese_sp_model_path, 'wb') as f:
    f.write(chinese_sp_model_mp.SerializeToString())

    ## load
    new_chinese_sp_model = spm.SentencePieceProcessor()
    new_chinese_sp_model.Load(new_chinese_sp_model_path)
    text='''#_#_#,$$%%##'''
    print("Test text:\n",text)
    print(f"Tokenized by Chinese-LLaMA tokenizer:{new_chinese_sp_model.tokenize(text)}")

sentencepiece 包使用总结

  • sentencepiece包中对分词模型的存储只需要一个文件xx.model
  • 分词模型存储文件中包含了词表信息,包括了piece,score等,同时按照顺序存储,也暗含了词表的编码信息
    • 排序按照score从大到小,score一般小于等于0
  • 想要在词表中增加编码,可以直接构建SentencePiece类对象,并添加到词表反序列化后的尾部即可
    • SentencePiece类对象包含两个关键信息piece,score

NLP——LLM模型评估工具


LightEval 工具

  • LightEval 是一个专注于 LLM 评估的开源工具库
  • 它提供了标准化的评估框架,支持模型性能测试、对比分析及结果可视化,帮助开发者更高效地衡量模型能力
  • 以下是 LightEval 主要能力:
    • 多维度评估指标 :覆盖知识理解、推理能力、语言生成质量等多个维度
    • 丰富测试数据集 :内置多个公开测试集,并支持自定义数据集扩展
    • 模型兼容性 :支持主流LLM模型的直接接入与评估
    • 结果可视化 :提供直观的图表展示,便于分析模型优势与不足

LightEval 代码示例

  • 一个简单的代码示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    from lighteval import Evaluator, load_dataset

    # 1. 加载评估数据集(内置或自定义)
    dataset = load_dataset("ceval") # 加载C-Eval中文能力测试数据集

    # 2. 初始化评估器(支持多个评估维度)
    evaluator = Evaluator(metrics=["accuracy", "f1_score", "perplexity"])

    # 3. 准备待评估模型(示例使用Hugging Face模型)
    from transformers import AutoModelForCausalLM, AutoTokenizer
    model_name = "gpt2" # 替换为实际模型
    tokenizer = AutoTokenizer.from_pretrained(model_name)
    model = AutoModelForCausalLM.from_pretrained(model_name)

    # 4. 定义预测函数(根据模型输入输出格式调整)
    def predict_fn(question):
    inputs = tokenizer(question, return_tensors="pt")
    outputs = model.generate(**inputs, max_length=100)
    return tokenizer.decode(outputs[0], skip_special_tokens=True)

    # 5. 执行评估
    results = evaluator.evaluate(
    dataset=dataset,
    model_predict_fn=predict_fn,
    batch_size=8, # 可调整参数
    verbose=True
    )

    # 6. 查看并可视化结果
    print("评估结果:", results)
    evaluator.plot_results() # 生成评估报告图表

评估工具-OpenCompass

  • OpenCompass 是上海人工智能实验室开源的大模型评测平台,涵盖学科、语言、知识、理解、推理等五大评测维度,可全面评估大模型能力
  • OpenCompass 支持在线查看榜单,在线参与评测竞技
  • OpenCompass 还开源了大模型评测工具,使用很简便,GitHub 地址:OpenCompass 项目
    • 使用教程:README.md

NLP——Ollama-DeepSeek-R1本地部署

本文主要介绍基于 Ollama 的 DeepSeek-R1 本地部署


安装 Ollama

  • 在 ollama官网 下载安装即可
  • ollama 是一个类 Docker 的大模型管理工具

安装 DeepSeek-R1 模型

  • 安装命令(以下命令会自动安装7B版本,即DeepSeek-R1-Distill-Qwen-7B)

    1
    ollama run deepseek-r1
  • 更多镜像可参考:ollama.com/library


基于 ChatBox 的可视化交互

  • 下载并安装:chatboxai.app
  • 点击设置选择指定本地模型即可启动

ollama API 调用

  • 参考链接:

    • 官方:ollama/docs/api.md
    • 博客:DeepSeek R1本地化部署以及API调用 - 数字梦想家的文章 - 知乎,包含使用ollama API和OpenAI API等方法
  • API 调用 Demo

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    def http_api_demo():
    import requests
    import json

    # Ollama 服务器的地址
    OLLAMA_URL = "http://localhost:11434/api/generate"

    # 要调用的模型名称
    MODEL_NAME = "deepseek-r1:latest"

    # 要发送的提示文本
    prompt = "你好,DeepSeek!请求解方程x^3+x^2+x-3=0"

    # 请求的 payload
    payload = {
    "model": MODEL_NAME,
    "prompt": prompt,
    "stream": False # 设置为 False 以获取完整的响应
    }

    # 发送 POST 请求
    response = requests.post(OLLAMA_URL, json=payload)

    # 检查响应状态码
    if response.status_code == 200:
    # 解析响应内容
    response_data = response.json()
    print("模型响应:", response_data.get("response"))
    else:
    print(f"请求失败,状态码: {response.status_code}")
    print("响应内容:", response.text)

    def ollama_api_stream_demo():
    import requests # 使用 requests 库调用 Ollama 的 API
    import json

    # Ollama 的 API 地址
    url = "http://localhost:11434/api/chat"

    # 请求数据
    data = {
    "model": "deepseek-r1:latest", # 使用的模型
    "messages": [
    {
    "role": "system",
    "content": "你是一个专业人士,每次回答前请先说“解:”"
    },
    {
    "role": "user",
    "content": "9.9和9.11哪个更大?"
    }
    ],
    "stream": True # 启用流式响应
    }

    # 发送 POST 请求
    response = requests.post(
    url,
    json=data,
    stream=True # 启用流式接收
    )

    # 打印结果
    print("模型返回的内容:")
    for line in response.iter_lines():
    if line: # 过滤掉空行
    # 解析 JSON 数据
    chunk = json.loads(line.decode('utf-8'))
    if "message" in chunk and "content" in chunk["message"]:
    print(chunk["message"]["content"], end='', flush=True) # 逐步打印内容

    def ollama_api_demo():
    import requests # 使用 requests 库调用 Ollama 的 API
    import json

    # Ollama 的 API 地址
    url = "http://localhost:11434/api/chat"

    # 请求数据
    data = {
    "model": "deepseek-r1:latest", # 使用的模型
    "messages": [
    {
    "role": "system",
    "content": "你是一个专业人士,每次回答前请先说“解:”"
    },
    {
    "role": "user",
    "content": "9.9和9.11哪个更大?"
    }
    ],
    "stream": False # 禁用流式响应
    }

    # 发送 POST 请求
    response = requests.post(
    url,
    json=data
    )

    # 检查响应状态
    if response.status_code == 200:
    # 解析 JSON 数据
    result = response.json()
    if "message" in result and "content" in result["message"]:
    print("模型返回的内容:")
    print(result["message"]["content"]) # 打印完整内容
    else:
    print(f"请求失败,状态码:{response.status_code}")
    print(response.text) # 打印错误信息

    def openai_api_demo():
    from openai import OpenAI
    client = OpenAI(
    base_url='http://localhost:11434/v1/',
    # required but ignored
    api_key='ollama',
    )
    chat_completion = client.chat.completions.create(
    messages=[
    {
    'role': 'user',
    'content': '9.9和9.11哪个更大?',
    },
    {
    'role': 'system',
    'content': '你是一个专业人士,每次回答前请先说“解:”',
    }
    ],
    model='deepseek-r1:latest',
    temperature=0.0, # 可以根据需要调整温度值,决定生成的随机性程度
    )
    # 打印结果
    print("模型返回的内容:")
    print(chat_completion.choices[0].message.content)


    def openai_api_stream_demo():
    from openai import OpenAI
    client = OpenAI(
    base_url='http://localhost:11434/v1/',
    # required but ignored
    api_key='ollama',
    )

    # 启用流式响应
    stream = client.chat.completions.create(
    messages=[
    {
    'role': 'user',
    'content': '9.9和9.11哪个更大?',
    },
    {
    'role': 'system',
    'content': '你是一个专业人士,每次回答前请先说“解:”',
    }
    ],
    model='deepseek-r1:latest',
    temperature=0.0, # 可以根据需要调整温度值,决定生成的随机性程度
    stream=True, # 启用流式响应
    )

    # 打印结果
    print("模型返回的内容:")
    for chunk in stream:
    if chunk.choices[0].delta.content: # 检查是否有内容
    print(chunk.choices[0].delta.content, end='', flush=True) # 逐步打印内容


    # openai_api_demo()
    # openai_api_stream_demo()
    ollama_api_stream_demo()
    # ollama_api_demo()
  • 多线程调用 Demo

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    import requests
    import json
    from concurrent.futures import ThreadPoolExecutor, as_completed

    def ollama_api_demo(message):
    # Ollama 的 API 地址
    url = "http://localhost:11434/api/chat"

    # 请求数据
    data = {
    "model": "deepseek-r1:latest", # 使用的模型
    # "model": "llama3.2:latest",
    "messages": [
    {
    "role": "system",
    "content": "你是一个专业人士,每次回答前请先说“解:”"
    },
    {
    "role": "user",
    "content": message
    }
    ],
    "stream": False # 禁用流式响应
    }

    # 发送 POST 请求
    response = requests.post(
    url,
    json=data
    )

    # 检查响应状态
    if response.status_code == 200:
    # 解析 JSON 数据
    result = response.json()
    if "message" in result and "content" in result["message"]:
    return result["message"]["content"] # 返回完整内容
    else:
    return f"请求失败,状态码:{response.status_code}\n{response.text}" # 返回错误信息

    def parallel_ollama_api_demo(messages):
    with ThreadPoolExecutor() as executor:
    # 提交任务到线程池
    futures = [executor.submit(ollama_api_demo, message) for message in messages]

    # 等待所有任务完成并获取结果
    results = []
    for future in as_completed(futures):
    try:
    result = future.result()
    results.append(result)
    except Exception as e:
    results.append(f"任务执行出错: {e}")

    return results

    if __name__ == "__main__":
    # 示例消息列表
    messages = [
    "9.9和9.11哪个更大?",
    # "Python 和 Java 哪个更适合初学者?",
    # "解释一下量子计算的基本概念。"
    ]

    import time
    x = time.time()
    # 并行调用 API
    results = parallel_ollama_api_demo(messages)

    # 打印结果
    for i, result in enumerate(results):
    print(f"结果 {i+1}:")
    print(result)
    print("-" * 40)
    y = time.time()
    print("time(s):", y-x)
  • 如果想要设定 temperature 等参数,可以在结构体中增加 option 参数,按照字典传入即可,比如,可以如下实现仅返回长度为 20 的 token

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    def ollama_api_demo():
    import requests # 使用 requests 库调用 Ollama 的 API
    import json

    # Ollama 的 API 地址
    url = "http://localhost:11434/api/chat"

    # 请求数据
    data = {
    "model": "deepseek-r1:latest", # 使用的模型
    "messages": [
    {
    "role": "system",
    "content": "你是一个专业人士,每次回答前请先说“解:”"
    },
    {
    "role": "user",
    "content": "9.9和9.11哪个更大?"
    }
    ],
    "options": {
    "temperature": 0.7,
    "top_p": 0.8,
    "num_predict": 20 # 仅返回20个 token 长度
    },
    "stream": False # 禁用流式响应
    }

    # 发送 POST 请求
    response = requests.post(
    url,
    json=data
    )

    # 检查响应状态
    if response.status_code == 200:
    # 解析 JSON 数据
    result = response.json()
    if "message" in result and "content" in result["message"]:
    print("模型返回的内容:")
    print(result["message"]["content"]) # 打印完整内容
    else:
    print(f"请求失败,状态码:{response.status_code}")
    print(response.text) # 打印错误信息

NLP——LLM-Tokenization

本文主要介绍 LLM 的 Tokenization(一些研究文献也叫作 Tokenize)

  • 参考链接:
    • 《从tokenizer说起,为LLM自回归预训练准备数据集》-大模型炼丹术(一)

Tokenization 技术分类

  • 分词(Tokenization),有些地方也称为词元化,用于将文本进行切分,以便于语言模型能够理解和使用
  • Tokenization 技术有三种粒度:Word-level Tokenization、Character-level Tokenization 和 Sub-word Tokenization

Word-level Tokenization(Word 粒度)

  • 按照单词为最小单位进行分词来进行分词,每个单词是一个 token
  • 优点:
    • 每个 token 都是完整的单词,能准确表达语义(理解:模型学到的embedding就是当前单词的),便于理解和处理
  • 缺点:
    • 词表一般会很大,比如英文中各种词语变化非常大,词汇量动辄超过 10W+
    • 容易遇到 OOV(未登录词)问题
    • 对新词语或者拼写错误词语敏感,比如“MAGA”等近期新出的词语就无法识别到

Character-level Tokenization(Char 粒度)

  • 按照字符来进行分词,每个字符是一个 token
  • 优点:
    • 无 OOV 问题,且任何的新词都能被拆解到字符粒度
    • 词表固定,且词表很小(就几十个很少的字符集合),占用存储少
  • 缺点:
    • 相同句子长度下 token 太多,导致模型训练和推理慢
    • 单个字符无法表达语义,学出来的 embedding 也难以对应到语义上,比如 “Queen” 和 “King” 就很难被联系起来

Sub-word Tokenization(Subword 粒度)

  • 介于 Word-level 和 Character-level 之间,可以灵活地将一个单词拆分成一个或多个有语义信息的部分,比如 “tokenization” 可以拆为 “token”+”ization”,而 “love” 则不需要拆分,能够在减少 OOV 和 表达语义之间做 Trade-off
  • Sub-word Tokenization 包括 BPE,WordPiece,Unigram 等
    • BPE(Byte-Pair Encoding) ,即字节对编码,是一种 Tokenize 算法。其核心思想在于将最常出现的子词对合并,直到词汇表达到预定的大小时停止(实际上该方法本质是一种压缩算法,最早1994年提出,被用于通用数据压缩)
      • 特别说明 :Byte-Pair Encoding 中的 Byte 不是字节的意思,可以理解为编码的最小单位,一般是字符级别(这里很容易误解)
      • BPE 的缺点 :由于 BPE 是字符级别,直接使用 BPE 可能导致词表过大
        • 理解:一个字符可能包含很多个字节,比如 UTF-8(Unicode编码的一种)是一种变长的编码方式,它可以使用 1 到 4 个字节来表示一个字符(理论上 4 个字节可以表示所有语言的所有字符);对于英文,通常 1 个字符 1 个字节,但对于汉字,通常使用 2 到 3 个字节,由于中文的字符空间过大,所以直接使用 BPE 可能导致词表过大
    • BBPE(Byte-level BPE) ,BBPE 是 BPE 的一个变体,与 BPE 的唯一区别是,BPE 是字符级别,BBPE 是字节级别,特别适用于中文,日文等基础字符过于庞大的语言编码,BBPE 会使用 “_”作为每个字第一个 Byte 的前缀来指示单词边界,从而识别
    • WordPiece 是 Google 提出的一种方法,没有公开的实现,其实现逻辑较为复杂,与 BPE 类似,但是每次合并子词时不是按照出现频率最高的词对,而是其他策略(Google是使用模型来预估词对出现的概率),比如 HuggingFace 上的一个实现是使用一个计算公式计算优先级 \(Score_{<A,B>} = \frac{Count_{A}}{Count_{A} \times Count_{B}}\),然后按照优先级选择合并的词对
      • 公开资料显示:WordPiece 的目标是寻找最大化(分词训练集数据)似然的分词组合
      • 大名鼎鼎的 BERT 使用的就是 WordPiece 分词方法
    • Unigram ,又名 ULM(Unigram Language Model) :先初始化一个巨大的词汇表 ,再逐渐删除出现概率低的词汇(会从词汇表中挑出使得 loss 增长最小的 10%~20% 的词汇),直到词汇表打到预定的大小时停止
  • 缺点:
    • 需要提前使用一些特定的算法,按照不同的文本训练集/分词方法进行分词,可能导致不同模型的分词结果不同
    • 每个模型需要增加一个自己的分词词表和分词函数(不同词表分词流程不同),以确保其他开发者可以使用
  • 优点:
    • 能准确控制词表大小,可大可小
    • 相对 Char 粒度分词,每个 Token 的语义相对独立,在词表数量足够的情况下,单个token不会大规模重复语义
    • 相对 Word 粒度分词,出现 OOV 的概率大大降低

分词的一般流程

  • 一个分词算法的目标是实现编码和解码
  • 常见的分词算法包括三个函数功能:
    • 词表生成 :对给定数据进行编码生成
    • 编码 :根据词表,对给定句子进行编码,输出为 token 列表
    • 解码 :根据词表,给定 token 列表,输出句子

BPE 词表生成过程

  • BPE 的词表生成函数的输入输出 :
    • 输入 :训练数据 \(\mathcal{D}\)
    • 输出 :词表(BPE的词表是有序的合并规则集合)
  • BPE 生成词表的执行步骤如下:
    • 第一步:初始化词汇表 vacab :将训练数据 \(\mathcal{D}\) 中的文本拆分为字符级别的词汇表并统计字符出现次数
      • 注:单词的结尾使用一个特殊字符来表示 /w,后续处理过程中该字符与其他字符等价处理
    • 第二步:统计频率 :统计所有相邻词汇对的出现频数并记录(注意:这一步是所有相邻的词汇都要统计)
    • 第三步:合并最频繁的字符对 :将频数最高的词汇对 \(<v_1,v_2>\) 合并为一个新的词汇 \(v_{\text{new}}\),并更新词汇表
      • 假设:原始词汇表频次统计次数为 \(v_1:count_1\),\(v_2:count_2\),而 \(v_3 = <v_1,v_2>\) 出现的频次是 \(count_3\),
      • 更新方式:增加一个新的词汇 \(v_3: count_3\),减少 \(<v_1,v_2>\) 对应的次数,更新结果为:\(v_1:count_1-count_3\),\(v_2:count_2-count_3\),若更新后的频数值为0,则该词汇可以从词表中永久删除了
      • 问题:可能出现新的词汇已经在词汇表中了吗?此时如何处理?
        • 回答:是可能出现的,相当于不同路径生成了同一个词汇,此时一般可以选择跳过更新或者将频数增加到新的词汇上,同时减少子词汇的频数

          如果更注重词汇表的稳定性和避免不必要的合并,那么跳过合并可能是较好的选择;如果希望更准确地反映子词的出现频率,以便在后续处理中更好地权衡不同子词的重要性,那么更新词频则更为合适。在一些实际的 BPE 实现中,也可能会综合考虑这两种方式,或者根据一些特定的条件来动态决定采取哪种处理方式

    • 第四步:重复第二步和第三步 ,直到触发终止条件,终止条件一般有:
      • 词汇表达到预定的词汇表大小:迭代过程中词汇表会越来越大,可以给定确定的词汇表大小
      • 迭代次数达到最大值

BPE 编码过程

  • 根据词表,对输入文本进行编码,生成 token 序列
  • 具体流程
    • 根据词表和有序的合并规则集合,对输入文本进行编码
    • 由于合并规则是有序的,所以按照规则即可实现固定的编码,相同文本编码一定是相同的
  • 实际上,在词表中加入权重分后,不需要任何其他文件(规则隐含在权重分中),可以使用贪心的方式编码
    • 首先轮询一遍,搜索所有的相邻编码 A 和 B 并尝试合并,如果合并结果 AB 在词表中(这里的词表可以使用有序词表,会加快匹配效率),则取出 AB 的权重分(注意,这里先不合并)
    • 轮询一遍以后分两种情况
      • 如果没有任何候选 AB 在词表中,则结束,编码完成
      • 否则,对所有候选的匹配 AB 按照权重分进行倒序排列,选择得分最高的匹配 AB 执行合并操作(注意,一个句子可以同时匹配多个完全相同的地方)
  • 另一种不需要权重分的贪心编码方式:
    • 直接从第一个字符开始,贪心匹配从第一个字符为初始起点的,最大满足长度的匹配(不需要权重分,只需要词表即可)
  • 代码示例,来自github.com/boyu-ai/Hands-on-NLP
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    ordered_vocabulary = {key: x for x, key in enumerate(vocabulary)}
    sentence = "nanjing beijing"
    print(f"输入语句:{sentence}")
    tokens = sentence.split(' ')
    tokenized_string = []
    for token in tokens:
    key = token+'_'
    splits = list(key)
    #用于在没有更新的时候跳出
    flag = 1
    while flag:
    flag = 0
    split_dict = {}
    #遍历所有符号进行统计
    for i in range(len(splits)-1):
    #组合两个符号作为新的符号
    current_group = splits[i]+splits[i+1]
    if current_group not in ordered_vocabulary:
    continue
    if current_group not in split_dict:
    #判断当前组合是否在词表里,如果是的话加入split_dict
    split_dict[current_group] = ordered_vocabulary[current_group]
    flag = 1
    if not flag:
    continue

    #对每个组合进行优先级的排序(此处为从小到大)
    group_hist=[(k, v) for k, v in sorted(split_dict.items(),\
    key=lambda item: item[1])]
    #优先级最高的组合
    merge_key = group_hist[0][0]
    new_splits = []
    i = 0
    # 根据优先级最高的组合产生新的分词
    while i < len(splits):
    if i+1>=len(splits):
    new_splits.append(splits[i])
    i+=1
    continue
    if merge_key == splits[i]+splits[i+1]:
    new_splits.append(merge_key)
    i+=2
    else:
    new_splits.append(splits[i])
    i+=1
    splits=new_splits
    tokenized_string+=splits

    print(f"分词结果:{tokenized_string}")

BPE 解码过程

  • 根据词表,对输出token进行解码,生成文本
  • 具体流程
    • 解码比较简单,按照token直接从词表查询原始字符值,添加到文本后面即可

其他说明

  • BPE 在对中英文处理时,方式不同,英文时,需要先按照空格进行切分,然后再进行词表生成,中文则不需要进行切分
    • 理解:英文需要切分的原因是为了防止出现"e y"这种包含空格的 token 吗?
    • 问题:实际上也可以包含吧?感觉不一定会影响效果(注:目前是不包含的)
  • OpenAI 统计 GPT3 和 GPT4 分词数量的在线链接OpenAI Tokenizer
  • ChatGPT-BPE编码过程实例(from OpenAI Tokenizer)
    • 实例1
    • 实例2
    • 实例3
    • 以上实例1-3说明:
      • BPE编码不是从最长的 token 开始搜索,否则实例3的编码应该是2个 token 才对
      • BPE编码也不是为了保证将每一个句子编码成最少的 token 数量(但原始算法的基本思想是所有训练数据上,尽量压缩 token 数量,减少存储)

附录:扩展词表的方法

  • 参考链接:自定义构造Tokenizer

附录:分词器比较

  • 常用分词器有:SentencePiece 和 Tiktoken
    对比维度 SentencePiece Tiktoken
    公司 Google OpenAI
    设计目标和使用场景 通用分词工具 OpenAI 专为 GPT 系列模型设计
    分词算法 支持多种算法,如 BPE、Unigram 等 BPE 算法
    词表特点 词表可根据数据集和任务训练,可调整大小和内容 针对 GPT 系列模型预先训练好,用户无法直接训练或修改
    性能和效率 性能和效率受算法选择和词表大小影响,某些情况下训练和分词速度可能较慢 分词速度快,能高效计算文本token数量
  • SentencePiece 是 Google 开源的一个分词工具库,包含以下优点:
    • SentencePiece 使用特殊的符号来转义空格,可以精确区分 love you ! 和 love you! 两个句子中 ! 前的空格
    • 高效实现了如 BPE 和 ULM 等分词方法,且自动将语料先转换为 Unicode 编码(注意这里不是 UTF-8),再输入分词算法,这样可以解决中文没有空格的问题
      • 理解(待确定):Unicode 编码中文时是以字为单位的,比如你对应 U+4F60,可以从 U+ 准确识别每个文字开头?
      • 一些文章中提到,中文字符是在进行 BBPE 算法时,需要先将中文经过 UTF-8 变成字节,然后再经过 Unicode 统一编码成字符,最后再进行分词表构造
  • Tiktoken 的使用讲解:NLP(五十四)tiktoken的使用
  • 注:Llama1 和 Llama2 使用 SentencePiece,Llama3 开始使用 Tiktoken 了
  • 两种分词器的使用 Demo,分别展示 SentencePiece 和 Tiktoken 从指定数据构造词表、加载词表、编码和解码的全过程:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    import sentencepiece as spm
    import tiktoken

    # 数据文件构造
    data = [
    "This is a sample sentence.",
    "Another sample sentence for testing."
    ]
    with open('data.txt', 'w', encoding='utf-8') as f:
    for line in data:
    f.write(line + '\n')

    # SentencePiece 使用 Demo
    # 构造 SentencePiece 词表
    spm.SentencePieceTrainer.train(input='data.txt', model_prefix='sp_model', vocab_size=100)

    # 加载 SentencePiece 词表
    sp = spm.SentencePieceProcessor()
    sp.load('sp_model.model')

    # 编码和解码
    text = "This is a test."
    sp_encoded = sp.encode(text)
    sp_decoded = sp.decode(sp_encoded)
    print("SentencePiece 编码结果:", sp_encoded)
    print("SentencePiece 解码结果:", sp_decoded)

    # Tiktoken 使用 Demo
    # 对于 Tiktoken,我们使用预训练的 cl100k_base 编码
    # 这里不需要构造词表,直接加载预训练编码
    encoding = tiktoken.get_encoding("cl100k_base")
    # 还可以选择其他编码方式,不同模型使用不同的编码方式,可以调用函数对应抽取到编码方式
    # print(tiktoken.encoding_for_model('gpt-3.5-turbo'))

    # 编码和解码
    tiktoken_encoded = encoding.encode(text)
    tiktoken_decoded = encoding.decode(tiktoken_encoded)
    print("Tiktoken 编码结果:", tiktoken_encoded)
    print("Tiktoken 解码结果:", tiktoken_decoded)

附录:transformers.AutoTokenizer 的使用

tokenizer.tokenize() 处理单文本

  • tokenize 处理单文本示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    from transformers import AutoTokenizer

    # Tokenizer 加载,根据路径下的文件初始化自身,路径下会包含:
    # # tokenizer.json(包含词表)
    # # tokenizer_config.json(包含 tokenizer_class(可以按照需要自己实现并提 PR 到 transformers 库) 和 special_token 等信息)
    # # special_tokens_map.json(通常与 tokenizer_config.json 中的内容重复,可选,可被类初始化时读取使用),special_token 包含 bos,eos,pad,unk 四个,是模型必须指定的,有时候也实现在 tokenizer_config.json 等文件中
    # # vocab.json (词表信息,可选,可被类初始化时读取使用)
    tokenizer = AutoTokenizer.from_pretrained("model_name")

    text = "Hello world! This is a test."
    # 单行分词
    tokens = tokenizer.tokenize(text)
    print("Tokens:", tokens) # 这里输出的可能是 类似 ['Hello', ',', 'Ġworld', '!', 'ĠThis', 'Ġis', 'Ġa', 'Ġtest', '.'] 的结果(分词器里面使用 'Ġ'(Unicode 字符 U+0120,带点空格)在这里表示普通空格(U+0020))
    # 注:这一步中,如果是BBPE 分词,且输入是中文甚至可能看起来像是乱码,因为常见的 BBPE 分词方式会先将中文转换成 UTF-8 编码后再进行分词

    # 转换为索引 IDs
    ids = tokenizer.convert_tokens_to_ids(tokens)
    print("IDs:", ids) # 这里输出具体索引(int 类型的数字列表)

    # 解码验证,将 IDs 解码为原始文本
    decoded = tokenizer.decode(ids)
    print("解码结果:", decoded) # 与原始输出文本 text 一致

tokenizer() 批量处理文本

  • tokenizer(text) 实际上是 tokenizer.__call__(text) 的简写,是最常用、最完整的文本编码方法
  • tokenizer(text) 函数包含以下步骤:
    • 1)分词 :将文本拆分成 Tokens(注意: Token 不是 ID,不是整数)
    • 2)映射 :将 Toke ns 转换为对应的 IDs
    • 3)添加特殊标记 :如[CLS], [SEP], [PAD]等
    • 4)填充和截断 :处理长度不一致的问题,需指定参数
    • 5)返回张量 :转换为 PyTorch/TensorFlow 张量,注意返回的是 ID 列表,不是 Token 列表
  • tokenizer(text) 函数参数较多,重点需要关注的如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    def __call__(
    self,
    text: Union[str, List[str], List[str], List[List[str]], None] = None, # 待编码的单条文本或文本列表;若为 List[str] 类型,表示已按词切分
    text_pair: Optional[Union[str, List[str], List[str], List[List[str]]]] = None, # 与 text 配对的第二条文本(或列表),用于需要“句子对”输入的任务,如 NLI、QA
    text_target: Union[str, List[str], List[str], List[List[str]], None] = None, # 作为“标签/目标”的单条文本或文本列表,用于 seq2seq 等需对目标序列编码的场景
    text_pair_target: Optional[
    Union[str, List[str], List[str], List[List[str]]]
    ] = None, # 与 text_target 配对的第二条目标文本(或列表)
    add_special_tokens: bool = True, # 是否在输出中自动添加特殊符号(如 [CLS]、[SEP])
    padding: Union[bool, str, PaddingStrategy] = False, # 是否/如何填充:True/False、'longest'、'max_length' 或 "do_not_pad"(PaddingStrategy 枚举)
    truncation: Union[bool, str, TruncationStrategy, None] = None, # 是否/如何截断:True/False、'only_first'、'only_second'、"longest_first" 或 "do_not_truncate"(TruncationStrategy 枚举)
    max_length: Optional[int] = None, # 允许的最大序列长度;超出则按 truncation 策略处理
    stride: int = 0, # 滑动窗口截断时,相邻片段间的重叠 token 数
    is_split_into_words: bool = False, # 表明输入 text 是否已经按词切分(List[str]),此时不重新分词
    pad_to_multiple_of: Optional[int] = None, # 将序列长度填充到指定值的整数倍(常见于 TensorRT / 优化内核)
    padding_side: Optional[str] = None, # 显式指定填充方向:'left' 或 'right';默认采用 tokenizer 配置
    return_tensors: Optional[Union[str, TensorType]] = None, # 返回张量类型:'np'、'pt'、'tf' 等 TensorType 枚举;None 则返回 Python 列表
    return_token_type_ids: Optional[bool] = None, # 是否返回 token_type_ids(区分句子对);None 时按模型默认
    return_attention_mask: Optional[bool] = None, # 是否返回 attention_mask;None 时按模型默认
    return_overflowing_tokens: bool = False, # 是否返回因截断被溢出的 token 列表
    return_special_tokens_mask: bool = False, # 是否返回特殊符号掩码(1 表示特殊 token,0 表示普通 token)
    return_offsets_mapping: bool = False, # 是否返回每个 token 对应原始文本的 (start, end) 字符偏移
    return_length: bool = False, # 是否返回编码后序列长度
    verbose: bool = True, # 遇到警告/异常时是否打印详细信息
    **kwargs,
    ) -> BatchEncoding: # BatchEncoding 对象支持字典索引和属性访问
tokenizer() 参数说明(按常用程度排序)
  • text :必填参数,要分词的文本数据
    • 支持类型:单个字符串(如 "Hello world")、字符串列表(如 ["Hello, World", "你好,你好"])、嵌套字符串列表(如 [["Hello, World", "你好,你好"], ["Hi,你好吗?", "谢谢你"]],仅部分模型支持)
    • 说明:当传入列表时,会自动批量处理;嵌套列表通常用于处理“文本对”场景,需结合 padding 等参数使用
  • padding :控制是否填充(补全)序列到相同长度,可选值如下:
    • False / None:不填充(默认值),每个序列保留原始长度
    • True / "longest":填充到批量中最长序列的长度 ,此时 "max_length" 指定了也不会使用
    • "max_length":填充到 max_length 参数指定的长度(需同时设置 max_length)
    • "do_not_pad":等价于 False
    • 说明:填充时使用分词器的 pad_token(默认通常是 <pad>),填充位置由 padding_side 控制(默认右侧)
  • truncation :控制是否截断过长的序列,可选值如下:
    • False / None:不截断(默认值),若序列长度超过 max_length 会(理解:不能截断,又不能超过 max_length,矛盾了,必须报错)
    • True / "longest_first":截断到 max_length(需指定 max_length),优先截断最长的序列(批量场景)
    • "only_first":仅截断输入文本对中的第一个文本(如 premise)
    • "only_second":仅截断输入文本对中的第二个文本(如 hypothesis)
    • "do_not_truncate":等价于 False
    • 说明:截断时默认从右侧截断(由 truncation_side 控制)
  • max_length :指定序列的最大长度
    • 类型:整数(int)
    • 说明:需与 padding(设为 "max_length")或 truncation(设为 True 等)配合使用;
      • 若不指定,默认使用分词器的 model_max_length(通常是模型支持的最大输入长度,如 512、1024 等)
  • return_tensors :指定返回的张量类型
    • 可选值:None(默认)、"pt"(PyTorch 张量)、"tf"(TensorFlow 张量)、"np"(NumPy 数组)、"jax"(JAX 张量)
    • 说明:默认返回普通 Python 列表;指定后返回对应框架的张量,可直接传入模型训练/推理
  • return_attention_mask :是否返回注意力掩码
    • 类型:布尔值(bool),默认 True
    • 说明:注意力掩码用于告诉模型哪些 token 是真实文本(值为 1),哪些是填充的 pad token(值为 0);若设为 False,返回结果中不包含 attention_mask 键
  • return_token_type_ids :是否返回 token 类型 ID(用于区分文本对)
    • 类型:布尔值(bool),默认值由模型决定(如 BERT 类模型默认 True,GPT 类模型默认 False)
    • 说明:文本对场景(如 text = (sent1, sent2))中,token_type_ids 用 0 标识第一个文本的 token,1 标识第二个文本的 token;单文本场景中全为 0
    • 注意:亲测,很多现代 GPT 模型中,即使设置 return_token_type_ids=True,输出都全 0 了
  • add_special_tokens :是否添加模型所需的特殊 token
    • 类型:布尔值(bool),默认 True
    • 说明:特殊 token 包括 cls_token(如 <cls>)、sep_token(如 <sep>)、bos_token(如 <s>)、eos_token(如 </s>)等,不同模型的特殊 token 不同;设为 False 则仅返回原始分词结果,不添加任何特殊 token
  • return_offsets_mapping :是否返回 token 在原始文本中的偏移量(起始/结束索引)
    • 类型:布尔值(bool),默认 False
    • 说明:返回的 offsets_mapping 是一个列表,每个元素为 (start, end) 元组,对应每个 token 在原始文本中的字符位置(可用于实体标注、文本对齐等任务)
  • return_length :是否返回每个序列的原始长度(未填充/未截断前)
    • 类型:布尔值(bool),默认 False
    • 说明:返回的 length 列表包含每个序列在处理前的实际长度,便于后续计算有效 token 数
  • padding_side :填充方向
    • 可选值:"right"(默认,右侧填充)、"left"(左侧填充)
    • 说明:部分模型(如 GPT 类)可能需要左侧填充,需根据模型要求设置
  • truncation_side :截断方向
    • 可选值:"right"(默认,右侧截断)、"left"(左侧截断)
    • 说明:根据任务需求调整,例如处理历史对话时可能需要左侧截断旧对话
  • stride :截断时的重叠长度(用于长文本分段处理)
    • 类型:整数(int),默认 0
    • 说明:当文本长度超过 max_length 时,截断后保留前一段序列的 stride 个 token,避免丢失上下文(如长文档分类、问答任务)
  • return_overflowing_tokens :是否返回截断后溢出的 token 组成的额外序列
    • 类型:布尔值(bool),默认 False
    • 说明:结合 stride 使用,长文本会被分成多个重叠的子序列,返回所有子序列及对应的 overflow_to_sample_mapping(映射子序列到原始样本的索引)
  • allow_multiple_sentences :是否允许单个文本包含多个句子(通过标点/换行分隔)
    • 类型:布尔值(bool),默认 True
    • 说明:部分分词器支持自动拆分多句子,但通常不影响核心分词逻辑,保持默认即可
  • verbose :是否输出详细日志
    • 类型:布尔值(bool),默认 True
    • 说明:设为 False 可关闭警告信息(如序列长度超过模型最大长度的警告)

简单的返回结果示例

  • 示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    from transformers import AutoTokenizer

    model_path = "path_to_model"

    # 初始化分词器(以 BERT 为例)
    tokenizer = AutoTokenizer.from_pretrained(model_path)

    # 1. 基础用法(单文本)
    text = "Hello, world! This is a test."
    inputs = tokenizer(
    text,
    padding=True,
    truncation=True,
    max_length=10,
    return_tensors="pt"
    )
    print(type(inputs)) # <class 'transformers.tokenization_utils_base.BatchEncoding'>
    print(inputs.keys()) # 输出:dict_keys(['input_ids', 'attention_mask'])
    print(inputs["input_ids"].shape) # 输出:torch.Size([1, 9])

    # 文本对用法(如句子相似度),仅在 BERT RoBERTa 等模型中可以使用,其他模型会输出 inputs_pair["token_type_ids"],但是包含的值都是 0
    text_pair = ("I like cats.", "I love dogs.")
    inputs_pair = tokenizer(
    text_pair,
    padding="max_length",
    max_length=5,
    return_token_type_ids=True,
    return_tensors="np"
    )
    print(inputs_pair.keys()) # 输出:dict_keys(['input_ids', 'token_type_ids', 'attention_mask'])
    print(inputs_pair["input_ids"].shape) # 输出:(2, 5)
    print(inputs_pair["token_type_ids"]) # 在 BERT RoBERTa 等模型中,0 对应第一个句子,1 对应第二个句子;现代大模型一般都是 0
    # 输出:
    # [[0 0 0 0]
    # [0 0 0 0]]

    # 长文本分段(带重叠)
    long_text = "This is a very long text that exceeds the max length. We need to split it into chunks with overlap."
    inputs_long = tokenizer(
    long_text,
    max_length=10,
    truncation=True,
    stride=3,
    return_overflowing_tokens=True,
    return_offsets_mapping=True
    )
    print(inputs_long.keys()) # 输出:dict_keys(['input_ids', 'attention_mask', 'offset_mapping', 'overflow_to_sample_mapping'])
    print(type(inputs_long)) # <class 'transformers.tokenization_utils_base.BatchEncoding'>
    print(len(inputs_long["input_ids"])) # 输出:3
    print(inputs_long["input_ids"])
    # 输出:
    # [
    # [3031, 472, 358, 2481, 2093, 3402, 524, 159, 369, 156],
    # [159, 369, 156, 5041, 112, 1769, 1838, 408, 11694, 574],
    # [408, 11694, 574, 1394, 48782, 537, 30033, 112]
    # ]

    # 批量处理多条文本
    texts = ["你好", "世界", "深度学习"]
    encoded_batch = tokenizer(texts)
    print(encoded_batch.keys()) # 输出:dict_keys(['input_ids', 'attention_mask'])
    print(encoded_batch["input_ids"]) # [[135], [487], [18834]]

其他更多完整详细示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
from transformers import AutoTokenizer
MODEL = "path_to_model"
tokenizer = AutoTokenizer.from_pretrained(MODEL)

# 故意构造 3 条长度差异很大的句子
raw_texts = [
"Hello world!",
"This is a much longer sentence, designed to exceed the usual 8 ~ 12 token budget.",
"Hi"
]

def group_title(title):
print(f"\n{title}")

def pretty_print(enc):
print("input_ids :", enc["input_ids"])
if "attention_mask" in enc:
print("attention_mask :", enc["attention_mask"])
if "token_type_ids" in enc:
print("token_type_ids :", enc["token_type_ids"])
if "overflowing_tokens" in enc:
print("overflowing_tokens:", enc["overflowing_tokens"])
print("-" * 50)

# 默认:不 padding 也不 truncation -> 长度各异的 list
group_title("默认:padding=False, truncation=None")
enc = tokenizer(raw_texts)
pretty_print(enc)
print("每条长度:", [len(ids) for ids in enc["input_ids"]])

# 只 padding 不 truncation -> 长度一致,无截断
group_title("只 padding(最长)不 truncation")
enc = tokenizer(raw_texts, padding=True)
pretty_print(enc)
print("每条长度:", [len(ids) for ids in enc["input_ids"]])

# 只 truncation 不 padding -> 超过 max_length 被截断,仍返回 list
group_title("只 truncation=True 不 padding, max_length=12")
enc = tokenizer(raw_texts, truncation=True, max_length=12)
pretty_print(enc)
print("每条长度:", [len(ids) for ids in enc["input_ids"]])

# padding + truncation -> 固定长度,超长截断,不足填充
group_title("padding + truncation, max_length=16")
enc = tokenizer(raw_texts, padding=True, truncation=True, max_length=16)
pretty_print(enc)
print("每条长度:", [len(ids) for ids in enc["input_ids"]])

# 返回 PyTorch 张量(必须 padding,否则长度不同会抛错)
group_title("return_tensors='pt' 必须配合 padding")
enc = tokenizer(raw_texts, padding=True, truncation=True, max_length=16, return_tensors="pt")
print("input_ids 形状:", enc["input_ids"].shape)
print("attention_mask 形状:", enc["attention_mask"].shape)
print("类型:", type(enc["input_ids"]))

# 按照窗口拆分成多个 chunks,stride + return_overflowing_tokens 演示滑动窗口
group_title("stride=4 + return_overflowing_tokens 截断窗口")
long_text = "This is a very long sentence that will be split into overlapping chunks."
enc = tokenizer(long_text, truncation=True, max_length=10, stride=4, return_overflowing_tokens=True, return_offsets_mapping=True)
print("共返回片段数:", len(enc["input_ids"]))
for idx, (ids, off) in enumerate(zip(enc["input_ids"], enc["offset_mapping"])):
print(f"片段 {idx}: tokens={ids}")
print(f"offsets={off}")
print("-" * 30)

# padding='max_length' 强制 pad 到 max_length
group_title("padding='max_length' 强制 32")
enc = tokenizer(raw_texts, padding='max_length', max_length=32)
print("每条长度:", [len(ids) for ids in enc["input_ids"]])

# pad_to_multiple_of 演示 8 的倍数填充
group_title("pad_to_multiple_of=8, 填充到最小符合要求的 8 的倍数,比 21 大的倍数是 24")
enc = tokenizer(raw_texts, padding=True, pad_to_multiple_of=8)
print("每条长度:", [len(ids) for ids in enc["input_ids"]])

# tokenizer 类属性:默认最大长度 & 截断策略
group_title("tokenizer 默认属性")
print("tokenizer.model_max_length :", tokenizer.model_max_length) # 从原始模型文件中读取
print("tokenizer.truncation_side :", tokenizer.truncation_side)
print("tokenizer.padding_side :", tokenizer.padding_side)
# 其他:如 tokenizer.vocab_size 等

# 默认:padding=False, truncation=None
# input_ids : [[20769, 3121, 224], [3156, 597, 483, 2799, 6991, 14941, 235, 7605, 533, 15890, 494, 15299, 444, 247, 8581, 444, 240, 241, 10539, 14517, 237], [23383]]
# attention_mask : [[1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1]]
# --------------------------------------------------
# 每条长度: [3, 21, 1]
#
# 只 padding(最长)不 truncation
# input_ids : [[20769, 3121, 224, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], [3156, 597, 483, 2799, 6991, 14941, 235, 7605, 533, 15890, 494, 15299, 444, 247, 8581, 444, 240, 241, 10539, 14517, 237], [23383, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]]
# attention_mask : [[1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
# --------------------------------------------------
# 每条长度: [21, 21, 21]
#
# 只 truncation=True 不 padding, max_length=12
# input_ids : [[20769, 3121, 224], [3156, 597, 483, 2799, 6991, 14941, 235, 7605, 533, 15890, 494, 15299], [23383]]
# attention_mask : [[1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1]]
# --------------------------------------------------
# 每条长度: [3, 12, 1]
#
# padding + truncation, max_length=16
# input_ids : [[20769, 3121, 224, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], [3156, 597, 483, 2799, 6991, 14941, 235, 7605, 533, 15890, 494, 15299, 444, 247, 8581, 444], [23383, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]]
# attention_mask : [[1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
# --------------------------------------------------
# 每条长度: [16, 16, 16]
#
# return_tensors='pt' 必须配合 padding
# input_ids 形状: torch.Size([3, 16])
# attention_mask 形状: torch.Size([3, 16])
# 类型: <class 'torch.Tensor'>
#
# stride=4 + return_overflowing_tokens 截断窗口
# 共返回片段数: 2
# 片段 0: tokens=[3156, 597, 483, 2606, 2218, 14941, 649, 1253, 621, 11819]
# offsets=[(0, 4), (4, 7), (7, 9), (9, 14), (14, 19), (19, 28), (28, 33), (33, 38), (38, 41), (41, 47)]
# ------------------------------
# 片段 1: tokens=[649, 1253, 621, 11819, 1519, 44984, 48907, 237]
# offsets=[(28, 33), (33, 38), (38, 41), (41, 47), (47, 52), (52, 64), (64, 71), (71, 72)]
# ------------------------------
#
# padding='max_length' 强制 32
# 每条长度: [32, 32, 32]
#
# pad_to_multiple_of=8
# 每条长度: [24, 24, 24]
#
# tokenizer 默认属性
# tokenizer.model_max_length : 131072
# tokenizer.truncation_side : right
# tokenizer.padding_side : right

常见注意事项

  • 建议批量处理,而不是单条处理
    • 大批量处理时注意内存使用
  • 不同模型有不同的最大长度限制,模型配置中会包含
  • 某些模型会自动添加特殊标记,也可以自己预设标记:
    1
    tokenizer.pad_token = tokenizer.eos_token  # 设置填充token

附录:transformers.AutoTokenizer.decode 的使用

  • tokenizer.decode 用于将模型输出的 token ID 序列(或 token 索引序列)反向解码为自然语言文本

    • 会自动处理 token 间的拼接逻辑(如去除 subword 分隔符、恢复原始词汇),屏蔽底层 tokenization 细节(如 BPE、WordPiece 等子词切分的拼接)
  • 函数签名:

    1
    2
    3
    4
    5
    6
    7
    def decode(
    self,
    token_ids: Union[int, List[int], "np.ndarray", "torch.Tensor", "tf.Tensor"],
    skip_special_tokens: bool = False,
    clean_up_tokenization_spaces: Optional[bool] = None,
    **kwargs,
    ) -> str:
  • 需要注意:

    • 解码结果依赖于 tokenizer 的训练数据和切分规则(如 BPE 切分的词汇可能需要特殊拼接逻辑),需确保解码使用的 tokenizer 与编码(tokenizer.encode)时一致;
    • 对于包含填充 token(PAD)的 input_ids,建议设置 skip_special_tokens=True 或指定 padding_token_id,避免解码出无效的填充文本;

参数说明

  • token_ids:必选,对应编码时的 input_ids,需要解码的 token ID 序列,支持两种输入形式:
    • 单个整数(单个 token ID),例如 101、2023;
    • 整数列表/张量(多个 token ID 组成的序列),例如 [101, 2054, 2182, 102]、torch.tensor([101, 3845, 102]),也支持批量输入(如二维张量,shape 为 [batch_size, seq_len])
      • 注意:这里的批量不是 list 形式,必须是一个张量才行
  • skip_special_tokens:布尔值,默认 False
    • 设为 True 时,解码过程中会自动跳过所有特殊 token(如 [CLS]、[SEP]、[PAD]、[MASK] 等,具体取决于 tokenizer 的配置);
    • 设为 False 时,会保留所有特殊 token 原样输出(例如解码结果可能包含 "<s>" "<pad>" 等标记)
    • 实际使用中通常设为 True,以获取干净的自然语言文本
  • clean_up_tokenization_spaces:布尔值,默认 True
    • 设为 True 时,会自动清理解码后文本中多余的空格(如 subword 拼接后残留的空格、特殊 token 移除后的空字符);
    • 设为 False 时,保留 token 拼接后的原始空格布局(可能出现连续空格或不合理空格)
    • 注:clean_up_tokenization_spaces 参数的必要性已大大降低,亲测这个参数为 True 或 False 的结果是一样的,多个特殊的字符串测试没有例外
  • padding_token_id:整数,默认使用 tokenizer 配置中的 padding_token_id(如 [PAD] 对应的 ID)
    • 当 input_ids 中包含填充 token ID 时,可通过该参数指定需要忽略的填充 ID,避免解码出 [PAD] 对应的文本标记
  • stop_at_eos:布尔值,默认 False(部分 tokenizer 版本默认 True,需结合具体库版本确认)
    • 设为 True 时,解码过程中遇到 eos_token_id 会立即终止,仅返回 eos_token_id 之前的文本;
    • 设为 False 时,会完整解码 input_ids 中的所有 token ID,包括 eos_token_id 之后的内容

使用示例

  • 使用示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # 基础解码(跳过特殊 token):
    from transformers import AutoTokenizer
    tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
    input_ids = [101, 2054, 2182, 2003, 1996, 3014, 102] # 包含 [CLS](101)和 [SEP](102)
    text = tokenizer.decode(input_ids, skip_special_tokens=True)
    print(text) # 输出:"i love this movie"(自动去除特殊 token 并拼接子词)

    # 保留特殊 token:
    text = tokenizer.decode(input_ids, skip_special_tokens=False)
    print(text) # 输出:"[CLS] i love this movie [SEP]"

    # 解码批量输入(二维张量):
    import torch
    batch_input_ids = torch.tensor([[101, 2054, 102], [101, 2182, 102]])
    batch_text = tokenizer.decode(batch_input_ids, skip_special_tokens=True, clean_up_tokenization_spaces=True)
    print(batch_text) # 输出批量文本(具体格式取决于 tokenizer,通常为列表或拼接字符串)

decode() vs batch_decode()

  • tokenizer.decode() 和 tokenizer.batch_decode() 的核心区别是 处理输入的维度和场景
    • tokenizer.decode() 用于解码「单个序列」的 input_ids,无批量优化
    • tokenizer.batch_decode() 用于批量解码「多个序列」的 input_ids,有批量优化,比循环调用 decode() 更高效
    • 本质是「单样本」与「多样本」的适配差异
  • 输入要求:
    • decode() 仅接受 1维输入 :
      • 支持 list[int](如 [101, 2054, 3110, ...])、1D Tensor、1D numpy数组,若传入2D数据会直接报错
    • batch_decode() 仅接受 2维输入 :
      • 支持 list[list[int]](如 [[101, 2054], [101, 3110]])、2D Tensor、2D numpy数组,自动处理批量样本
  • 输出情况:
    • decode() 输出单个字符串(str);
    • batch_decode() 输出字符串列表(list[str])
  • 两者用相同参数解码时,结果应该一致

附录:中英文 Tokenization 编码效率对比

  • 注:本文以 Qwen3 编码方式为例,且只是简单的粗糙对比,不严谨

  • 分别尝试了将文本从中文翻译为英文和从英文翻译为中文两种方式(翻译工作由 豆包完成),代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    from transformers import AutoTokenizer
    import sys
    model_name = "/Users/jiahong/Workspace/IdeaProjects/Torch/LLaMA-Factory/model/Qwen3-0.6B/"

    tokenizer = AutoTokenizer.from_pretrained(model_name)

    print("Chinese to English")
    print("Chinese")
    prompt_c = """xxx"""
    print(f"{len(prompt_c)} 字, 存储 {sys.getsizeof(prompt_c)-49} B") # 49 是对象的固定存储部分
    token_c = tokenizer.tokenize(prompt_c)
    print(f"len of tokens: {len(token_c)}, {len(prompt_c)/len(token_c)} 字/token")

    print("English")
    prompt_e = """xxx"""
    print(f"{len(prompt_e.split(' '))} 单词, {sys.getsizeof(prompt_e)-49} B, len: {len(prompt_e)}")
    token_e = tokenizer.tokenize(prompt_e)
    # print(token_e)
    print(f"len of tokens: {len(token_e)}, {len(prompt_e)/len(token_e)} B/token, {len(prompt_e.split(' '))/len(token_e)} words/token")

    print("="*30)
    print("English to Chinese")
    print("Chinese")
    prompt_c = """xxx"""
    print(f"{len(prompt_c)} 字, 存储 {sys.getsizeof(prompt_c)-49} B")
    token_c = tokenizer.tokenize(prompt_c)
    # print(token_c)
    print(f"len of tokens: {len(token_c)}, {len(prompt_c)/len(token_c)} 字/token")

    print("English")
    prompt_e = """xxx"""
    print(f"{len(prompt_e.split(' '))} 单词, {sys.getsizeof(prompt_e)-49} B, len: {len(prompt_e)}")
    token_e = tokenizer.tokenize(prompt_e)
    print(f"len of tokens: {len(token_e)}, {len(prompt_e)/len(token_e)} B/token, {len(prompt_e.split(' '))/len(token_e)} words/token")

    # Chinese to English
    # Chinese
    # 2514 字, 存储 5053 B
    # len of tokens: 1784, 1.4091928251121075 字/token
    # English
    # 1611 单词, 21073 B, len: 10524
    # len of tokens: 2239, 4.700312639571237 B/token, 0.7195176418043769 words/token
    # ==============================
    # English to Chinese
    # Chinese
    # 2061 字, 存储 4147 B
    # len of tokens: 1421, 1.450387051372273 字/token
    # English
    # 814 单词, 10739 B, len: 5357
    # len of tokens: 1223, 4.3802125919869175 B/token, 0.6655764513491415 words/token
  • 结论(以 Qwen3 的编码方式为例):

    • 中文:约 1.5 字/token
    • 英文:约 0.7 words/token,且大约合 4.5 B/token
    • 编码效率上看,汉译英时,中文优于英文(0.8:1);英译汉时,则英文较优(1.16:1)
  • 有趣的是,汉译英和英译汉的效率差异很大:

    • 汉译英时,1 个单词大概对应 1.56 个中文字
    • 英译汉时,1 个单词大概对应 2.53 个中文字
    • 解释:不翻译别人,自由发挥的情况下,文字使用效率是最高的

附录:新增 Token 的方法

  • 可以对 HuggingFace 模型添加新的 Token,添加 Token 的类型可以有两种
    • 特殊 Token(Special Token):
      • Special Token 是具有特定语义或结构作用的标记,常用于向模型指示文本的结构、任务类型或特殊信息
      • Special Token 会保证原子性 (Atomicity) : Special Token 永远不会被分词器拆分成更小的 subword 或字符,它们作为一个整体被识别
      • 通常可通过两种方式添加:
        • 通过 tokenizer.add_special_tokens() 方法添加(推荐使用)
          • 接受一个字典,如 {'additional_special_tokens': ['<NEW_TOKEN>']}
        • 通过 tokenizer.add_tokens() 并设置参数 special_tokens=True 来添加
      • 在解码时,通常可以选择跳过这些 Special Token(使用 skip_special_tokens=True)
      • Special Token 添加后,词表中显示它的属性为 "special": true
    • 常规 Token (Regular Token):
      • Regular Token 指的是模型训练过程中常见的词汇、词片段(subword)或字符,它们在自然语言文本中出现,主要用于表示文本内容
      • 主要通过 tokenizer.add_tokens() 方法添加
        • 新添加的 Token 会被加入到词汇表中,从现有词汇表的末尾开始分配新的索引
        • 如果添加的 Token 是一个完整的词汇,它通常不会被分词器拆分 ,但如果它本身就是 subword 的一部分,则会按照分词器的逻辑处理
        • 如果一个新词汇被添加,分词器在遇到该词时会优先使用该新词汇作为一个整体
      • 常规 Token 添加后,词表中显示它的属性为 "special": false
  • 此外,如果在模型预训练时已经增加了 <mask_id> 还可以手动添加(已测试没问题):
    • 直接修改 tokenizer.json 中的字段
      • 分别修改 <mask_id_xx> 为指定 Token 文本,比如 <start_think>
      • 映射关系一般有两个地方
        • 一个是 "added_tokens" 中的 ID 映射
        • 另一个是词表中的映射关系(一般也在 "tokenizer.json" 的 "model" -> "vocab" 中)

新增 Token 对模型嵌入层的影响

  • 无论是哪种方式添加了 Token,都必须调用 model.resize_token_embeddings(len(tokenizer)) 来调整模型的词嵌入层 (Embedding Layer) 大小,使其与新的词汇表大小匹配,否则新增的 Token 无法被模型正确处理
  • Special Tokens 通常会被赋予特定的嵌入初始化方式(例如部分模型对特殊 tokens 有默认初始化逻辑),而普通 tokens 的嵌入则可能随机初始化

修改文件的方式添加 Special Token

  • 不建议使用这种方式,建议通过代码动态添加并 save_pretrained 的方式持久化到文件中
  • 手动修改文件容易出错,比如 Special Token 的添加,除了词表外,还需要修改 special_tokens_map.json 文件, 否则 tokenizer 不会将其识别为 Special Token
    • 注:部分模型也可以将 “special_tokens_map” 作为一个 key 放到 tokenizer.json 中?

tokenizer.add_special_tokens() 函数使用及对比

  • tokenizer.add_special_tokens() 接受一个字典对象作为参数,且字典的 key 必须在 tokenizer.SPECIAL_TOKENS_ATTRIBUTES 中
  • 在 Hugging Face 的 tokenizers 库中,tokenizer.SPECIAL_TOKENS_ATTRIBUTES 是分词器类(如 PreTrainedTokenizer)的一个内置属性,它定义了 “特殊 token 类型” 的标准名称集合
    • 注意:不同版本可能写作 SPECIAL_TOKENS_ATTRIBUTES 或类似属性,本质一致
    • 常见配置为:['bos_token', 'eos_token', 'unk_token', 'sep_token', 'pad_token', 'cls_token', 'mask_token', 'additional_special_tokens']
    • “additional_special_tokens” 用于添加自定义的 Special Token
  • 使用 tokenizer.add_special_tokens() 添加 与 tokenizer.add_tokens(..., special_tokens=True) 的区别:
    • Special Token 是否会出现在 special_tokens_map.json 文件 和 tokenizer_config.json 的 "additional_special_tokens" 字段中?
      • tokenizer.add_tokens(..., special_tokens=True) 方式添加的不会出现
      • tokenizer.add_special_tokens() 方式添加的则会出现
    • 两者都会出现在 tokenizer_config.json 的 "added_tokens_decoder" 中
    • 说明:add_special_tokens(标准用法)会将新添加的 Token 关联到分词器的任何标准特殊 Token 属性,而 add_tokens(..., special_tokens=True) 不会
      • add_tokens(..., special_tokens=True) 添加后只能在词表中找到索引,其他地方看不到
  • 添加 Special Token 的示例:
    1
    2
    3
    4
    5
    print(tokenizer.special_tokens_map) # 可能输出为:{'bos_token': '<bos>', 'eos_token': '<eos>', 'unk_token': '<unk>', 'pad_token': '<pad>'}
    tokenizer.add_special_tokens({"additional_special_tokens": ["<llm_assistant>", "<llm_user>"]})
    print(tokenizer.special_tokens_map) # 可能输出为:{'bos_token': '<bos>', 'eos_token': '<eos>', 'unk_token': '<unk>', 'pad_token': '<pad>', 'additional_special_tokens': ['<llm_assistant>', '<llm_user>']}

    # 注意:tokenizer.add_tokens(["<longcat_assistant>", "<longcat_system>"], special_tokens=True) 方式添加的 Special Token 不会出现在 special_tokens_map 中

新增 Special Token 与解码注意事项

  • 特别注意:推理引擎解码时,一般会默认会自动跳过所有 Special Token
  • 在定义 Special token 时需要非常小心
    • 工具调用等场景中,会依赖工具调用相关的 Token 的明文文本做工具解析(例如 <tool_call> 等)
    • 因此需要设置为这部分 Token 为 Regular Token (配置方式为:tokenizer.json 中设置 "special": false)
  • 必须设置为 Regular Token 的 Token 有:
    • tool 调用回复相关的 Token
    • thinking 相关的 Token

新增 Special Token 的初始化

  • 新增 Token 的训练可能不太充足,可考虑使用已有 Token 的 embedding 来进行初始化
  • 常用方式:使用添加 Token 前的编码 Token(可能为多个)对应的 embedding 均值作为添加 Token 后的 embedding 初始值
    • 比如新增 '<think>' 作为 Special Token,这个 Token 在原来的场景里面可能是编码为 ['<', 'think', '>'] 这三个 Token 的,此时可以使用 这三个 Token embedding 的均值作为新 Token 的 embedding 初始值
    • 注:这种方式对于有语义的 Special Token 可能有一定帮助(比如 assistant: 或 <assistant:> 等),但是对于非常特殊的非语义 Token 理论上没有帮助,如 ACBD34 或 <ACBD34> 等)
    • 亲测:这种方法得到的结果可能持平甚至负向,原因是提前保留的 <mask_id_xx> 对应的 Embedding 其实已经被训练过了(压低出现的概率),这里替换可能反而会影响结果
  • 初始化流程(以 HF 格式的模型为例):
    • 在 model.safetensors.index.json 中 找到 embedding 层变量和 LM Head 层变量所在的 safetensors 文件
    • 读取对应文件,检索到对应的 embedding 并修改(注意:一定要两个层的参数都修改)
      • 注:可能部分模型上该 embedding 参数和 LM Head 层参数是共享的?
  • 已有模型,修改初始化值的代码示例(一般来说续修改 embedding 层 和 LM Head 层):
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    # 以使用旧 Token ID embedding 的均值初始化为例
    from safetensors.torch import load_file, save_file
    from transformers import AutoTokenizer
    import torch

    # 新旧路径加载 Tokenizer
    tokenizer_base = AutoTokenizer.from_pretrained('/path_to_old_base_tokenizer/') # 不包含新 Special Token
    tokenizer_new = AutoTokenizer.from_pretrained('/path_to_new_tokenizer/') # 包含新 Special Token

    # 指定需要修改的文件
    # # 对于多文件存储的大模型参数,需要在 model.safetensors.index.json 中找到 Embedding 层参数或者 LM Head 层参数所在的路径
    st_path_base = '/path_to_old_base_safetensors/model_xx-of-xx.safetensors' # 随机初始化/原始初始化的模型权重
    st_path_new = '/path_to_new_safetensors/model_xx-of-xx.safetensors' # 待写入路径,写入后包含自定义初始化的 embedding 的结果

    # 从原来的 safetensors 文件路径读取数据,得到一个字典数据 dict[name, weight]
    safetensors_weights = load_file(st_path_base)

    # # print weights for debug
    # for name, weight in safetensors_weights.items():
    # print(name, weight.shape)

    # 从上面的打印结果中查找对应的权重名称并抽取出对应的值,也可以写个 for 循环,逐个修改
    weight_name = '$LM_Head_name or $Embedding_name'
    embedding_layer_weight = safetensors_weights[weight_name]

    # 待替换的 Special Token
    # # 注意肯定是不存在于 tokenizer_base 中但新定义到 tokenizer_new 中的新 Token
    # # 这种 Special Token 使用第一个 tokenizer_base 编码一般是编码为几个 Token 的组合,但使用 tokenizer_new 时一般是编码为一个整体(跟新修改的 ID 对齐)
    new_special_tokens = [3,5,18] # 这里只是一个示例,需要修改为自己的;也可以使用 文本而不是 ID,下面的脚本对应修改即可

    for new_token_id in new_special_tokens:
    token_text = tokenizer_new.decode([new_token_id]) # 抽取原始文本,也可提前定义
    old_token_ids = tokenizer_base(token_text).input_ids # 使用旧的 tokenizer 编码新的样本
    # print(token_text, new_token_id, old_token_ids) # 可打印看看 Token 对应情况
    new_embedding = torch.mean(safetensors_weights[weight_name][old_token_ids], dim=0) # 按照旧 Token ID 检索向量并做平均
    # print(new_token_id, "old == new: ", torch.equal(embedding_layer_weight[new_token_id], new_embedding)) # 打印日志,纯新的 Token 不应该相等,旧的则应该相等(因为旧的两者 ID 都只有一个且相同)
    safetensors_weights[weight_name][new_token_id] = new_embedding # 核心代码,替换 Token embedding

    # 将 safetensors 对象写入新的目标路径
    save_file(safetensors_weights, st_path_new)

附录:新增 Special Token 初始化的讨论

  • 纯新增的 Special Token 确实可以考虑使用一些特殊的初始化操作,比如使用 Embedding 的均值
  • 非纯新增的 Special Token (使用预留的 <mask_1> 等转换而来), 理论上在预训练中会被训练到:
    • 即使 <mask_1>等 token 永远没出现在语料库中,也会参与训练,因为训练时是整个词表参与的,Softmax 保证了每次都会训练到所有 Embedding
    • 理解:为了压低语料库中不存在的 Token (如 <mask_1>)出现的概率(监督学习目标输出其他正常 Token),也需要学习 <mask_1> 等 Token 的 Embedding 的
  • 思考:使用预留的 <mask_1> 等转换 Special Token,特别是 Special Token 没有太明确的明文语义时,不应该随便替换 Special Token 的 Embedding
    • 因为替换后得到的可能是很奇怪的无语义 Embedding,反而丢失了原始 <mask_1> 在预训练中学到的 Embedding(这个 Embedding 可以保证这个 Special Token 以较低的概率出现)
    • 进一步的理解:如果替换 Embedding,也不建议使用原始 Token 文字使用旧 词表编码的结果
      • 因为特意设计作为非常特殊用途的,非语义的 Special Token,理论上就是 Special 的,一般来说不应该含有语义(其文本形式应该可以为任意值),这时候使用 旧词表的编码结果来初始化是奇怪的,打破了这种特殊的 Special Token 没有语义的设定

NLP——SGLang-Qwen3本地部署

  • 参考链接:
    • sglang-zh.llamafactory.cn

整体说明

  • SGLang 全称是 Structured Generation Language,是由 LMSYS Org 发起的开源项目
  • SGLang 通过共同设计后端运行时和前端语言,使用户与模型的交互更快、更可控
  • 采用 RadixAttention 技术,通过基数树管理键值缓存(KV Cache),支持多轮对话中共享前缀的缓存复用,在多轮任务中可将缓存命中率提升 3-5 倍,显著降低延迟
  • SGLang 的前端采用编译器式设计,通过领域特定语言(DSL)简化复杂任务编程,后端运行时优化调度和资源分配,还可通过正则表达式和有限状态机(FSM)实现约束解码,直接生成 JSON 等结构化数据
  • SGLang 更适合处理复杂任务,如多轮对话、规划、工具调用(如调用 API 或数据库)等,以及需要生成 JSON、XML 等结构化数据的任务,如智能客服、数据分析等
    • 在 Llama-7B 多轮对话任务中,吞吐量比 vLLM 高 5 倍,延迟降低 30%-50%
  • 注:SGLang 接口经常变化,导致不同版本对应的接口不可复用,非常麻烦!

安装 SGLang

  • 通过 pip 安装
    1
    2
    3
    4
    5
    pip install --upgrade pip
    pip install "sglang[all]"

    # Install FlashInfer CUDA kernels
    pip install flashinfer -i https://flashinfer.ai/whl/cu121/torch2.4/

部署服务

  • 使用命令行启动
    1
    python -m sglang.launch_server --model-path ~/llm/model/Qwen3-0.6B --port 30000

请求服务

  • 注:下面的命令暂未考虑 Qwen 模型的 Chat 模版

  • 使用命令行访问

    1
    2
    3
    4
    5
    6
    7
    8
    9
    curl http://localhost:30000/generate \
    -H "Content-Type: application/json" \
    -d '{
    "text": "白日依山尽,",
    "sampling_params": {
    "max_new_tokens": 16,
    "temperature": 0
    }
    }'
  • 使用 OpenAI 兼容的 API 访问

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    import openai
    client = openai.Client(
    base_url="http://127.0.0.1:30000/v1", api_key="EMPTY")

    # Text completion
    response = client.completions.create(
    model="default",
    prompt="The capital of France is",
    temperature=0,
    max_tokens=32,
    )
    print(response)

    # Chat completion
    response = client.chat.completions.create(
    model="default",
    messages=[
    {"role": "system", "content": "You are a helpful AI assistant"},
    {"role": "user", "content": "列出三个国家和他们的首都"},
    ],
    temperature=0,
    max_tokens=64,
    )
    print(response)

    # Text embedding,需要在服务启动命令中添加 --is-embedding 参数才能访问下面的接口
    response = client.embeddings.create(
    model="default",
    input="How are you today",
    )
    print(response)

NLP——LLM相关数据集


Tulu3

  • 原始论文:(Tulu3)Tülu 3: Pushing Frontiers in Open Language Model Post-Training
  • Tulu3 数据集是艾伦人工智能研究所(Ai2)为训练Tulu3模型而创建的大规模多语言文本数据集
  • Tulu3 数据集包含 939,344 个样本,覆盖多种语言和任务,数据来源广泛,包括 Coconot、Flan v2、No Robots 等
  • Tulu3 数据集支持语言模型的训练和微调,特别是在多语言环境下,其结构包含标准的指令调整数据点,如 ID 等
  • Tulu3 数据集的意义在于为研究人员和开发人员提供了丰富的语言资源,以增强和优化多语言人工智能模型的性能,可用于教育和研究目的,但需遵循特定的许可协议

lmarena-ai/arena-human-preference-140k

  • HuggingFace:huggingface.co/datasets/lmarena-ai/arena-human-preference-140k
  • Blog:A Deep Dive into Recent Arena Data, 20250731
  • lmarena-ai/arena-human-preference-140k 数据集包含文本类别的用户投票(vote)数据,累计大约 14W 数据
    • 每行代表一次投票,记录用户在特定对话场景下对两个模型(model_a 和 model_b)的评判结果,同时包含完整对话历史及元数据
  • 核心字段说明如下:
    • id:每次投票/每行数据的唯一反馈ID
    • evaluation_session_id:每次评估会话的唯一 ID,一个会话可包含多次独立投票/评估
      • 经测试,同一个 evaluation_session_id 对应的模型可能不同,如何理解?
    • evaluation_order:当前投票的评估顺序(序号)
    • winner:对决结果,取值为
      • model_a(模型 A 获胜)
      • model_b(模型 B 获胜)
      • tie(平局)
      • both_bad(两者均差)
    • conversation_a/conversation_b:当前评估轮次中两个模型对应的完整对话内容
    • full_conversation:完整对话历史,包含上下文提示词及所有先前评估轮次的模型回复
      • 注意:每次投票后会重新采样模型,因此完整上下文中的响应模型会有所不同
      • 问题:发现多轮数据中,两个模型的上下文是分离的,所以这部分训练时相当于是多轮信号
    • conv_metadata:聚合元数据(含格式标记、令牌计数),用于风格控制
    • category_tag:标注标签,包括数学(math)、创意写作(creative writing)、高难度提示词(hard prompts)、指令遵循(instruction following)四类
    • is_code:对话是否涉及代码(布尔值)
  • 其他分析:
    • 大部分数据是英语,约 51.8%
    • 中文简体占比较少,仅 5%
    • 中文繁体占比,仅 0.65%
  • 注:这个数据集是用作 Reward Model 的优秀模型,也可以用于 SFT 和 DPO 等
    • 用作 SFT 时,多轮数据也可以使用;用作 Reward Model 和 DPO 时,多轮数据暂无法使用

NLP——LLM预训练相关笔记

本文主要介绍LLM预训练相关的笔记

  • 参考链接
    • [如何从零开始训练大模型(minicpm分享&讨论)](https : //zhuanlan.zhihu.com/p/686664720):minicpm模型训练分享
    • [LLM训练-Pretrain](https : //zhuanlan.zhihu.com/p/718354385):非常详尽的一些实操笔记和思考,论文的许多笔记是参考了这篇博客
    • [花费千万试出来的LLM预训练经验](https : //mp.weixin.qq.com/s/MF6MuZC_fvKu25S1MzXkCQ)

预训练的重要性

  • 预训练阶段是知识注入的关键阶段
  • 已有研究基本都证明了,预训练阶段是知识注入阶段,后续的 post-training 阶段主要是激发模型的能力

预训练一个大模型有哪些需要关注的点?

数据爬取和解析

  • 数据需要上 T,甚至几十 T 才够用
  • 爬虫不好做,容易被封 IP
  • 很多论文是 PDF,使用 Python 解析可能不准确,使用 GPT4 等解析成本较高
  • 最好多找开源的数据

数据清洗、去重、配比

  • 清洗很重要
  • 需要自己训练打分器,最好是使用 BERT 系列模型,而不是 Decoder-Only 模型训练打分器,分类器的 label 可以使用 GPT4 标注,或者按照优质源与劣质源作为正负label标记

    需要注意的是,基本上大家都认同:同等 size 下,BERT 结构的模型的表征能力是强于 transformer-decoder 模型的,因此打分模型最好还是从 BERT 家族中选一个来训,效果好、速度还快

  • 除了打分器,规则也很重要
  • 需要做好数据脱敏,避免人名等隐私被侵犯出来
  • 数据去重很重要,网页互相引用会造成爬取到的数据重复性太高,做不了 sentence 粒度的,就做 document 粒度的
  • 可以训练一个文档分类器来对文档进行分类,不同类别使用不同的相似度阈值去重
  • 数据配比一般是 中 : 英 : code = 4 : 4 : 2

数据顺序

  • 同样大小的数据,按照不同顺序输入模型,结果也会不同,基本上可以类似课程学习的思想,想简单后复杂,先优质再劣质等
  • 不同文档的句子之间应该互相看见吗?llama3.1 强调了不能看见,但是作者理解看见其实也还好

流水化数据线

  • 数据需要提前 tokenization
  • 训练和数据处理应该是两个进程
  • 训练过程中多存 save_checkpoint,为了训练时决定是否即时保存,可以使用一些规则,比如“遇到存在 save 文件的文件夹就保存一下”

数据实验

  • 训练前可以先用小模型验证一下,如何设置数据配比,顺序更合适

模型结构

  • RoPE + GQA + RMS_Norm + SwiGLU
  • 预训练不建议创新,试错成本太高,一般来说照抄开源的优秀的技术报告即可

模型大小

  • 按照合适来选择,可以通过时间和数据来评估自己需要多少参数量的模型
  • layer_num 和 hidden_size 同步增加或者减少更好些
  • seq_len 不要上来就很大,RoPE 的 NTK 外推方法已经是各大厂标配的方案:4K/8K + RoPE 小 Base + 90% 数据量 –> 32K/64K + RoPE 大 Base + 10% 数据量

训练框架

  • Megatron 和 DeepSpeed之间选:

    Megatron 和 DeepSpeed 该怎么选?直接说结论:从零开始的 Pretrain 必须选 Megatron,Continue-Pretrain 可以考虑使用 DeepSpeed, 换句话说,T 级别的 token 训练量必须是 Megatron,B 的级别 token 训练量无所谓

  • DeepSpeed 加载速度慢
  • 其他:

    无论是用哪个训练框架,都要记得把 attention 的默认方式换成 flash_attention

训练时评估

  • loss 分不同种类观察,即 channel_loss,比如英文、中文、代码分开观察

训练后评估

  • 可以使用 ACC 来衡量评估结果

概率探针评估

  • 可以用于查看某个目标条件概率是否在增大

一个特别干货的分享

  • 参考链接:[如何从零开始训练大模型(minicpm分享&讨论)](https : //zhuanlan.zhihu.com/p/686664720), 这个博客分享了包含了一些地数据集处理、清洗等
  • 文中很有意思的一段话

    但我们回到2020年,当大部分人都在基于 bert 做各种魔改的时候
    OpenAI 发现了这么一个规律。数据,训练,参数一直增长下去,好像 loss 的确是在不断的下降哎?
    于是,他们拿着这个 paper 去问微软的 CTO,你想不想看看这个 loss 下降到一定程度会发生什么?
    会发生什么?
    chatgpt 就出来了


LLM 预训练实战经验

  • 以下内容参考自:[花费千万试出来的LLM预训练经验](https : //mp.weixin.qq.com/s/MF6MuZC_fvKu25S1MzXkCQ)

随机初始化的痛点

  • 训练成本高 :早期使用 Qwen1.5-0.5B 结构,训练 8T+token 通用数据仅达勉强效果,相比 1-2T 训练量提升有限
  • 核心问题 :能否复用已有模型?直接续训开源模型存在两大问题:
    • 参数量不匹配(如业务需要 3B 模型,开源仅有 7B/4B/1.5B,则还是需要重头训练)
    • 版权风险(如 Qwen2.5-4B 无商用许可),注:Qwen 的同一系列不同模型的开源许可也可能不同

模型初始化:大模型 to 小模型

  • 适用场景 :当拥有可商用大模型(如14B),需初始化小模型(如3B)
  • 主流方法对比 :
    方法 核心原理 数据要求 初始化效果 限制条件
    Sheared LLaMA 训练mask筛选重要参数 几B-几十B高质量数据(如代码/数学) 初始loss更低,几十步收敛至2.x 模块类型不可改(激活函数/attention类型固定)
    Weight Subcloning 计算神经元重要度裁剪 需输入数据获取激活值 后期loss略低,评测效果持平 同上
  • 实践效果 :14B -> 3B 裁剪可行,结合蒸馏后 500B token 可达原 8T 效果
  • 关键结论 :0.5B-72B 范围内,已有模型初始化效果优于随机初始化

模型初始化:小模型 to 大模型

  • 代表方法 :Bert2BERT、Llama Pro
  • 适用场景 :封闭域对话能力提升,训练数据充足
  • 效果预期 :仅能带来1-2个点的提升,不适合大规模预训练

模型初始化:Dense to Sparse(MoE模型)

  • 初始化挑战 :随机初始化易出现 loss spike,需 50k+step 恢复
    • 理解:“loss spike”(损失激增)是指模型的损失函数值在训练过程中出现突然且显著的上升现象
  • Sparse Upcycling 方案 :
    • 1)用同结构 Dense 模型通过 FFN 复制生成 MoE
    • 2)关键操作:
      • 裁剪前打乱 neuron 顺序打破专家对称性
      • 保留 50%-70% 原 FFN 参数,其余随机初始化
    • 3)优化技巧:全单精度训练可提升稳定性,但 GPU 利用率 < 50%

通用预训练数据准备

基础清洗
  • 清洗原则 :数据量充足时可严格筛选
  • 具体规则 :
    • 过滤 ppl 异常、格式混乱(多分行/短词)、长度过短数据
    • 排除 url、安全词、重复内容(防 LLM 复读)
    • 结合 fasttext 二分类模型筛除低质量数据
  • 辅助工具 :
    • 人工打标 + FastText 二分类模型筛选低质量数据
    • 参考 Llama3.1 和 Qwen2 技术报告的清洗流程
去重
  • 技术方案 :minhash 实现文章/段落粒度去重
  • 执行原则 :宁杀错不放过,重复数据严重影响模型能力(重复数据会导致模型 “记忆偏差”)
    • 理解:目前数据量已经很大了,可以多删除一些,对于数据量不大的场景,还是要小心错杀现象
分类与配比
  • 数据类型 :代码、数学、高教育性数据(如 chinese-fineweb-edu )、通用数据(体育/音乐/时政等)
  • 经验配比 :提高 education score、代码、数学数据比例,无需过度纠结具体数值(如 30% vs 40%)
  • 关键逻辑 :通用数据先夯实语言基础,再切入专业领域数据

长文本预训练数据被准

  • 数据要求 :几B-几十B 即可
  • 质量排序 :
    • 1)天然长文本(大学课本、GitHub 项目)【效果最好】
    • 2)相关文档拼接(如带 reference 的论文)【效果次之】
    • 3)无关文档拼接(仅训练位置编码)【这个方法效果不好,不建议使用】
  • 注意事项 :避免领域分布集中(避免数据 Bias),构造阅读理解类长文本效果有限

退火阶段数据准备

合成数据核心原则
  • 多样性优先 :

    • 方法1:调整解码参数(温度/top k/top p)
    • 方法2:Prompt 注入随机变量(如腾讯 Persona Hub 的 10亿 人物描述)
    • 示例:生成数学题时,在 Prompt 中加入不同人物背景描述
      1
      2
      3
      4
      5
      6
      7
      8
      9
      # 数学题生成示例
      prompt = """
      根据以下人物描述生成生活场景数学题:{persona}
      """
      persona_list = [
      "23岁卡车司机,身高178cm,单身,爱吃海鲜...",
      "北京化学家小A,本科就读于...",
      # 更多个性化描述
      ]
  • 质量保障 :

    • 模型规模:越大越好(专用模型 > 通用模型)
    • 质量评估:LLM-as-judge 打分、代码可执行性验证(如 OpenAI 工具)
数据类型与来源
  • 推理能力 :数学/代码数据(大量合成)
  • 知识储备 :educational data(学科试题/教科书)
  • 语言能力 :高质量网页数据(严格阈值筛选)

训练优化策略-蒸馏策略

  • 适用场景 :大模型初始化的小模型训练
  • 实施要点 :
    • teacher 模型与初始化大模型同系列
    • 温度策略:先高后低(前期广覆盖,后期精收敛)
  • 效率优势 :训练 token 数可减少至直接训练的 10% 以下

超参迁移优化-MuP参数化

  • MuP,即 Maximal Update Parameterization
  • 核心价值 :
    • 设计模型超参实现大小模型 LR/BS 通用
    • 小模型调参与大模型训练超参一致性强
  • 实践应用 :
    • 小模型(如 10B 数据)网格搜索调参,直接迁移至大模型
    • 搜参过程中关注 loss 下降速度而非绝对数值

学习率策略-WSD vs Cosine

  • Cosine 衰减(余弦衰减) :
    • 需预先设定总步数,收敛效果更稳定
  • WSD(Warmup-Stable-Decay)学习率调度 :
    • 分为预热阶段、稳定阶段和衰减阶段
    • 优势:灵活调整阶段,适合实验场景,无需提前设定总步数
      • 虽然平时也常将预热阶段设置为总步数的一定百分比,但也可以设定为固定值,且稳定阶段的步数一般没有特定要求
    • 劣势:同 token 数下效果不如 Cosine(需足够长 Decay 阶段 + 低最终 LR)

多阶段退火训练(提升效果的核心模块)

  • Step1:学科能力提升

    • 数据比例:教材 30%+合成选择题 50%+通用选择题 10%+通用数据 10%
    • 关键参数:LR = 3e-4,训练量 120B+ token(最佳 checkpoint 在几十 B 时达成)
    • 效果:MMLU/Ceval 超越 Qwen 官方模型
  • Step2:加入数学数据

    • 数据比例:学科 40% : 数学 60%
    • 关键发现:
      • LR = 8e-5 时,效果优于 1e-4
      • 数学数据超过 60% 会导致学科能力下降
    • 效果:Ceval比基线高6分,学科能力稳定
  • Step3:加入代码数据

    • 数据比例:学科 20% : 数学 40% : 代码 40%
    • 关键参数:LR = 5e-5,训练量几十B token
    • 代码和数学关联效应:数学与代码能力正相关,无需过高代码比例
  • Step4:综合能力提升

    • 数据比例:学科15% : 数学 25% : 代码 25% : 通用 10% : SFT 30%
    • 关键参数:LR = 3e-5 衰减至0 ,训练量几十B token
    • 训练目标:
      • 保持语言能力 + 引入 SFT 数据(如 Tool Use 的 function call)
    • 数据特性:加入 Tool Use 等 SFT 数据

关键原则

  • 数据不重复使用,预留充足退火数据
  • 小阶段训练顺序:学科 > 数学 > 代码 > 通用(按最佳学习率排序)
  • 单一数据训练时需优化内部配比(如代码数据中 Python 占比、GitHub 项目比例等)

Batch 内数据分配

  • 实时跟踪各类数据 token 数,严格按预定比例混合
  • 避免某类数据在 batch 中占比波动过大

评测数据集

  • 通用能力:opencompass(代码/数学/语言/知识)
  • 下游任务:业务相关数据集(如 Tool Use 评测集)

评测结果分析经验

  • loss 与效果关系 :loss 低不一定效果好,但 loss 高效果一定差
  • 通用与下游平衡 :两者指标并非总是正相关,需针对性优化

NLP——LLM模型评估指标

本文主要介绍LLM的各种评估指标


LLM 的评估指标

  • NLP常见的评估指标包括评估指标可能包括准确率、F1 分数、BLEU 分数、ROUGE 分数和 Perplexity等,其中BLUE 和 ROUGE是自然语言处理中用于评估文本生成质量的两种常用指标
  • 还有许多开源的综合评测体系,会借用以上评估指标来评估各自的得分
  • 比如:Big-bench 的文本生成等任务就需要使用 BLEU 分数和 ROUGE 分数等
  • AGI-Eval 社区 中包含大模型相关的各种评估指标,可以在这里面找到各种评估指标的介绍和使用方式等

BLEU(Bilingual Evaluation Understudy)

  • BLEU 用于评估机器翻译或文本生成结果与参考文本的相似度。它通过计算生成文本与参考文本之间的 n-gram 重叠来评估准确性,并结合简洁惩罚(Brevity Penalty)来惩罚过短的输出。BLEU 得分范围在 0 到 1 之间,1 表示与参考文本完全一致

ROUGE(Recall-Oriented Understudy for Gisting Evaluation)

  • ROUGE 主要用于评估自动摘要生成的质量。它通过计算生成文本与参考文本之间的 n-gram、词序列或词对的重叠来评估召回率(Recall)
  • 常见的 ROUGE 变体包括 ROUGE-N(n-gram重叠)和 ROUGE-L(最长公共子序列)
  • ROUGE 得分范围也在 0 到 1 之间,1 表示与参考文本完全匹配

BLUE 和 ROUGE 对比

  • BLEU :侧重准确率,常用于机器翻译
  • ROUGE :侧重召回率,常用于自动摘要
  • 两者均通过n-gram重叠评估生成文本的质量

pass@k 评估指标

  • 原始的 pass@k 指标 :为每个问题生成 \(k\) 个回复 ,只要其中任意一个回复解决了问题 ,则判定为当前问题被解决
    $$ \text{pass@}k = \frac{被解决的问题数}{总问题数}$$

    • 上面的方法方差较大,基本已经被弃用
  • 改进后的 pass@k 指标(原始指标的无偏估计) :在 Evaluating Large Language Models Trained on Code, OpenAI, 2021 中提出了一种无偏评估方法,对每个任务生成 \(n\) 个样本(其中 \(n \ge k\)),统计通过单元测试的正确样本数量 \(c\) 个(\(c \le n\)),最后计算无偏估计量 :
    $$\text{pass@}k := \underset{\text{Problems}}{\mathbb{E}} \left[ 1 - \frac{\dbinom{n - c}{k}}{\dbinom{n}{k}} \right]$$

    • 理解:分母是从 \(n\) 个中挑选 \(k\) 个的排列组合数;分子是从 \(n-c\) 个中挑选 \(k\) 的排列组合数,直观理解就是 在 \(k\) 次尝试中至少有一次成功的概率
      • 无偏估计量的证明:\(P(至少一次成功) = 1- P(全部失败) = 1-\frac{\dbinom{n - c}{k}}{\dbinom{n}{k}}\)
      • 当所有样本都正确时,分子为 0,当前问题样本的 \(\text{pass@}k = 1\);当所有样本都错误时,分子等于分母,当前问题样本的 \(\text{pass@}k = 0\)
      • 最终的 \(\text{pass@}k\) 是所有问题样本的 \(\text{pass@}k\) 的平均值
    • 这种评估指标更 Soft,对单个问题样本来说,不是绝对的取值为 \(\{0,1\}\),方差更小,更合适在样本数量较少的场景评估
    • 注意:即使 \(n=k\) 时,也不会退化为原始的 pass@k 指标
    • pass@k 评估指标的实现和证明,详情见 Evaluating Large Language Models Trained on Code, OpenAI, 2021
  • 改进后的 pass@K 指标代码实现:

    1
    2
    3
    4
    5
    6
    7
    8
    def pass_at_k(n, c, k):
    """
    :param n: total number of samples
    :param c: number of correct samples
    :param k: k in pass@$k$
    """
    if n - c < k: return 1.0
    return 1.0 - np.prod(1.0 - k / np.arange(n - c + 1, n + 1))
  • 独立采样还是 Beam Search?

    • 通常使用 核采样(Nucleus Sampling/Top-p)或 温度采样(Temperature Sampling) 进行独立随机采样
    • 不能使用 Beam Search,因为 Beam Search 倾向于生成高度相似的序列,缺乏多样性,无法体现 \(k\) 次尝试的覆盖能力
  • 样本是否可以重复?

    • 在生成阶段,因为是独立采样,完全可能生成两个一模一样的代码块
    • 在计算阶段,公式中的 \(n\) 是总生成数,\(c\) 是正确的数量
    • 重复的正确样本计入 \(c\),重复的错误样本计入 \(n-c\)
    • 计算过程看作是“无放回抽样”
    • 注:开源实现一般没有看到去重,实际上也可以去重?
  • 重点:为什么有的时候,pass@1 提升了,但是 pass@k 反而下降?

    • 直观上看,由于 pass@k 中的多个样本是完全独立采样的,所以直观上看,pass@1 提升时,pass@k 也一定会提升(因为 pass@k 理论上是一个独立的随机时间发生了 \(k\) 次,至少有一次成功的概率)
    • 实际上,由于 pass@k 还跟答案的多样性有关,举例如下:
      • 模型 A 的 pass@1 为 10%,即每次挑选样本,只有 10% 的概率正确,但答案多样性(范围)高(假设生成的答案都不同),那么模型 A 的 pass@10 大约为 67%,假设每次采样 100 个样本用来计算 pass@10,则:
        $$ pass@10 = 1 - \frac{\dbinom{100 - 10}{10}}{\dbinom{100}{10}} \approx 67%$$
      • 模型 B 的 pass@1 为 20%,即每次挑选样本,只有 20% 的概率正确,但答案多样性(范围)极低(假设对同一个样本,生成的答案都相同),那么模型 B 的 pass@100 大约为 20%(多个不同 样本,模型正确的概率只有 20%)
        • 不能再用上面的计算公式了,因为对于每个 Prompt 来说,没有多样性的模型是非此即彼,此时一个正确就全正确,一个错误就全错误,所以 pass@k = pass@1 = 20%
      • 结论:对于完全没有多样性的模型,pass@1 和 pass@k 其实是相同的;多样性越高,则 pass@1 和 pass@k 的差距越大

SBS(Side by Side)评估指标

  • 人工评测两个模型的好坏(新旧模型对比)
  • 人工打分为四种:好、坏、一样好、一样坏

MMLU

  • 链接:huggingface.co/datasets/cais/mmlu
  • MMLU(Massive Multitask Language Understanding)是一个用于评估大规模语言模型在多种任务和领域上理解和推理能力的综合性基准
  • 原始的数据集一共包含 15,908 道题目,涵盖 57 个不同的学科主题,每个主题至少 100 道题目(每个主题包含以 test 集为主,5道 dev 题目和部分 val 集在内的至少100道题目)
  • 它涵盖了广泛的知识领域和任务类型,旨在全面评估模型的多任务学习能力和泛化能力
  • 具体来说,包含以下能力:
  • 多任务评估 :
    • MMLU 包含 57个不同的任务,涵盖了人文学科、STEM(科学、技术、工程和数学)、社会科学和其他领域
    • 任务类型包括但不限于问答、文本分类、推理和阅读理解
  • 广泛的知识领域 :
    • 任务涉及的知识领域非常广泛,包括历史、地理、生物、化学、物理、数学、法律、经济、哲学等
    • 这种多样性使得MMLU能够全面评估模型在不同领域的知识掌握情况
  • 难度层次多样 :
    • MMLU中的任务难度层次多样,从基础的知识问答到复杂的推理和问题解决任务
    • 这种多样性有助于评估模型在不同难度任务上的表现
  • 零样本和少样本学习 :
    • MMLU评估模型在零样本(zero-shot)和少样本(few-shot)学习场景下的表现
    • 零样本学习是指模型在没有任务特定训练数据的情况下进行推理,而少样本学习是指模型仅使用少量示例进行推理

MMLU 的评估方法:

  • 任务类型 :每个任务通常是一个多项选择题或填空题
  • 评估指标 :主要使用准确率(accuracy)作为评估指标,即模型在所有任务上的平均正确率
  • 数据集 :MMLU数据集包含大量的问题和答案,涵盖了各种学科和难度级别

MMLU-Redux

  • 链接:huggingface.co/datasets/edinburgh-dawg/mmlu-redux
  • MMLU-Redux 是 MMLU 的人工精校子集,由 14 位专家对 30 个学科各 100 题共 3,000 道四选一选择题进行重新标注,公开于 Hugging Face(edinburgh-dawg/mmlu-redux),遵循 CC-BY-4.0 许可
  • 针对 MMLU 存在标注错误、问题 / 选项不清晰等问题,推出高质量、低噪音版本,用于更可靠的模型评估
  • 30 个学科 × 100 题 / 学科 = 3,000 题;
  • 每题含 question、choices、answer(0–3)、error_type、source、correct_answer、potential_reason 等字段
  • 采用层级化错误分类体系,先识别再校正;研究显示在 Redux 上模型排名与原 MMLU 存在显著差异

MMMU

  • 链接:huggingface.co/datasets/MMMU/MMMU
  • MMMU (MultiModal Massive Multi-task Understanding) 是针对多模态场景设计的大规模、多任务、多学科基准,在 2023年 提出
  • MMMU 涵盖10大学科,包括生物学、化学、地理等,共1838道问题
    • 每道题目都包含图片和文本信息,需要视觉+语言联合理解
  • MMMU 评估方式 为问题形式为图片与文字结合,多项选择或开放式问答
  • MMLU 的主要评估指标 是 准确率(Accuracy) ,即正确回答问题的比例,尤其关注模型处理跨模态输入后的综合能力

MMMU_Pro

  • 链接:huggingface.co/datasets/MMMU/MMMU_Pro
  • MMMU_Pro (A More Robust Multi-discipline Multimodal Understanding Benchmark)

MMMLU

  • 链接:huggingface.co/datasets/openai/MMMLU
  • MMMLU(Multilingual Massive Multitask Language Understanding) 是 OpenAI 发布的一个多语言版本的 MMLU 评测基准 ,用于衡量大模型在多种语言上的知识和推理能力
  • MMMLU 是 OpenAI 基于原始 MMLU 数据集开发的(多语言扩展版)
  • MMMLU 覆盖了57个学科,与英文 MMLU 一致,但题目被翻译成了 26种 不同的语言,包括中文、法语、西班牙语、阿拉伯语等主流及部分低资源语言
  • MMMLU 用于评估大型语言模型在跨越不同文化和地区时,能否保持广泛且准确的知识理解与推理能力
    • 每道题为四选一选择题,涉及科学、人文、社会科学等多个领域
    • 各学科问题均以目标测试语言呈现,无需英语中转
  • 通常采用 zero-shot 或 few-shot 设置,即不给或只给少量示例,让模型直接用目标外语作答
  • 核心指标为准确率 (Accuracy)

Big-Bench

  • 链接:待补充
  • Big-bench(Beyond the Imitation Game Benchmark)是一个大规模、多样化的基准测试,旨在评估语言模型在各种复杂任务上的表现
  • Big-bench 由社区驱动,包含大量任务,涵盖了广泛的知识领域和技能类型,旨在推动语言模型的能力边界,具体来说,包含以下能力:
  • Big-bench 包含超过200个任务,涵盖了语言理解、推理、知识问答、翻译、数学计算、编程、常识推理等多个领域
    • 任务类型包括但不限于问答、文本生成、分类、排序、逻辑推理等
  • 根据任务类型不同,评估指标可能包括准确率、F1 分数、BLEU 分数、ROUGE 分数等
  • 任务难度从简单到复杂不等,既有基础的语言理解任务,也有需要复杂推理和问题解决能力的任务
    • 这种多样性有助于评估模型在不同难度任务上的表现
  • Big-bench 是由研究社区共同贡献和维护的,任务由来自不同领域的研究者设计和提交
    • 这种社区驱动的模式使得Big-bench能够不断扩展和更新,保持其前沿性和多样性
  • Big-bench 旨在评估模型在未见过的任务上的泛化能力,而不仅仅是在特定任务上的表现
    • 通过大量多样化任务的评估,Big-bench 能够全面反映模型的综合能力

BBH

  • HuggingFace 地址:huggingface.co/datasets/lighteval/bbh
  • BBH 评估集(Big-Bench Hard)是 BIG-Bench(Beyond the Imitation Game Benchmark) 中最具挑战性的子集,由 Google 研究团队于 2022 年提出,专门用于评估大语言模型在高阶推理任务上的表现
  • BBH 从 BIG-Bench 的 204 项任务中,筛选出 23 项当前语言模型表现显著低于人类水平的任务
  • BBH 涵盖复杂推理、因果判断、反事实理解、多步规划等高阶认知能力,包含多个任务的共 21 个子集,下面列举几个任务:
    • 因果判断(causal judgment)
    • 布尔逻辑推理(boolean expressions)
    • 多步数学推理(Multi-step mathematical reasoning)
    • 反事实推理(Counterfactual reasoning)
  • BBH 支持多种提示方式,包括标准提示(answer-only)和 思维链提示(Chain-of-Thought, CoT) ,后者能显著提升模型表现
  • BBH 可通过 GitHub 官方仓库获取数据集和运行脚本,支持自定义模型评估

HELM(评估框架)

  • GitHub 开源框架地址:github.com/stanford-crfm/helm/
  • 论文地址:Holistic Evaluation of Language Models, Stanford, 202308
  • 斯坦福地址:crfm.stanford.edu/helm/
  • HELM(Holistic Evaluation of Language Models,语言模型整体评估)是一个由斯坦福大学开发的综合性评估框架,旨在全面评估大语言模型(LLMs)在多个维度和任务上的表现
  • HELM 的评估指标涵盖了广泛的能力和场景
  • HELM 从多个维度对语言模型进行评估,包括但不限于:
    • 准确性 :模型在各类任务中的表现,如问答、信息检索、摘要生成等
    • 稳健性 :模型对输入变化的鲁棒性,例如拼写错误、同义词替换等
    • 公平性 :模型输出是否对不同群体存在偏见或歧视
    • 效率 :模型的推理速度和资源消耗
    • 有毒性 :模型生成内容是否包含有害或不当信息
    • 不确定性 :模型输出的置信度是否合理
    • 环境影响 :模型训练和运行的能耗及碳排放
  • HELM 设计了16个核心场景 ,覆盖了语言模型的主要应用领域,例如:
    • 问答任务 :如 Natural Questions、MMLU 等
    • 信息检索 :如微软的 Macro 数据集
    • 摘要生成 :如 CNN/Daily Mail 和 XSum 数据集
    • 情感分析 :如 IMDB 电影评论数据集
    • 毒性检测 :如 CivilComments 数据集
  • HELM采用“自上而下”的方法,由专家团队精心选择和设计评估任务,确保评估的高质量和针对性。其评估方法包括:
    • 自动化评估 :使用标准指标(如准确率、F1分数、ROUGE等)对模型进行量化评估
    • 人类评估 :通过众包平台进行A/B测试,评估模型生成内容的有用性和无害性
    • 红队分析 :邀请领域专家对模型进行对抗性测试,识别潜在风险和漏洞

Arena

  • Arena(竞技场/角斗场)大模型测评方法是一种凉凉对战评测的方法,用户可以并排与两个匿名模型聊天,并通过投票选出哪一个更好。最后,根据PK结果使用ELO评分系统对模型进行评分和排名‌
  • 除了大模型以外,多模态模型也可以采用这个方式,其中典型的平台包括典型平台包括:
    • Chatbot Arena :由LMSYS Org推出,支持190多种大语言模型的匿名对战和排名,详情见:https://lmarena.ai/
    • Compass Multi-Modal Arena :支持多模态模型的匿名对战,涵盖图像理解和生成任务,详情见:https://rank.opencompass.org.cn/home
    • Decentralized Arena:基于群体智能的多维度评估平台,支持自动化和可定制化评测

Arena-Hard

  • 相关链接:(ArenaHard)From Live Data to High-Quality Benchmarks: The Arena-Hard Pipeline, 202404
  • 更多细节待补充

Arena-Hard v2

  • Arena Hard v2 (ArenaHardV2, Arena_Hard_v2) 数据来源于 github.com/lmarena/arena-hard-auto
  • Arena-Hard v2(Arena-Hard Benchmark version 2),来自 LMSys (Large Model Systems Organization)
  • Arena-Hard v2 通常不作为数据集发布,而是用于 LMSys Chatbot Arena 上的模型对战,其问题集为私有
    • LMSys Chatbot Arena 是一个用于 LLM 对战的排行榜和评估基准
  • Arena-Hard v2 是一些挑战性问题的集合,旨在通过更难、更具迷惑性的问题来区分顶级模型之间的性能差异,这些问题通常需要复杂推理、多步逻辑或对细节的精确理解
  • Arena-Hard v2 使用 GPT-4.1 和 Gemini-2.5 打分来评估模型效果

MATH-500(数学)

  • huggingface.co/datasets/HuggingFaceH4/MATH-500包含500道题目(都在 test split 里面)
  • 每道题目包含 problem,solution,answer,subject,level,unique_id 列
    • subject 列: 指出题目类型,比如代数(Algebra)、数论(Number Theory)等
    • level 列:指出题目等级,level 越大,难度越大

AMC(数学)

  • 包含 AMC 8,AMC 10,AMC 12 三个赛事,题目难度逐渐提升
  • AMC 和 AIME 是有一定关系的:
  • AMC 的题目会被包含在其他数学类测试集中

AIME(数学)

  • AIME 是美国数学邀请赛(American Invitational Mathematics Examination)的简称‌,是美国数学竞赛(AMC)系列中的高级别赛事,旨在选拔在 AMC10/12 中表现优异的学生,为美国数学奥林匹克竞赛(USAMO/USAJMO)和国际数学奥林匹克(IMO)选拔参赛者
  • AIME 每年都会有新的题目出现,对 LLM 来说属于比较难的题目
  • 比如 2024 和 2025 数据集如下:
    • HuggingFaceH4/aime_2024:共 30 题,包含 id,problem,solution,answer,url,year 列
    • MathArena/aime_2025:共 30 题,包含 problem_idx,problem,answer,problem_type 列
      • 题目原始列表:artofproblemsolving.com/wiki/index.php/2025_AIME_II_Problems
    • opencompass/AIME2025:共 30 题(两个子集 AIME2025-I 15 题, AIME2025-II 15 题, 都在 test split里面),包含 question,answer 列

OlympiadBench

  • HuggingFace 链接:huggingface.co/datasets/Hothan/OlympiadBench
  • 论文链接:OlympiadBench: A Challenging Benchmark for Promoting AGI with Olympiad-Level Bilingual Multimodal Scientific Problems, arXiv 20240606, THU
  • GitHub 链接:github.com/OpenBMB/OlympiadBench
  • 以奥林匹克级别的中英双语 ,且多模态科学问题推动通用人工智能发展的挑战性基准[ACL 2024]
  • 数据集中还包含图片等信息,且有很多个子集,数据量巨大(从 56 行到 1.9K 行不等)
  • 每一行包含多个列,包括了图片,答案和语言等

GPQA

  • HuggingFace 链接:huggingface.co/datasets/idavidrein/gpqa
  • GitHub 链接: github.com/idavidrein/gpqa
  • 论文链接: GPQA: A Graduate-Level Google-Proof Q&A Benchmark, arXiv 20231220
  • GPQA(Graduate-Level Google-Proof Q&A),一个极具挑战性的数据集,包含多个子集,每个子集几十到几百道由生物、物理和化学领域专家编写的多项选择题
  • GPQA 数据集每行包含很多列,信息非常详细,比如正确答案 1 个,错误答案 3 个等,常常用作 4 选 1 的选择题形式
  • GPQA 任务类型属于知识 + 复杂推理(MCQ)
  • GPQA 评估指标常用 Accuracy
  • GPQA 常用设置 0-shot / few-shot / Chain-of-Thought

GPQA-D

  • GPQA-D(GPQA Diamond)全称 Graduate-Level Google-Proof Q&A Diamond,是 GPQA(Graduate-Level Google-Proof Q&A)基准的“超高难度”子集,专门用来测试大模型在 博士级科学问题 上的深度推理与专业知识运用能力
  • HuggingFace 链接(与 GPQA 相同):huggingface.co/datasets/idavidrein/gpqa
    • GPQA-D 在上述链接的子集里
  • GPQA-D 包含 198 道四选一选择题,涵盖 生物学、物理学、化学 等专业

ARC 数据集

  • ARC 数据集,即 AI2 Reasoning Challenge(AI2 推理挑战)数据集
  • 整个 ARC 数据集包含 7787 道问答题目,分为 ARC Challenge Set(挑战集)和 ARC Easy Set(简单集)
  • ARC 旨在评估大模型对科学问题的理解和推理能力
  • ARC Challenge Set 包含 2590 道较难的问题
  • 部分论文中也会使用 ARC Benchmark 或 ARC Challenge Benchmark 来表示以数据集为基准
  • 注:还有一个重名的数据集 Abstraction and Reasoning Corpus(ARC)
    • 这是由 François Chollet 于 2019 年发布的基准测试集,旨在评估 AI 系统在面对全新任务时的抽象推理和泛化能力

ARC-AGI

  • 通用推理评估数据集
  • 参考链接:Abstraction and Reasoning Corpus for Artificial General Intelligence (ARC-AGI), 2019

RULER

  • GitHub 地址:github.com/NVIDIA/RULER
  • RULER 测试集是 NVIDIA 在 2024年6月 发布的一个用于评估语言模型长上下文建模能力的基准测试数据集
  • RULER 的任务类别包括:
    • 检索 :扩展了大海捞针(Needle in a Haystack,NIAH)测试,包括单针检索(Single NIAH,S-NIAH)、多针检索(Multi-keys NIAH,MK-NIAH)、多值 NIAH(Multi-values NIAH,MV-NIAH)和多查询 NIAH(Multi-queries,MQ-NIAH)四个任务,用于评估模型在不同类型和数量“针”情况下的检索能力,要求检索能力与“针”和“大海”的类型无关,能忽略硬干扰,且在检索多个项目时具有高召回率
    • 多跳跟踪 :提出变量跟踪(Variable Tracing,VT)任务,模拟最小的指代链解析任务,检查模型跟踪相关共现模式和在长输入中绘制跳过连接的行为,通过增加跳数或链的数量来增加任务复杂性
    • 聚合 :引入常见词提取(Common Word Extraction,CWE)和频繁词提取(Frequent Word Extraction,FWE)任务,作为摘要任务的代理,用于测试模型聚合跨越长距离上下文相关信息的能力
    • 问答 :在现有短上下文问答数据集的输入中添加干扰信息,以评估模型在各种上下文大小下的问答能力
  • 注:RULER 测试集 主要用于评估长上下文语言模型

Longeval (Long-context Evaluation)

  • Longeval 用于评估大型语言模型在处理和利用长上下文信息时的表现
  • 任务类型包括:
    • 长对话理解: 模型需要理解并回应一个非常长的、包含多个轮次的对话历史
    • 长文档问答: 模型需要基于一篇长文档(如论文、报告)来回答具体问题
  • Longeval 的数据长度通常在几千到几万个 token 之间,模拟了用户与AI助手进行长时间、多主题深入交流的场景
    • 更侧重于测试模型在多轮对话中维持上下文一致性和记忆关键信息的能力
  • 评估方式:通常使用自动化的脚本,通过比较模型生成答案与标准答案的精确度、召回率或 F1 分数来进行评估
  • 强调了在“对话”和“单文档问答”这两个实用场景下对模型长文本能力进行压力测试的重要性

LongBench

  • 论文地址: LongBench: A Bilingual, Multitask Benchmark for Long Context Understanding, arXiv 2023, THU & Zhipu.AI & IACAS
  • GitHub 地址: github.com/THUDM/LongBench
  • Hugging Face 地址:huggingface.co/datasets/THUDM/LongBench
  • LongBench 是一个非常全面且被广泛使用的多任务、双语(中英)长文本评测基准。它由清华大学的研究团队发布,旨在提供一个标准化的平台来衡量模型在各种长文本任务上的表现。LongBench 的最大特点是其任务全面性和数据多样性 ,覆盖了从单文档理解到多文档推理的广泛场景
  • LongBench 主要任务类别有:
    • 单文档问答 (Single-document QA)
    • 多文档问答 (Multi-document QA)
    • 摘要 (Summarization)
    • 少样本任务 (Few-shot Learning)
    • 代码补全 (Code Completion)
    • 合成任务 (Synthetic Tasks)

LongBench-V2

  • 论文地址:LongBench v2: Towards Deeper Understanding and Reasoning on Realistic Long-context Multitasks
  • GitHub 地址(与 LongBench 想通):github.com/THUDM/LongBench
  • Hugging Face 地址:huggingface.co/datasets/zai-org/LongBench-v2
  • LongBench V2 是 LongBench 升级版,这是一个旨在评估 LLMs 处理长语境问题能力的基准测试
  • LongBench V2 包含503道具有挑战性的多项选择题,题干文本长度跨度从 8K 到 2M Words 不等,涵盖六大主要任务类别,具体如下:
    • 单文档问答(single-document QA)
    • 多文档问答(multi-document QA)
    • 长语境学习(long in-context learning)
    • 长对话历史理解(long-dialogue history understanding)
    • 代码仓库理解(code repository understanding)
    • 长结构化数据理解(long structured data understanding)
  • 为确保测试的广度与实用性,研究团队从近 100 位拥有不同专业背景的高学历人士处收集数据
    • 同时,通过自动化与人工审核相结合的流程,保障测试数据的高质量与高难度
    • 难度体现:在15分钟的时间限制下,人类专家的答题准确率仅为 53.7%
  • o1-preview 模型准确率 57.7%,比人类基准准确率高出 4 个百分点

InfiniteBench

  • GitHub地址:github.com/OpenBMB/InfiniteBench
  • InfiniteBench 专注于评估模型在 “无限”或“极长” 上下文长度下的表现
    • 任务通常具有非常长的输入文本(可达 100K tokens 甚至更长),并且答案(或关键信息)往往被刻意放置在输入的非常深的位置(例如中间部分),以此来挑战模型在超长序列中“大海捞针”的能力
    • 特别关注模型是否真的利用了全部上下文,而不是仅依赖开头或结尾的信息
  • InfiniteBench 主要特点:
    • 极长的上下文: 输入文本长度远超常规评测
    • “大海捞针”式任务: 关键信息被隐藏在长文本的任意位置
    • 抗捷径设计: 任务设计旨在防止模型通过只看开头/结尾等“作弊”方式获得高分
    • 覆盖英语和中文任务

HelloBench

  • 论文地址:HelloBench: Evaluating Long Text Generation Capabilities of Large Language Models
  • HelloBench,是层级式长文本生成基准测试(Hierarchical Long Text Generation Benchmark)
  • HelloBench 将长文本生成任务划分为五个子任务,分别是:
    • 开放式问答(open-ended QA)
    • 文本摘要(summarization)
    • 对话生成(chat)
    • 文本补全(text completion)
    • 启发式文本生成(heuristic text generation)

MorehopQA

  • 论文地址:MoreHopQA: More Than Multi-hop Reasoning
  • GitHub:github.com/Alab-NII/morehopqa
  • MoreHopQA 是多跳数据集
  • 背景:在 MoreHopQA 前,大多数已有的多跳数据集均为抽取式答案数据集(此类数据集的问题答案可直接从给定语境中提取得到)
    • 这一特点往往导致模型采用启发式方法或“捷径”解题,而非执行真正的多跳推理
  • MoreHopQA 数据集将答案形式从“抽取式”转向“生成式”
  • MoreHopQA 的构建以三个现有多跳数据集为基础,分别是 HotpotQA、2WikiMultihopQA 和 MuSiQue
    • 在构建过程中,不再单纯依赖事实推理,而是通过增加一层“拓展问题”来升级原有多跳问题
    • 这些拓展问题需涉及以下一种、两种或全部三种推理类型:
      • 常识推理(commonsense)
      • 算术推理(arithmetic)
      • 符号推理(symbolic)
  • MoreHopQA 数据集通过“半自动化流程”构建而成,最终包含 1118 个样本,且所有样本均经过人工验证
  • 仅有部分模型能实现“完美推理”(即所有相关子问题均回答正确),其中 GPT-4 的完美推理率为 38.7%,Llama3-70B 的完美推理率为 33.4%

HLE(推理)

  • HLE 是“Humanity’s Last Exam”的缩写,即“人类最后一次考试”,由 Center for AI Safety(AI安全中心)与Scale AI联合打造
  • HLE 是一个多模态基准测试,旨在成为封闭式学术基准测试的最终版本,用于衡量大语言模型在人类极限能力边界上的通用推理与智能水平
  • HLE 包含3000个问题,涉及上百门学科,包括数学、人文科学和自然科学等
  • HLE 包含精确匹配题和选择题两种
    • 其中 80% 的问题为精确匹配题,模型需要输出一个完全匹配的字符串作为答案;
    • 其余为选择题,模型需要从五个或更多选项中选择一个正确答案
    • 此外,10% 的问题要求理解文本和图像参考
  • HLE 每个问题都有一个已知的明确且易于验证的解决方案,但无法通过快速互联网检索获得答案,通常需要研究生水平的专业知识或高度特定主题的测试知识
  • HLE由来自 50 个国家的 500 多个机构的近 1000 名学科专家贡献,经过问题筛选、迭代优化、手动审核等流程创建,同时还保留了一部分私有测试集,用于评估模型是否存在过拟合现象
  • 如果模型在 HLE 中获得高分,将表明模型在封闭式、可验证的问题以及前沿科学知识方面具备专家级表现,有助于推动模型在复杂问题上的表现提升,为研究者提供了一个标准化工具,用于比较不同模型在跨学科任务中的表现

IFEval

  • IFEval(Instruction Following Evaluation)是用于评估大模型指令遵循的指标
  • 链接:huggingface.co/datasets/google/IFEval
  • IFEval 是一个专门用于评估模型遵循指令能力的基准
    • 它不侧重于解决具体问题,而是看模型能否严格按照指令中的各种约束来生成回应
    • 约束复杂多样,例如“以 X 开头”、“用 Y 结尾”、“提到某个词 Z 次”、“段落数量不超过 N 个”等等
  • IFEval 评估集包含大约 400 条带有明确约束的提示(Prompt),涵盖了 25 种不同类型的约束
  • 评估时,通过自动检查生成的内容是否满足所有指令约束来计算模型的准确率
  • 在 IFEval 基准测试中,按照要求严格程度,分为 “strict prompt”(严格提示)和 “non-strict prompt”(非严格提示)两种评估策略(两者主要体现在对模型输出的评估标准上,而不是提示本身的内容)
    • Strict Prompt (严格提示) :采用非常严格的评估标准。模型的输出必须精确匹配所有预定义的要求才算正确
      • 即使内容基本正确,但只要在格式、措辞、顺序或某个细节上与预期有丝毫偏差,就会被判定为错误
      • 这通常用于评估模型执行高度结构化或精确指令的能力
      • 目的是测试模型的精确性和可靠性
    • Non-strict Prompt (非严格提示) :采用更宽松的评估标准
      • 评估者会判断模型的输出是否在语义上满足了指令的核心要求 ,允许一定的表达差异或格式灵活性
      • 只要关键信息正确且意图达成,即使不完全一致,也可能被视为正确
      • 目的是测试模型的理解和意图实现能力

GuideBench: Benchmarking Domain-Oriented Guideline Following for LLM Agents

  • 论文地址:GuideBench: Benchmarking Domain-Oriented Guideline Following for LLM Agents, ACL 2025, SJTU, BateDance
  • GitHub 地址:github.com/Dlxxx/GuideBench
  • 背景:LLM 正越来越多地被用作领域导向型智能体,这类智能体的运行依赖于特定领域的指南(domain-oriented guidelines),而这些 guidelines 可能与模型自身的常识知识存在冲突
    • 这类领域 guidelines 具有两个关键特征:一是包含大量领域专属规则,二是会频繁更新
  • GuideBench(指南基准)是一个专为评估大语言模型指南遵循性能而设计的综合基准
  • GuideBench 从三个关键维度对大语言模型进行评估:
    • (1)对多样规则的遵循程度
    • (2)对规则更新的鲁棒性
    • (3)与人类偏好的对齐度

HMMT

  • HMMT(Harvard-MIT Mathematics Tournament)是 “哈佛-麻省理工大学数学竞赛”,是全美国影响力最大和名校理工科专业认可程度最高的高中数学竞赛之一
  • HMMT 作为面向全球顶尖高中生的最高水平数学竞赛之一,其题目难度高,覆盖代数、几何、组合数学、微积分等多个领域,需要极强的逻辑推理和创造性解题能力
  • HMMT 竞赛题通常在多个数学数据集中被整合,目前暂时没有一个独立的 HuggingFace 官方仓库
  • 在大模型评估集中,HMMT 代表了使用哈佛-麻省理工数学锦标赛(HMMT)的题目作为评估数据集
    • 将其作为评估指标,旨在衡量模型在专业级数学推理方面的极限
    • 数据量通常是历年竞赛的几百到几千道题目
    • 比如:HMMT 25 表示 25 年的 HMMT 比赛试题集合

IMO-ProofBench

  • IMO(International Mathematical Olympiad):国际数学奥林匹克(全球最高级别数学竞赛)
  • IMO-ProofBench 是一个证明数据集

附录:一些数学比赛集合

  • AIME(American Invitational Mathematics Examination):美国数学邀请赛(高中阶段)
  • HMMT(Harvard-MIT Mathematics Tournament):哈佛-麻省理工数学竞赛(全球顶尖高中竞赛)
  • IMO(International Mathematical Olympiad):国际数学奥林匹克(全球最高级别数学竞赛)
  • CMO(Chinese Mathematical Olympiad):中国数学奥林匹克(中国国家队选拔赛事)
  • Putnam(William Lowell Putnam Mathematical Competition):普特南数学竞赛(北美顶尖大学竞赛)

BeyondAIME

  • 链接:huggingface.co/datasets/ByteDance-Seed/BeyondAIME
  • 精心挑选的数学推理数据集,来自字节-Seed

LiveCodeBench (24/8∼25/5)

  • LiveCodeBench,简称(LCB),通常还会加上时间周期,比如用表达如 LiveCodeBench (24/8∼25/5) 表示 24年8月 至 25年5月 期间的题目
  • LiveCodeBench 是一个动态的、持续更新的编程能力评估基准
  • LiveCodeBench 每周都会从 LeetCode、AtCoder、CodeForces 等在线编程竞赛平台收集最新的、真实的人类竞赛题目
  • LiveCodeBench 可以有效防止模型在训练数据中“见过”评测题目,从而更真实地反映模型的泛化编程能力

OIBench

  • 论文链接:OIBench: Benchmarking Strong Reasoning Models with Olympiad in Informatics, arXiv 20250612, AGI-Eval && Meituan && BNU && SJTU
  • 链接:huggingface.co/datasets/AGI-Eval/OIBench
  • OIBench 是美团 Meituan-M17 团队联合上海交大等发布的信息学奥赛(IOI)级算法评测基准,含 212 道高难度原创题,侧重区分模型的推理与链式思考能力,已在 GitHub 与 Hugging Face 开源
  • 原始论文:OIBench: Benchmarking Strong Reasoning Models with Olympiad in Informatics, arXiv 20250612, AGI-Eval && Meituan && BNU && SJTU
    • 注:AGI-Eval 是一个由 上海交通大学、同济大学、华东师范大学 及 DataWhale 等高校和机构联合创建的‌大模型评测社区‌,专注于评估基础模型在人类认知与问题解决任务中的通用能力
  • OIBench 定位为高区分度的算法编程评测基准,聚焦真实、可复现的模型能力评测
  • OIBench 包含 212 题(250 候选),题目由高校教练与 ACM-ICPC 团队编制,难度为 IOI 级别,多为“至多仅一个标杆模型能解”的强筛选
  • 测试用例覆盖大数据量与边界,配可验证的 C++ 标准解(C++ 标准解作为金标准,确保公平与可复现)
  • 支持 C++/Python/Java/JavaScript
  • 评测范式为 Zero-shot;提供“伪代码提示”以测思路理解与复现
  • 在 GitHub、Hugging Face 开源(题目私有、未公开,降低训练数据同源污染风险),并托管于 AGI-Eval 社区
    • AGI-Eval 社区会更新排名 agi-eval.cn/evaluation/detail?id=60
  • 问题:虽然说是私有化,确实在 HuggingFace Data Studio 上看不到示例,但是 HuggingFace 上有相关的数据文件怎么理解?huggingface.co/datasets/AGI-Eval/OIBench/tree/main/data

OIBench 的区分度足够好

  • 对 18 个主流模型的 zero-shot 评测显示:
    • 推理型模型平均约 21.4%,显著高于普通指令微调模型的约 3.6%;
    • o4-mini-high 以 36.35 分领先,说明能拉开真实差距
  • 闭源模型平均 14.5%,开源 6.3%;语言偏好上,JavaScript/Python 平均低于 C++/Java 约 10%,中英文差异很小
  • 伪代码提示可显著提升所有模型表现,强推理模型提升更明显;o4-mini-high 以较少 Token 解出更多题,推理效率最佳

OJBench

  • 论文:OJBench: A Competition Level Code Benchmark For Large Language Models, arXiv 20250619, THU & Moonshot AI
  • GitHub 链接:github.com/He-Ren/OJBench
  • OJBench comprises 232 programming competition problems from NOI and ICPC, providing a more rigorous test of models’ reasoning skills

ZebraLogic

  • HuggingFace 链接(Data): huggingface.co/datasets/allenai/ZebraLogicBench
  • GitHub(Code for evaluation): github.com/yuchenlin/ZeroEval
  • Leaderboard: https://hf.co/spaces/allenai/ZebraLogic
    • 注:这里的 hf.co 会重定向到 huggingface.co
  • ZebraLogic 用于评估逻辑推理能力
  • 每个用例都是一个 Logic Grid Puzzle(Zebra Puzzle)

Sudoku-Bench

  • HuggingFace 链接:huggingface.co/datasets/SakanaAI/Sudoku-Bench
  • 属于推理数据集
  • Sudoku-Bench 是一个用于评估模型解决数独谜题能力的基准
    • 注:数独是一个经典的逻辑和约束满足问题,它需要模型理解规则(每行、每列、每宫数字1-9不重复)并根据给定的数字进行推理

Aider-Polyglot

  • 官方链接:epoch.ai/benchmarks/aider-polyglot
  • Aider-Polyglot Code Editing Benchmark,是一个代码编辑基准,对应 AI 编程助手项目 Aider 上的题目
    • Polyglot 含义是“多语言的”,意味着支持多语言(C++, Go, Java, JavaScript, Python, and Rust)

SWE-bench

  • HuggingFace 链接:huggingface.co/datasets/princeton-nlp/SWE-bench
  • 考察解决 GitHub 上 issue 的能力
  • 收集了来自 12 个 GitHub Python 项目((如 Django, scikit-learn))上的 2,294 个 Issue-Pull Request 对,使用 post-PR 作为参考解决方案
    • 每个任务都是一个实际发生过的问题,模型需要像一个软件工程师一样,理解问题描述、定位和修改代码库中的多个文件来修复 bug 或添加功能
    • 评估是通过在真实环境中运行测试用例来验证模型提交的补丁(patch)是否成功解决了问题

SWE-bench_Verified

  • HuggingFace 链接:huggingface.co/datasets/princeton-nlp/SWE-bench_Verified
  • SWE-bench_Verified 包含 500 条用例,是 SWE-bench 测试集的一个子集,是经过人工质量验证过集合

BigCodeBench

  • 原始论文:BigCodeBench: Benchmarking Code Generation with Diverse Function Calls and Complex Instructions, arXiv 2024 & ICLR 2025,
  • HuggingFace 链接:huggingface.co/datasets/bigcode/bigcodebench
  • BigCodeBench 基准测试要求 LLMs 调用来自 139 个库和 7 个领域的多个函数调用作为工具,来解决 1,140 个细粒度任务
    • 每个任务包含 5.6 个测试用例,平均分支覆盖率高达 99%
  • BigCodeBench 包含两个变体
    • BigCodeBench-Complete: Code Completion based on the structured docstrings.
    • BigCodeBench-Instruct: Code Generation based on the NL-oriented instructions.
      • BigCodeBench-Instruct 能自动将原始文档字符串(docstrings)转换为仅包含关键信息的简短指令

BFCL v3

  • 博客地址:BFCL V3 • Multi-Turn & Multi-Step Function Calling Evaluation
  • BFCL v3(Berkeley Function Calling Leaderboard v3),用于验证大模型的 FC 能力(function-calling capabilities)
  • 子集 BFCL v3 multi turn: 这个子集专注于多轮对话场景下的函数调用
    • 模型不仅要响应当前指令,还可能需要利用前几轮对话的上下文信息来做出正确的函数调用决策
  • 子集 BFCL v3 full: 指的是在整个 v3 数据集上进行的全面评估,涵盖单轮、多轮、并行、多函数等各种复杂的函数调用场景

AST(Abstract Syntax Tree) 准确率指标

  • 通过构建抽象语法树(Abstract Syntax Tree)来计算指标,最早由 伯克利(BFCL 的作者团队)提出
  • 详情可参考:Gorilla: Large Language Model Connected with Massive APIs

\(\tau\)-bench & \(\tau^2\)-bench (Retail, Airline, Telecom)

  • 关键词:tau-bench, Tau-Bench, \(\tau\)-bench
  • 论文链接: \(\tau\)-bench: A Benchmark for Tool-Agent-User Interaction in Real-World Domains, arXiv 20240617
  • \(\tau\)-bench(Tool-Agent-User Interaction Benchmark)用于评估 Agent 的交互能力,包含不同的场景
  • \(\tau\)-bench 测试模型在复杂的多步骤任务中,如何通过调用一系列 API 或工具来完成目标
  • \(\tau^2\)-bench 是该系列的第二个版本,可能在任务复杂性、工具数量或评估维度上有所增强
  • \(\tau\)-bench 和 \(\tau^2\)-bench 包含的任务被设计为贴近真实世界的应用场景,下面是一些子集介绍
    • \(\tau\)-bench-Retail : 专注于零售场景的任务,例如管理库存、查询订单、处理退货等
      • 注:有时候可以用 \(\tau\)-bench-Retail(P1)代表评测的第一个阶段(Phase 1)?
    • \(\tau\)-bench-Airline : 专注于航空服务场景,例如查询航班、预订机票、管理行程等
    • \(\tau^2\)-bench-Retail :
    • \(\tau^2\)-bench-Airline :
    • \(\tau^2\)-bench-Telecom :\(\tau^2\)-bench 新增的电信场景

MiniF2F

  • HuggingFace 链接:huggingface.co/datasets/Tonic/MiniF2F
  • 论文链接:MiniF2F: a cross-system benchmark for formal Olympiad-level mathematics, arXiv 20220228, OpenAI
  • MiniF2F 数据集是一个由奥林匹克数学竞赛难度级别的正式数学命题组成的资源库,旨在为神经定理证明(neural theorem proving)领域提供跨系统的统一基准测试框架。该基准目前覆盖 Metamath、Lean、Isabelle(部分)和 HOL Light(部分)四种证明系统,包含从 AIME(美国数学邀请赛)、AMC(美国数学竞赛)、国际数学奥林匹克竞赛(IMO)以及高中与本科数学课程材料中精选的 488 道数学命题

NuminaMath-1.5

  • NuminaMath-1.5 数据集包含 89.6 万个数学问题,涵盖常用数据源和高等数学主题

AoPS Forum

  • AoPS 论坛(Art of Problem Solving 社区)是面向中学生与数学爱好者的在线数学交流平台,围绕竞赛与进阶数学问题讨论、分享解法与课程互动,广泛用于准备 AMC、AIME、USAMO 等赛事
  • AoPS 论坛 隶属 Art of Problem Solving(AoPS),1993 年创立,覆盖课程、教材与社区板块
  • 社区论坛为核心:按课程/主题分版,支持与讲师和同学交流,支持匿名提问、LaTeX/Asymptote 排版与图片嵌入
  • 搜索功能完善,支持全文、高级筛选与公式搜索,便于定位历史讨论与解法
  • 讨论范围主要是
    • 竞赛训练:AMC 系列、AIME、USAMO 等,以及 MathCounts、Math Kangaroo、HMMT、PUMaC 等赛事与夏令营讨论
    • 课程配套交流:与 AoPS 在线课程深度整合,便于针对讲义与作业提问
    • 趣味与游戏:数学谜题、策略游戏与社区自建游戏板块(如 The Incredible Forum)
  • AoPS 论坛中会产出一些可用于 LLM 的数据,故而在这里记录

TriviaQA

  • HF 链接:mandarjoshi/trivia_qa
  • TriviaQA 是一个大规模开放域问答(Open-Domain QA)数据集
  • 由约 95 万个问答对和来自维基百科与网页的约 66.2 万篇文档构成,平均每个问题有 6 篇“证据文档”支持
  • 适合评估复杂的多文档阅读理解与推理能力
  • 构建方式:爱好者撰写问题,自动检索证据,再经人工验证与机器生成子集混合而成
  • 答案常需多句推理与跨文档综合,难以仅靠短跨度抽取解决,且上下文长、句式与词汇变化大
  • 常见任务包含问答、多文档阅读理解、开放域QA、问题生成等
  • 包含 8 个 subset,每个 subset 包含 3 个 split

FRAMES

  • 原始论文:Fact, Fetch, and Reason: A Unified Evaluation of Retrieval-Augmented Generation, arXiv 202409 & 202411 & 202506, Harvard & Google & Meta
  • HuggingFace 链接:huggingface.co/datasets/google/frames-benchmark
  • FRAMES(Factuality, Retrieval, And reasoning MEasurement Set),a high-quality dataset designed to test LLMs’ factual responses, retrieval capabilities, and reasoning in generating final answers
  • 高质量的,测试事实响应,检索能力和推理能力的数据集
  • 注:DeepSeek-R1 使用了该指标

Pile-test

  • Pile-test 是大语言模型评估中常用的标准测试集,源于更庞大的通用文本数据集 The Pile
  • The Pile 由 EleutherAI 构建的大规模开源文本数据集,总规模约 800GB,涵盖 22 个不同来源的文本类型(如学术论文、网页文本、书籍、新闻、代码等),旨在为模型提供多样化、高质量的训练与评估数据,避免单一数据分布导致的 “过拟合评估”
  • Pile-test 是 The Pile 的测试子集,与训练集(Pile-train)严格划分,用于客观衡量模型在通用语言理解与生成任务上的泛化能力,由于其覆盖场景广,模型在 Pile-test 上的表现能更真实反映 “通用能力”,而非仅适配某类特定数据

DS-1000

  • DS-1000,也称为 DS1000,其中 DS 是 Data Science 的含义,包含 1000 个数据科学问题的代码生成基准测试集,是测评代码能力的测试集
  • 原始论文:DS-1000: A Natural and Reliable Benchmark for Data Science Code Generation, 2023, HKU & PKU & Meta AI
  • 开源网站:ds1000-code-gen.github.io
  • DS-1000 覆盖 NumPy、Pandas 等 7 个 Python 库
  • DS-1000 具备三大核心特性:
    • 数据集的问题均来自 StackOverflow,能够反映多样化、贴近实际场景的实用用例;
    • 自动评估具有高度特异性(可靠性),在所有经评估判定为“可接受”的 Codex-002 模型预测解决方案中,仅有 1.8% 存在错误
      • 运行测试用例验证代码的功能正确性
      • 限制 API 使用或关键字来约束代码的表面形式;
    • 为防范模型依赖记忆答题,作者对原始 StackOverflow 问题进行了微调,使其与源问题存在差异,从而避免模型通过记忆预训练数据中的解决方案得出正确答案

GAIA

  • 论文地址:GAIA: a benchmark for General AI Assistants
  • GAIA(General AI Assistants)是通用人工智能助手基准测试
    • 论文中提到:该基准若能被攻克,将成为人工智能研究领域的一座里程碑
  • GAIA 设计的现实世界问题,要求模型具备一系列核心能力
    • 例如推理能力、多模态处理能力、网页浏览能力,以及通用意义上的工具使用熟练度
  • GAIA 的问题对人类而言难度较低,但对多数人工智能系统却极具挑战性:
    • 研究显示,人类受访者的正确率达 92%,而配备插件的 GPT-4 正确率仅为 15%
    • 这一显著的性能差距,与近年来 LLM 在法律、化学等需要专业技能的任务上超越人类的趋势形成鲜明对比
  • GAIA 的设计理念与当前人工智能基准测试的主流趋势有所不同,现有基准往往倾向于设置对人类而言难度越来越高的任务
    • 作者认为,AGI 的实现,关键在于系统能否展现出与普通人类相当的稳健性(robustness),在这类问题上达到人类水平
  • GAIA 包含构建了 466 个问题及其对应的答案
    • 作者对其中 300 个问题的答案予以保留,以便为排行榜 huggingface.co/gaia-benchmark 提供数据支持

VitaBench

  • 官方博客:美团 LongCat 团队发布 VitaBench:基于复杂生活场景的交互式 Agent 评测基准, 20251020
  • VitaBench(Versatile Interactive Tasks Benchmark)是高度贴近真实生活场景、面向复杂问题的大模型智能体评测基准
  • VitaBench 以外卖点餐、餐厅就餐、旅游出行三大高频真实生活场景为典型载体,构建了包含 66 个工具的交互式评测环境,并进行了跨场景的综合任务设计

BABILong

  • HuggingFace:huggingface.co/datasets/RMT-team/babilong
  • 原始论文:BABILong: Testing the Limits of LLMs with Long Context Reasoning-in-a-Haystack
  • HuggingFace 排行榜:BABILong Leaderboard
  • BABILong 是一款可扩展的生成式多任务测评基准,核心用于评估大语言模型在超长文档中跨分散事实的推理能力
    • 涵盖 20 种推理任务,包括事实链、归纳、演绎、计数、集合处理等,均基于 bAbI 基准扩展而来
    • 以 PG19 语料库的书籍作为背景文本,将任务相关事实隐藏其中,可构建任意长度的测评样本
    • 提供预定义的0K至1000万token的样本分割,实测可支持高达5000万token的超长文本评估
  • BABILong 弥补了Longbench等传统基准仅支持4万token的短板,适配当前LLM的百万级token处理能力
  • BABILong 能抗数据泄露,通过合成任务事实与自然背景文本混合的方式,避免模型因训练数据重叠获得虚假优势

MemBench

  • MemBench 是由中国人民大学高瓴人工智能学院与华为诺亚方舟实验室联合构建的大语言模型智能体记忆能力多维度评测基准
    • 其核心目标是解决现有评测难以全面衡量 LLM 智能体记忆性能的问题,为智能体记忆机制的研发提供可靠的评估依据
  • 聚焦智能体核心的两种记忆能力
    • 事实记忆 ,用于评估智能体对客观信息的存储与召回能力,比如用户的基本需求、任务中的关键参数等;
    • 反思记忆 ,侧重衡量智能体对过往经验的归纳总结能力,例如从多次交互中提炼用户行为规律
  • 设计了两种贴合智能体实际应用的场景
    • 参与场景(智能体作为参与者直接执行任务、与用户交互)
    • 观测场景(智能体作为观察者记录外部事件与信息)
    • 覆盖不同应用场景下的记忆需求
  • 突破单一效果评估的局限,从记忆的有效性(记忆内容的准确性、召回率)、效率(记忆存储与检索的速度)和容量(记忆承载上限)三个核心维度展开评估
  • 实验显示,当记忆容量超过 500 条时,多数模型在该基准中的召回率下降超 20%,可有效检测模型的记忆容量瓶颈

FreshQA

  • FreshQA 是由谷歌和 OpenAI 研究团队联合构建的动态问答基准评测集,专门用于评估大型语言模型生成内容的事实准确性 ,尤其针对模型处理实时变化知识和错误前提问题的能力
    • 用于暴露当前 LLM 的幻觉问题和知识滞后缺陷
  • 该评测集共包含 600 个自然问题,分为测试集和开发集
    • 其中测试集有 500 个样本,四种问题类型各 125 个;
    • 开发集 100 个样本,四种问题类型各 25 个,另外还提取了 15 个跨类型示例用于演示
    • 这些问题覆盖多类主题,且需模型具备不同层级的推理能力
  • 所有问题归为四大类,适配对不同知识类型的评测需求
    • 永不改变的知识 ,比如“谁写了《杀死一只知更鸟》”这类历史、文学领域固定事实问题;
    • 慢变知识 ,答案可能隔数年变化,像“纽约市的人口数是多少”;
    • 快变知识 ,答案一年内可能多次变动,例如“本年度奥斯卡最佳男主角是谁”;
    • 错误前提知识 ,问题基于虚假假设,如“罗杰·费德勒在 2022 年总共赢得了多少个大满贯赛事冠军”,这类问题要求模型能指出前提缺陷而非直接作答
  • 每类问题又分为一跳和多跳两个难度。一跳问题无需额外推理,如“谁是 Twitter 的首席执行官”;多跳问题则需多步推理才能获取答案,如“世界上最高建筑的总高度是多少”
  • 评测采用双模式人工评估程序,累计完成超5万次判断,评估标准严谨
    • RELAXED(宽松模式) ,仅关注核心答案的正确性,允许不影响核心结论的非规范表述或次要信息瑕疵
    • STRICT(严格模式) ,要求回答中所有表述均符合最新事实,无任何幻觉信息。对于错误前提类问题,模型必须主动指出前提错误才能得分;
    • 数字类答案一般不接受近似值,除非基准答案中明确允许

FACTS Grounding

  • FACTS Grounding 数据集是谷歌 DeepMind 与谷歌研究院联合构建的基准测试数据集,核心用途是评估大型语言模型 基于给定上下文文档生成内容的事实准确性与事实锚定能力 ,以此破解模型“幻觉”问题,目前已搭配 Kaggle 在线排行榜用于实时跟踪模型性能进展
  • 目前数据集仅聚焦长文本输入的事实锚定响应评估
  • 数据来源于公开互联网,但其中的用户请求和系统指令为全新设计,不存在污染问题
  • 数据集共包含 1719 个示例,分为 Public 和 Private 两个集合
    • 其中公共集合有 860 条样本,已公开供研究者开展模型评估;
    • 私有集合含 859 条样本,仅用于排行榜评分,这种划分能有效避免基准污染和排行榜作弊问题
    • 最终榜单分数需结合两个集合的平均性能得出
  • 每个样本均由三部分构成
    • 系统指令,明确要求模型只能依据给定上下文文档生成回应,不得调用外部知识;
    • 用户请求,涵盖摘要、问答生成、文档改写等真实任务;
    • 上下文文档,包含回答问题所需的全部信息,文档平均长度为 2.5k 个 token,最长可达 32k 个 token(约2万个单词)
  • 文档覆盖金融、技术、零售、医学和法律等多个实用领域
    • 且数据集刻意避开了需要创造力、数学运算或复杂推理的任务,用户请求也无需领域专业知识,更贴合日常场景下的文本处理需求
  • 研究人员聘请第三方人工标注员,依据长篇输入和各类文本任务撰写长篇输出内容,保障样本的真实性和合理性
    • 标注完成后会经过手动验证,移除与指令不一致的样本;同时剔除来源为 PDF 的文档,避免光学字符识别(OCR)可能带来的误差;还会确保用户请求具有实际意义,过滤无效或无价值的内容
  • 该数据集采用两阶段AI评判模式,选用 Gemini 1.5 Pro、GPT-4o 和 Claude 3.5 Sonnet 三款前沿模型作为评判器,以此保障评估结果的客观性和与人工评分的一致性
    • 第一阶段先评估模型回应的合格性,若未充分满足用户请求则直接取消评分资格;
    • 第二阶段聚焦事实准确性,判断回应是否完全基于上下文文档,无任何幻觉信息
    • 最终得分取三款评判模型在所有样本上的评分平均值

Terminal Bench

  • Terminal-Bench是由斯坦福大学与 Laude 研究所联合开发的开源基准测试与评测框架,专门用于衡量 AI 在真实终端(如 Linux 命令行)环境中完成复杂任务的能力
  • Terminal-Bench 通过标准化任务和环境,量化AI代理在终端场景的实操能力,为开发者优化代理的可靠性和安全性提供参考依据
  • 其初始版本 Terminal-Bench-Core 包含 80 个人工设计并验证的任务,后续持续扩展,目前“head”版已达 117 题
    • 任务覆盖科学工作流、网络配置、网络安全漏洞修复、代码编译部署等多个实用场景,且每个任务都配有人类验证的解决方案和测试用例集
    • 每个任务都配备专属 Docker 环境,能确保测试在隔离的沙箱中进行,避免外部环境干扰,同时保证不同开发者和机构测试结果的可重复性,解决了终端任务评估中环境不一致导致的结果偏差问题
    • 提供 CLI(命令行界面)工具,开发者通过一条命令就能拉起沙箱、连接代理并执行测试
    • 评估时不依赖提示词匹配或主观评价,而是以测试用例是否通过为标准计分,结果客观精准
  • Terminal Bench 设立了公开排行榜展示各类代理-模型组合的任务解决率,方便开发者直观对比不同 AI 代理的性能
    • terminal-bench@2.0 Leaderboard
    • terminal-bench@1.0 Leaderboard
  • 该框架不仅有自建任务,还适配了 SWE-Bench Verified、AppWorld 等热门外部评测,开发者接入一次接口就能完成多基准测试
  • Terminal Bench 作为开源项目,开放了文档、任务注册表等资源

CoreCodeBench

  • HuggingFace:huggingface.co/datasets/meituan-longcat/CoreCodeBench-Source_Copy
  • CoreCodeBench 是美团与上海交大联合推出的仓库级代码评测基准,用于测试 LLM 在真实工程场景的编程与调试能力,以自动化单元测试判分,核心指标为Pass@1(首条输出通过所有测试用例的比例)
  • CoreCodeBench 以 Pass@1 为核心,用真实仓库级任务与自动化测试,评估 LLM 的工程级编程与调试能力,适合筛选能落地的代码模型
  • CoreCodeBench 场景覆盖:单/多函数的开发(Development)、缺陷修复(BugFix)、测试驱动开发(TDD),并设有高难度子集(Difficult),总计约1545题,源自12个真实开源仓库,更贴近量产级开发
  • CoreCodeBench 构建流程:通过 CorePipe 自动化 pipeline 定位核心代码、生成题目与测试用例,兼顾规模化与高质量
  • 关键评测指标
    • Pass@1:首条生成的代码通过全部单元测试的比例
      • 主指标,衡量一次性正确性与可靠性
    • AC@1/AC Rate:同Pass@1(不同文档命名),增量正确率
      • 辅助追踪迭代改进
    • 题型细分通过率:按开发/修复/TDD、单/多函数拆分的通过率
      • 定位模型短板与优势场景
  • 相比 HumanEval/MBPP(单函数补全):覆盖跨文件、多函数协作与缺陷修复,更贴近真实工程
  • 相比 SWE-Bench(仓库级):自动化生成与判分,可复现性与规模化更好,适合快速迭代评测
  • 快速上手:从 AGI-Eval 社区 获取数据集与评测脚本,按统一 Prompt 生成代码,批量执行测试用例统计 Pass@1;建议同时记录题型细分通过率,便于归因优化

AlpacaEval

  • AlpacaEval 是一种用于评估 LLM 指令遵循能力(Instruction-following)的自动化评估基准
  • AlpacaEval 是一个让 GPT-4 当裁判,来给其他大模型打分的排行榜
    • AlpacaEval 目前是业界衡量大模型对话能力的重要参考指标之一(尤其是 2.0 版本),但研究者通常会结合 Arena-Hard、MT-Bench 等其他指标来综合判断模型性能,以避免单一指标带来的误导
  • AlpacaEval 由斯坦福大学(tatsu-lab)的研究团队推出,旨在通过模拟人类评估的方式,快速、低成本地衡量模型在开放式对话中的表现
  • AlpacaEval 的核心理念是 “以大模型评测大模型” (LLM-as-a-judge)
    • 不依赖昂贵且耗时的人工评分,而是利用强大的模型(通常是 GPT-4)作为裁判,来判断待测模型的回答质量
  • AlpacaEval 评估流程:
    • 1)数据集:使用包含真实用户指令的评估集(源自 AlpacaFarm 数据集),涵盖各种类型的用户问题
    • 2)生成回复:待测模型针对这些指令生成回复
    • 3)对抗评测:裁判模型(如 GPT-4)会同时看到待测模型的回复和一个基准模型(通常是 text-davinci-003 或 GPT-4 Turbo)的回复
    • 4)判定胜负:裁判判断哪个回复更好,从而计算出待测模型相对于基准模型的胜率(Win Rate)
  • AlpacaEval 的优点:
    • 在 AlpacaEval 出现之前,评估聊天模型通常依赖于静态的客观题(如 MMLU)或昂贵的人工评估(如 Chatbot Arena)
    • 模拟人类偏好:AlpacaEval 的设计目标是与人类的偏好高度相关,即 GPT-4 认为好的回答,通常人类也认为好
    • 速度与成本:相比人工评估,它极其快速且经济,允许研究人员在模型迭代过程中频繁测试
    • 指令遵循:它专门针对“指令遵循”能力进行测试,这比单纯的知识问答更能反映聊天机器人的实际用户体验
  • AlpacaEval 在发展过程中经历了 AlpacaEval 1.0 到 AlpacaEval 2.0 迭代,主要是为了解决 “长度偏差”(Length Bias) 问题
    • AlpacaEval 1.0 的问题:早期的评估发现,裁判模型(GPT-4)倾向于认为“越长越好”
      • 即使一个回答内容空洞,只要写得很长,往往也能获得高分
      • 这导致许多模型通过生成冗长的废话来“刷榜”
    • AlpacaEval 2.0 的改进:为了解决这个问题,AlpacaEval 2.0 引入了长度控制胜率(Length-Controlled Win Rate, LC Win Rate)
      • 通过统计方法(Logistic Regression)以此来消除输出长度对评分的影响,迫使模型通过提高回复的质量而不是长度来提升排名
  • AlpacaEval 的缺点
    • 裁判偏见:由于裁判通常是 GPT-4,它可能更偏好与自己风格相似的回答(Self-preference bias)
    • 容易被“作弊”:研究发现,即使是一个始终输出固定内容的“空模型”或经过特定微调的模型,也有可能通过利用裁判的漏洞在榜单上获得极高的胜率,甚至在数据上“吊打”GPT-4
    • 评估单一性:它主要评估单轮对话的指令遵循,可能无法全面反映模型在多轮对话、逻辑推理或特定领域(如数学、代码)的深层能力

附录:AlpacaEval 2.0 LC Win Rate 详细介绍

  • AlpacaEval 2.0 LC Win Rate(Length-Controlled Win Rate,长度控制胜率)是目前大模型领域非常权威的一个评测指标,主要用于评估模型在指令跟随(Instruction Following)方面的能力
  • TLDR: 它的核心目的是:在去除“字数越多得分越高”这种偏见的前提下,公正地评判一个模型回答得好不好
  • 在早期的大模型自动评测中,人们发现了一个严重的 bug:AI 裁判(如 GPT-4)非常喜欢“长篇大论”
    • 哪怕一个回答内容空洞,只要写得很长,GPT-4 往往会判它赢(Win),很多模型厂商为了刷榜,故意把模型训练得非常啰嗦(Verbosity),导致分数虚高,但实际用户体验很差
    • 为了解决这个问题,AlpacaEval 团队推出了 2.0 版本,并引入了 LC (Length-Controlled) 机制
  • LC Win Rate 的计算逻辑不是简单的“谁赢了加 1 分”,而是一个统计学上的调整过程:
    • 1)两两对战: 让待测模型(例如 Llama 3)和基准模型(通常是 GPT-4 Turbo)回答同样的 805 个指令问题
    • 2)裁判打分: 让 GPT-4 Turbo 作为裁判,判断哪个回答更好
    • 3)统计去噪(核心步骤):
      • 系统会使用一个统计模型(如 LR 模型)来分析裁判的偏好
      • 它会计算:“如果我们假设这两个回答的长度是一样的,裁判更倾向于谁?”
      • 通过这种数学手段,把“长度”这个干扰因素从分数中剥离出去,只保留“内容质量”带来的胜率
  • 经过 LC 修正后,AlpacaEval 2.0 的排名与 LMSYS Chatbot Arena(人类盲测竞技场) 的排名高度一致(相关性高达 0.98)
  • LC Win Rate 可以防刷榜: 它迫使模型开发者关注回答的质量、逻辑和准确性 ,而不是单纯地增加字数

InfoBench

  • 原始论文:InfoBench: Evaluating Instruction Following Ability in Large Language Models, 20240107, Tencent AI Lab
  • HuggingFace:huggingface.co/datasets/kqsong/InFoBench
  • InfoBench 是一个专门用于评估 LLM 指令遵循(Instruction Following)能力的基准测试
  • 与传统的 NLP 任务不同, InfoBench 侧重于考察模型在处理包含多个复杂约束(Constraints)的指令时的表现
  • InfoBench 聚焦于 LLM 对“原子约束”(即任务中明确的细节要求)的遵循能力,通过量化模型满足约束的表现,评估其在复杂任务中的可靠性
  • InfoBench 的核心在于将一个复杂的指令分解为多个原子约束(Atomic Constraints)
    • 例如,如果指令是“写一段关于猫的 50 字短文,不要提到‘鱼’,并使用法语”,它会被分解为:
      • 1)话题:关于猫
      • 2)长度限制:约 50 字
      • 3)负向约束:不提到“鱼”
      • 4)语言要求:法语
  • InfoBench 不仅给出总分,还会根据约束的性质进行分类评估,常见的维度包括:
    • 格式约束 (Format) : 要求输出特定的数据格式
      • 比如:”以 JSON 格式输出”, “使用 Markdown 表格”
    • 内容约束 (Content) : 要求包含或排除特定信息
      • 比如: “提及‘量子力学’”, “不要提到任何颜色”
    • 长度约束 (Length) : 限制字数、句子数或段落数
      • 比如: “少于 30 个词”, “正好三段”
    • 语言约束 (Language) : 指定输出的语种
      • 比如: “使用古汉语回答”, “翻译成德语”

InfoBench 的核心评测指标与公式

  • InfoBench 主要通过以下三个维度的指标来衡量模型的性能:
    • CSR:看所有约束的整体达标率 ,不区分指令
    • SA:看指令级的“全对率” ,要求单个指令的所有约束都满足
    • ACF:看指令级的“平均达标率” ,允许单个指令部分约束满足,再取平均
  • 约束满足率 (Constraint Satisfaction Rate, CSR)
    • CSR 衡量模型在所有测试用例中,成功遵循的原子约束占总约束的比例(最基础的整体约束达标率)
      $$
      CSR = \frac{\sum_{i=1}^{N} \text{Satisfied}(c_i)}{N}
      $$
      • \( N \):所有测试样本中包含的原子约束总数;
      • \( \text{Satisfied}(c_i) \):指示函数(第\( i \)个约束满足则为1,否则为0)
  • 严格准确率(Strict Accuracy, SA)
    • SA 衡量模型在单个指令(Prompt)中,完全满足所有原子约束的比例(更严苛,反映复杂多约束任务的可靠性)
      $$
      SA = \frac{\sum_{j=1}^{M} \prod_{k=1}^{K_j} \text{Satisfied}(c_{j,k})}{M}
      $$
      • \( M \):总指令(Prompt)数量;
      • \( K_j \):第\( j \)个指令包含的原子约束数量;
      • \( c_{j,k} \):第\( j \)个指令的第\( k \)个约束;
      • \( \prod \)(连乘):只有当该指令的所有约束都满足(连乘结果为1),才算该指令通过
  • 平均约束遵循度(Average Constraint Following, ACF)
    • ACF 衡量模型在每个指令中满足约束的比例的平均值(关注单个任务的平均表现,而非整体约束计数)
      $$
      ACF = \frac{1}{M} \sum_{j=1}^{M} \left( \frac{\sum_{k=1}^{K_j} \text{Satisfied}(c_{j,k})}{K_j} \right)
      $$
      • 先计算“单个指令内满足约束的比例”(\( \frac{\sum_{k=1}^{K_j} \text{Satisfied}(c_{j,k})}{K_j} \)),再对所有指令取平均

InfoBench 评测流程简述

  • Step 1 分解 (Decomposition): 利用强大的教师模型(如 GPT-4)或预定义的规则,将复杂指令拆解为
  • Step 2 执行 (Execution): 被测模型生成回复
  • Step 3 验证 (Verification): 再次利用教师模型或程序脚本,逐一检查每个原子约束是否在回复中得到满足
  • Step 4 汇总 (Aggregation): 应用上述公式计算最终得分

BPB(Bits per Byte)

  • BPB(Bits per Byte,比特 / 字节)是 LLM 语言建模的 Byte-level 评估指标
  • BPB 的物理意义:在编码或预测文本时,平均每个字节(Byte)需要消耗多少个比特(Bit)的信息量
  • BPB 可以衡量模型对文本的预测效率与压缩能力,值越小越好,且与分词器无关 ,便于跨模型公平对比
  • 注:在基于深度学习的图像/视频压缩大模型中,也有个类似指标 BPP (Bits Per Pixel,每像素比特数),此时,BPP 是衡量压缩率的最关键指标
  • BPB 的本质是归一化到字节的平均交叉熵(以2为底取对数)。对长度为\( T \)字节的文本,BPB 计算公式为:
    $$
    \text{BPB} = \frac{1}{T} \sum_{t=1}^T -\log_2 P(x_t | x_1, x_2, \dots, x_{t-1})
    $$
  • 其中:
    • \( T \):文本的总有效字节数(排除特殊 token、masked 位置等非内容字节)
    • \( x_t \):第\( t \)个字节的内容
    • \( P(x_t | x_1, x_2, \dots, x_{t-1}) \):模型基于前文\( x_1 \dots x_{t-1} \)对第\( t \)个字节的预测概率

与困惑度(Perplexity)的换算关系

  • 若文本的 token 序列长度为\( N \),每个 token 对应的字节数为\( l_i \)(\( i=1,2,\dots,N \)),总字节数\( T = \sum_{i=1}^N l_i \),token 级困惑度\( \text{PPL} = \exp\left( \frac{1}{N} \sum_{i=1}^N -\log P(y_i | y_1, \dots, y_{i-1}) \right) \)(\( y_i \)为第\( i \)个 token),则 BPB 与困惑度的近似换算(以自然对数转2为底):
    $$
    \text{BPB} \approx \frac{1}{T} \cdot \frac{\ln(\text{PPL}) \cdot N}{\log_2 e}
    $$
  • 其中\( \log_2 e = \frac{1}{\ln 2} \approx 1.4427 \),因此也可简化为:
    $$
    \text{BPB} = \frac{N \cdot \ln(\text{PPL})}{T \cdot \ln 2}
    $$

关于 BPB 名字的理解

  • 公式里“没有包含比特信息”,是因为这里的“比特(Bit)”并非指计算机存储中的物理开关(0或1),而是信息论(Information Theory)中的度量单位
  • TLDR:公式中的 \(\log_2\)(以 2 为底的对数) 就是将“概率”转换为“比特”的数学算子
    • 在信息论中,\(-\log_2(p)\) 的结果单位定义就是“比特”
  • 让我们把公式拆开,逐项对应到物理意义上,你就会发现它完美对应了 Bits Per Byte 的名字,公式:
    $$ \text{BPB} = \frac{1}{T} \sum_{t=1}^{T} \underbrace{-\log_2 P(x_t | x_{<t})}_{\text{核心部分}} $$
  • 分子:“Bit” 藏在 \(-\log_2 P\) 里
    • 在香农信息论中,信息量(Information Content) 的定义是:
      $$ I(x) = -\log_2 P(x) $$
      • 数学定义 :如果一个事件发生的概率是 \(P\),那么消除这个不确定性所需的信息量就是 \(-\log_2 P\)
      • 单位定义 :当底数为 2 时,计算结果的单位被定义为 Bit(比特)
      • 如果底数是 \(e\),单位是 Nat
      • 如果底数是 10,单位是 Hartley
    • 举例
      • 抛硬币 :正面朝上的概率是 $0.5$ ($1/2$)
        $$ -\log_2(0.5) = 1 \text{ bit} $$
        • 这符合直觉:需要 1 个比特(0或1)来记录硬币的结果
      • 完全随机的字节 :一个字节有 256 种可能(0-255),如果模型完全猜不到(均匀分布),概率是 \(1/256\)
        $$ -\log_2(1/256) = \log_2(2^8) = 8 \text{ bits} $$
        • 这意味着:如果你完全猜不到下一个字节是什么,你就需要完整的 8 个比特 来存储它(没有任何压缩)
    • 结论:公式中的求和项 \(\sum -\log_2 P(\dots)\) 计算出的数值,物理意义就是 “编码这段文本理论上所需的总比特数”
  • 分母:“Byte” 藏在 \(T\) 里,在 BPB 指标中,\(T\) 代表的是文本序列中 字节(Byte)的总数量
  • 将分子和分母结合起来看:
    $$ \text{BPB} = \frac{\text{总信息量 (Total Bits)}}{\text{总字节数 (Total Bytes)}} $$
    • 分子 :\(\sum -\log_2 P \rightarrow\) 单位是 Bits(因为用了 \(\log_2\))
    • 分母 :\(T \rightarrow\) 单位是 Bytes(因为 \(t\) 遍历的是字节序列)
    • 结果 :Bits Per Byte

为什么 BPB 指标代表“压缩能力”?

  • 引入算术编码(Arithmetic Coding)的概念
  • 理论上,存在一种完美的压缩算法,它能将概率为 \(P\) 的符号,压缩成长度为 \(-\log_2 P\) 个比特的二进制码流
  • 模型越聪明(\(P\) 越大) :
    • 如果模型非常确信下一个字是“A”,预测概率 \(P=0.99\)
      $$ -\log_2(0.99) \approx 0.014 \text{ bits} $$
    • 这意味着只需要极少的比特就能存储这个信息(压缩率极高)
  • 模型越笨(\(P\) 越小) :
    • 如果模型觉得下一个字可能是任何字,预测概率 \(P=0.001\)
      $$ -\log_2(0.001) \approx 9.96 \text{ bits} $$
    • 这意味着需要花费很多比特来记录这个意外的信息
  • 公式 \(\text{BPB} = -\frac{1}{T} \sum \log_2 P\) 实际上是在计算:
    • “如果利用该模型对文本进行无损压缩,平均每个字节最终会被压缩成多少个比特”

HealthBench

  • 原始论文:HealthBench: Evaluating Large Language Models Towards Improved Human Health, OpenAI, 20250513
  • HuggingFace:huggingface.co/datasets/openai/healthbench
  • 博客链接:Introducing HealthBench, OpenAI, 20250512
  • HealthBench 是 OpenAI 推出的医疗大模型评估基准,核心以医生编写的细粒度评分标准为核心,从 5 大行为维度与 7 大场景主题对模型回复打分,用标准化方式衡量医疗大模型在真实临床交互中的安全性、准确性与实用性
  • 基于真实对话与医生共识,评估结果更贴近临床实际需求,避免传统选择题评估的局限性
  • 覆盖26个医学专科、49种语言,适配不同地区诊疗差异
  • 开源评分器与基准结果,支持第三方复现评估、扩展标准,推动医疗 AI 评估标准化
  • 评分流程:
    • 1)评分标准生成 :每段对话由医生编写专属评分标准,明确应包含/避免的内容及对应分值(±10分),反映该标准的重要性
    • 2)自动化评分 :由 GPT‑4.1 训练的模型评分器,判断回复是否满足每项标准,达标得全部分值,未达标不得分
    • 3)综合得分计算 :单条回复得分 =(达标标准总分 ÷ 该对话满分)×100%,最终按所有对话平均分计算模型整体得分
    • 4)可靠性验证 :评分器与医生评估的宏 F1(Macro F1) 值达 0.71,接近医生间互评一致性,确保自动化评分的可信度
  • 衍生评估版本
    • HealthBench Consensus :聚焦 34 项经医生验证的核心标准,重点评估紧急处置、情境澄清等关键模型行为,适合快速基准测试
    • HealthBench Hard :精选 1000 段高难度对话,用于挑战模型在复杂临床场景下的极限表现

核心评估维度(5 大行为轴)

  • 每个评分标准都对应以下维度,每个维度有明确正向/负向判据,分值范围 ±10 分,用于奖励或惩罚模型行为:
  • 1)准确性(Accuracy) :±10分(奖励正确行为,惩罚错误行为)
    • 定义:仅含事实正确信息,有证据/共识支持,证据有限时明确不确定性
    • 评估点:无错误事实、避免常见误解、不编造信息、标注信息来源与不确定性
  • 2)完整性(Completeness) :±10分
    • 定义:覆盖安全有效回复所需的关键信息,不遗漏必要内容
    • 评估点:包含核心诊疗步骤、风险提示、随访建议等,无关键信息缺失
  • 3)沟通质量(Communication Quality) :±10分
    • 定义:回复长度、清晰度、细节度、词汇、结构契合用户需求与情境
    • 评估点:语言通俗、逻辑清晰、重点突出、避免冗余术语、适配用户角色(患者/医生)
  • 4)情境感知(Context Awareness) :±10分
    • 定义:结合用户角色、环境、资源等情境恰当响应,必要时主动澄清
    • 评估点:适配地区诊疗规范、用户可及资源、患者基础疾病,主动询问缺失信息
  • 5)指令遵循(Instruction Following) :±10分
    • 定义:严格遵守用户明确指令,如格式要求、任务步骤等
    • 评估点:不偏离用户需求、按要求完成临床笔记总结/报告生成等任务

评估主题(7 大主题场景分类)

  • 覆盖真实医疗交互的关键场景,每个主题聚焦特定临床挑战,所有对话由 262 名跨 60 国医生共同设计,共 5000 段多轮对话,对应 48,562 条独特评分标准:
    • 1)紧急转诊(Emergency Referrals):评估危急情况(如心脏骤停、休克)下的规范处置与转诊建议
    • 2)情境寻求(Context Seeking):判断模型是否主动识别缺失信息以提供精准回复
    • 3)不确定性下的响应(Responding Under Uncertainty):考察低证据场景下的不确定性表达与安全建议
    • 4)响应深度(Response Depth):根据用户背景(如患者/医生)调整回答详细程度
    • 5)健康数据任务(Health Data Tasks):评估医疗文档书写、临床笔记总结、健康管理等任务的完成质量
    • 6)专业定制沟通(Professionally Tailored Communication):适配不同专业角色的沟通策略(如对患者通俗解释、对医生专业表述)
    • 7)全球健康(Global Health):适配不同地区资源、流行病学与诊疗规范的跨地域响应能力

WritingBench

  • 论文链接:WritingBench: A Comprehensive Benchmark for Generative Writing
  • WritingBench (生成式写作综合基准) 是一个专门针对 LLMs 生成式写作能力进行评估的综合性基准,旨在解决传统基准无法全面覆盖写作领域的问题
  • HuggingFace:
  • WritingBench 包含 6 个核心写作领域(如创意写作、技术写作、公文写作等)和 100 个子领域 ,旨在全面评估模型在不同语境下的生成能力
  • WritingBench 采用了一种“查询依赖”(query-dependent)的评估框架,这意味着评估标准会根据用户的具体指令动态调整,而非使用单一的通用标准
  • WritingBench 基准通常结合了 LLM-based 自动评估(如使用 GPT-4 或 Claude 作为裁判)与人工评估的验证,重点关注文本的连贯性、创造性、遵循指令的能力以及内容的深度

EQ-Bench

  • GitHub (官方仓库):github.com/EQ-bench/EQ-Bench
  • 官方网站 (Leaderboard):eqbench.com/
  • 数据集:
    • EQ 任务数据子集:EQ-Bench GitHub Release
    • Creative Writing 数据子集:Creative Writing Bench GitHub
  • EQ-Bench (情商与创意写作基准) 最初设计用于评估大语言模型的情商(Emotional Intelligence),随后扩展了针对创意写作(Creative Writing)的评估分支,是目前评估模型“人性化”与“文学性”的重要榜单
  • EQ-Bench 包含多个 任务:
    • 核心任务 (EQ) :评估模型理解复杂情绪和社交互动的能力
      • 数据集包含具有挑战性的角色扮演(Roleplays)对话,模型需要预测对话中角色的情绪状态或做出符合情境的反应
    • 创意写作 (Creative Writing v3) :这是 EQ-Bench 的一个重要分支,专门用于评估模型的文学创作能力
      • 它通过设定具体的写作提示(Prompt),让模型生成故事或文章
  • EQ-Bench 评分使用 Rubric Score,使用一套详细的规则(Rubric),通常由高水平模型(如 Claude 3.5 Sonnet 或 GPT-4)作为裁判,对生成内容的风格、重复度、长度和指令遵循度进行打分
  • EQ-Bench 排行榜使用 Elo Score,采用竞技排名系统(Elo Rating),通过成对比较不同模型的输出优劣来计算相对排名,这种方法被认为比单一分数更能反映模型间的细微差距

MRCR (Multi-Round Co-reference Resolution)

  • HuggingFace:openai/mrcr
  • MRCR 是一个针对长上下文(Long Context)能力的评估任务
    • 与传统的单点检索(NIAH)不同,MRCR 要求模型在长对话或长文本中识别并关联多个分散的信息点,主要测试模型在长窗口下区分相似信息(Multiple Needles)和处理共指关系的能力
  • 该数据集通常作为技术报告的一部分出现,而非单一论文,在评估 GPT-4 Turbo、GPT-4o 以及 Gemini 1.5 Pro 等长窗口模型时被广泛引用
    • 相关技术报告参考:Gemini 1.5: Unlocking multimodal understanding across millions of tokens of context(其中详细描述了 MRCR 任务机制) * 相关研究:Long Context Evaluations Beyond Haystacks via Latent Structure Queries
  • MRCR 任务逻辑:
    • 模型会看到一段非常长的用户与 AI 的多轮对话历史(作为“大海/Haystack”)
    • 用户会提出一个问题,该问题需要模型回溯到对话中的特定轮次(例如“我在第 3 次询问水果时,你推荐了什么?”)
  • MRCR 数据集的难点(重点考察点):
    • 多针检索 (Multiple Needles) :上下文中包含大量相似的对话结构,模型必须精确定位到特定的那一轮,而不是混淆其他轮次的信息
    • 共指消解 (Co-reference) :模型需要理解 “第 i 次”、“那个” 等代词具体指代长文本中的哪一部分内容
  • MRCR 的评估指标通常使用 检索准确率 (Retrieval Accuracy)
    • 在不同长度的上下文窗口(如 32k, 128k, 1M)下,计算模型成功定位并正确回答特定轮次内容的百分比

MT-Bench

  • HuggingFace:huggingface.co/datasets/lmsys/mt_bench_human_judgments
  • MT-Bench (Multi-Turn Benchmark) 由 LMSYS Org(也就是发布了著名的 Chatbot Arena 和 Vicuna 模型的组织)提出
  • MT-Bench 是一个专门用于评估 LLM 多轮对话能力和指令跟随能力的评测基准
    • MT-Bench 的核心目的是测试模型在连续对话中是否“聪明” ,能不能接得住话、记住了前文,并准确完成复杂的指令
  • “MT” (Multi-Turn) 的含义
    • 传统的评测往往是一问一答(Single-turn),模型回答完就结束了,但在真实应用中,用户经常会追问、修改需求或要求模型根据上文继续生成
    • MT-Bench
      • 多轮机制: MT-Bench 的每个测试题包含两轮对话
      • 第一轮: 提出一个有难度的问题
      • 第二轮: 基于模型的回答,提出一个追问(例如:“请把刚才的代码重写一遍,要求效率更高”或“请缩短刚才的文章,但保留核心观点”)
    • MT-bench 会考察模型是否具备上下文记忆、逻辑推理以及根据反馈修正的能力
  • MT-Bench 精心设计了 80 个高质量的多轮问题(共 160 个问答),涵盖了 8 个主要类别,旨在模拟人类的高频使用场景:
    • Writing (写作) :创作、修改文本
      • 如 “写一篇博客…” -> “请改写得更幽默一点”
    • Roleplay (角色扮演):模拟特定语气或人设
      • 如 “你是一个求职面试官…”
    • Reasoning (推理) :逻辑推导、常识推理
      • 如 解决逻辑谜题或分析因果关系
    • Math (数学) :数学计算与解题
      • 如 几何、代数或应用题
    • Coding (代码) :编写与修改代码
      • 如 “写一个Python函数…” -> “请优化它的时间复杂度”
    • Extraction (提取) :从长文中提取信息
      • 日 总结会议记录、提取关键数据
    • STEM (理工科) :科学、技术、工程知识
      • 如 物理概念解释、工程原理
    • Humanities (人文) :历史、文化、哲学
      • 如 分析历史事件或文化现象
  • MT-Bench 最具标志性的特点是它不完全依赖人工评分(太慢太贵),也不依赖传统的死板指标(如 BLEU/ROUGE,对生成式对话不准)
    • MT-Bench 使用 GPT-4 作为裁判(Judge),将待测模型的回答输入给 GPT-4,让 GPT-4 根据回答的质量、准确性、创造性等维度打分(通常是 1-10 分)
    • 研究表明,GPT-4 的打分结果与人类专家的偏好具有很高的一致性(Agreement rate),因此被广泛作为“平替”人类评估的低成本方案
  • 注:在开源大模型领域(如 Llama 3, Qwen, Mistral 等),MT-Bench 几乎是必测的标准之一,原因如下:
    • 1)更接近真实体验: 相比于做选择题(如 MMLU),MT-Bench 的开放式问答更像用户平时使用 ChatGPT 的真实感觉
    • 2)区分度高: 很多模型第一轮回答得不错,但第二轮追问时就会露馅(比如忘记前文设定),MT-Bench 能有效暴露出这种弱点
    • 3)Chatbot Arena 的风向标: MT-Bench 的分数通常与模型在 Chatbot Arena(盲测竞技场)中的 Elo 排名高度正相关
  • MT-Bench 的缺点:
    • GPT-4 偏见: GPT-4 可能更倾向于给“像自己”的回答打高分(Self-preference bias)
    • 题目数量少: 只有 80 题,模型可能会针对性地去“刷题”或过拟合(Overfitting)
    • 静态性: 题目是固定的,随着模型越来越强,很多题目变得太容易,区分度正在逐渐降低

MultiChallenge

  • 原始论文:MultiChallenge: A Realistic Multi-Turn Conversation Evaluation Benchmark Challenging to Frontier LLMs
  • GitHub :ekwinox117/multi-challenge
  • MultiChallenge 是一个专注于评估 LLM 在多轮对话 中应对现实挑战能力的基准
  • MultiChallenge 主要验证模型的以下能力:
    • 指令保留 (Instruction Retention) :模型能否在多轮对话后依然记住最初的指令
    • 推理记忆 (Inference Memory) :对用户在前几轮提到的信息进行推理
    • 可靠的版本编辑 (Versioned Editing) :在多次修改任务(如修改代码或文案)时保持一致性
    • 自我连贯性 (Self-coherence) :回答不与之前的发言冲突

MarsBench (MARS-Bench)

  • 原始论文:MARS-Bench: A Multi-turn Athletic Real-world Scenario Benchmark for Dialogue Evaluation
  • MarsBench(全称:Multi-turn Athletic Real-world Scenario Benchmark)是首个利用体育赛事文字直播(Play-by-play)数据 构建的多轮对话基准,模拟了极端真实、高动态的场景
  • 注:学术界还有一个同名但不同领域的 Mars-Bench(用于火星科学任务的视觉模型评测),但在 LLM 评测语境下,通常指代以下对话基准
  • MarsBench 主要验证模型的以下能力:
    • 超多轮对话 (Ultra Multi-turn) :包含超过 30 轮的对话序列
    • 交互式生成 (Interactive Generation) :模型需要在每一轮都做出正确响应
    • 跨轮任务 (Cross-turn Tasks) :需要从非相邻的轮次中提取和推理信息
    • 动机转移 (Motivation Transfer) :用户意图在对话中发生突变时的处理能力

Multi-IF

  • 原始论文:Multi-IF: Benchmarking LLMs on Multi-Turn and Multilingual Instructions Following
  • Hugging Face :huggingface.co/datasets/facebook/Multi-IF
  • GitHub :facebookresearch/Multi-IF
  • Multi-IF 是 IFEval 的升级版
    • IFEval 主要关注单轮、英语的指令遵循,而 Multi-IF 将其扩展到了 多轮 和 多语言 环境
  • Multi-IF 主要验证模型的以下能力:
    • 多轮复杂性 :每组数据包含 3 轮 连续对话,每轮都有特定的可验证指令(如“回复不少于 300 字”、“使用 JSON 格式”等)
    • 多语言支持 :涵盖 8 种语言(包括中文、印地语、俄语等非拉丁字母语言)
    • 硬约束检查 :沿用了 IFEval 的严苛评分方式,要求模型必须严格满足所有格式和内容约束

附录:大模型训练集和测试集的划分

  • 补充问题:很多大模型相关文章不会明确给定训练样本和测试样本的拆分,那训练集和测试集是如何配置的呢?
  • 回答:与常规的机器学习方法不同,大模型中很多数据集是自动划分了 train 和 test 的
    • 使用 split="train" 就可以获取到训练数据
    • 使用 split="test" 就可以获取到测试数据
    • 未做特殊说明时,大部分方法都是在默认的训练集上进行训练,使用测试集进行评估

附录:数据集的访问和下载

  • 一般来说,直接在 HuggingFace 上直接可以看到相关数据集

  • 下载opencompass/AIME2025(包含 AIME2025-I 和 AIME2025-II子集)并打印的示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    from datasets import load_dataset

    dataset = load_dataset("opencompass/AIME2025", 'AIME2025-I') # 'AIME2025-I' 是子数据集的名称,需要去huggingface上看看是否有子集
    # dataset = load_dataset("opencompass/AIME2025", 'AIME2025-II') # 'AIME2025-II' 是子数据集的名称,需要去huggingface上看看是否有子集

    # 检查是否存在 'train' split
    if 'train' in dataset:
    print(dataset['train'][0])

    # 检查是否存在 'test' split
    if 'test' in dataset:
    print(dataset['test'][0])

    ## output
    # {'question': 'Find the sum of all integer bases $b>9$ for which $17_{b}$ is a divisor of $97_{b}$.', 'answer': '70'}
  • 特别注意:

    • 部分数据集还有子集,此时下载需要指定子集,否则可能失败
    • 部分数据集需要先登录和授权才能下载

附录:评估/测试工具

LightEval

  • LightEval 是一个专注于 LLM 评估的开源工具库,由 Hugging Face 开发
  • 它提供了标准化的评估框架,库中包含了许多数据集,可以直接加载和评估
  • 传入数据集和 model_predict_fn 即可
    • 数据集可通过 lighteval 库的 load_dataset 来加载
    • model_predict_fn 需要自定义模型的 predict 函数

OpenCompass

  • OpenCompass 是上海人工智能实验室开源的大模型评测平台,涵盖学科、语言、知识、理解、推理等五大评测维度,可全面评估大模型能力
  • OpenCompass 支持在线查看榜单,在线参与评测竞技
  • OpenCompass 还开源了大模型评测工具,使用很简便,GitHub 地址:OpenCompass 项目
    • 使用教程:README.md
  • OpenCompass 比 LightEval 封装的更好,评估更加简单

RM 评估:RewardBench

  • 原始论文:(RewarBench)Evaluating Reward Models for Language Modeling, arXiv 20240608, Allen Institute for AI & University of Washington
  • HuggingFace: allenai/reward-bench
  • GitHub: allenai/reward-bench
  • RewardBench 是目前最权威、应用最广泛的奖励模型评估基准之一,解决了以往评估缺乏标准化和涵盖面不足的问题
  • RewardBench (v1): 由 AllenAI 推出,旨在通过统一的框架评估奖励模型
    • 它包含四个主要类别的测试数据:
      • Chat(通用对话)
      • Chat-Hard(高难度对话)
      • Safety(安全性)
      • Reasoning(推理能力)
  • 数据来源见原始论文表 1:

RewardBench2

  • 原始论文:REWARDBENCH 2: Advancing Reward Model Evaluation, 20250602

  • RewardBench2 是 RewardBench 基准的升级版本

    • v2 引入了更具挑战性的“多技能”评估维度,进一步测试 RM 在捕捉细微人类偏好和处理复杂逻辑时的表现
  • RewardBench2 包含的测试集有(相对 RewardBench 移除了 Chat 子集,新增了三个数据集并重新划分一些数据集):

    • Math: 数学能力,主要包含从中学的物理(physics)和几何学(geometry)到大学级别的化学(chemistry)、微积分(calculus)、组合数学(combinatorics)等
    • Safety: 安全性
    • Focus: 检查一般 Query 上的高质量回复能力

      Tests RMs’ ability to detect high-quality, on-topic answers to general user queries.

    • Ties (NEW!): 用一些回答为平手的数据检查模型评分的健壮性

      This new type of subset tests the robustness of RMs in domains with many possible similar answers. For example, the question “Name a color of the rainbow” has seven possible correct answers and infinitely many incorrect ones.

    • Factuality (NEW!): 模型幻觉检查
    • Precise Instruction Following (Precise IF,NEW!): 指令遵循能力

      such as “Answer without the letter u”.

  • 非 Ties 的子集,基本都是针对 ID 进行分组,多个样本一起体现一个组内的表现,评估是否最高分是 label=1 的样本

    • 当 N 样本都打了最高分,给分为 1/N
  • RewardBench2:Ties,这个数据集会在同一个 prompt 上给出很多等价的正确回答和错误回答,然后评估模型给出的分差 GAP

    • 目标是希望所有正确回答的分数 GAP(最高正确-最低正确)比 正负 GAP(最低正确-最高错误)的值要大
    • 不仅关注打分是否正样本大于负样本,还关注是否所有正样本比负样本都大得多
    • 取名为 Ties 的本意就是让模型能对平手的 回复打分方差不要太大
    • 举例:prompt=”Pick a number between 1 and 10.”,正确回答是 1-10 之间的数字,错误回答是其他数字,从而可以 check 模型的评估能力是否有问题
    • 样本示例:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      {
      "id": "tied:0",
      "target": "Eleven",
      "label": 0,
      "num_c": 10,
      "num_r": 12,
      "num_all": 22,
      "subset": "Ties",
      "eval_set": "reward_bench2",
      "messages": [
      {
      "role": "user",
      "content": "Pick a number between 1 and 10."
      }
      ]
      }
  • allenai/reward-bench 中有 RewardBench2 指标的具体统计实现


RM 评估:JudgeBench

  • JudgeBench: A Benchmark for Evaluating LLM-based Judges, arXiv 20241016 & ICLR 2025, Berkeley
  • HuggingFace:huggingface.co/datasets/ScalerLab/JudgeBench
  • JudgeBench 是一个极具挑战性的基准,专门用于评估模型作为“裁判”(LLM-as-a-Judge)的能力
  • 不同于简单的闲聊偏好,JudgeBench 侧重于评估模型在复杂推理任务中的表现,涵盖数学、编程、知识问答等领域
  • JudgeBench 要求奖励模型不仅要理解人类的主观偏好,还要具备判断客观事实正确性(Correctness)和逻辑严密性的能力
    • 这对于训练能够进行自我修正和长思维链(CoT)推理的模型尤为重要

RM 评估:PPE Preference

  • PPE Preference & Correctness
  • HuggingFace:
    • huggingface.co/datasets/Skywork/Skywork-Reward-Preference-80K-v0.1
    • huggingface.co/datasets/Skywork/Skywork-Reward-Preference-80K-v0.2
  • PPE(Prompt-Preference-Evaluation)相关的测试主要关注模型对真实人类偏好的理解深度
  • PPE 基准使用的数据通常源自真实用户的交互记录(而非合成数据),因此更能反映模型在实际应用场景(如 AI 助手)中的表现
  • PPE 的评估维度包含 Preference(偏好排序)和 Correctness(正确性判断)
  • 在 Skywork-Reward-V2 的评估中,PPE 被用来验证模型是否能区分(高质量)与(次优)数据,确保模型真正理解人类的意图和喜好
  • 该指标常在 Skywork (昆仑万维) 及相关 RLHF 研究中作为核心真实场景指标出现

RM 评估:RM-Bench

  • 原始论文:RM-Bench: Benchmarking Reward Models of Language Models with Subtlety and Style, 20241021, Fudan & THU & HKUST
  • 开源项目:github.com/THU-KEG/RM-Bench
  • RM-Bench 是一个专门针对奖励模型鲁棒性和一致性的评估基准
  • RM-Bench 通常关注模型在面对不同类型的输入干扰、对抗性样本或特定安全边界时的表现
  • RM-Bench 基准旨在揭示奖励模型在“过度优化”(Reward Hacking)风险下的稳定性,确保模型给出的高分确实对应高质量回复,而非利用了模型的漏洞
  • RM-Bench 的评估指标核心特点是聚焦“内容敏感性”与“风格偏见抗性”,以三级准确率梯度设计为核心,结合域特异性与跨域综合指标,且与策略模型性能强相关

基础评估框架:分类任务定义

  • 核心任务:给定三元组 \((x, y_c, y_r)\)(\(x\) 为提示词,\(y_c\) 为优选 Response ,\(y_r\) 为弃选 Response ),判断 \(R_\psi(x, y_c) > R_\psi(x, y_r)\)(\(R_\psi\) 为奖励模型,\(\psi\) 为模型参数)
  • 基础准确率公式:
    $$
    \text{Accuracy} = \frac{1}{|\mathcal{D}|} \sum_{(x, y_c, y_r) \in \mathcal{D} } \mathbb{I}\left[R_{\psi}(x, y_c) > R_{\psi}(x, y_r)\right]
    $$
    • 其中 \(\mathbb{I}(\cdot)\) 为指示函数,\(\mathcal{D}\) 为评估数据集;多目标奖励模型采用奖励向量逐元素比较

风格-实质分离评估:3×3 矩阵与三级准确率

  • 风格变体:涵盖三类 Response 风格——简洁型(\(y^\emptyset\))、详细纯文本型(\(y^L\))、详细带Markdown型(\(y^{L,M}\))
  • 评估矩阵:构建行(Chosen Response 风格)X 列(Rejected Response 风格)的 3x3 风格-实质矩阵,衍生三类核心指标:
    • 简易准确率(Easy Accuracy):矩阵下三角均值,衡量有风格提示时的内容识别能力
    • 正常准确率(Normal Accuracy):矩阵对角线均值,评估风格一致时的内容判断能力
    • 困难准确率(Hard Accuracy):矩阵上三角均值,测试弃选 Response 风格更优时的纯内容区分能力
  • 详情见原始论文图 2

域特异性与综合指标结合

  • 域特异性指标:针对聊天、代码、数学、安全四大核心域,分别计算各域的三级准确率(如 Chat Normal Accuracy、Math Hard Accuracy)
  • 跨域综合指标:计算所有域的平均准确率(Average Accuracy),作为奖励模型整体性能的统一衡量标准

与策略模型性能强关联

  • 风格控制相关性:奖励模型的 Chat 域 Hard Accuracy 与策略模型的风格控制得分显著正相关,能反映策略模型对风格偏见的抵抗能力
  • 下游任务相关性:与策略模型在数学(GSM8k、Big Bench Hard)、代码(HumanEval+、MBPP+)、安全(ToxiGen、XSTest)等下游任务的表现呈中度正相关,Pearson 相关系数 \(r=0.55\)(\(p=0.07\)),显著优于传统基准(如 RewardBench 的 \(r=0.21\))

RM 评估:RMB

  • RMB:Reward Model Benchmark
  • 原始论文:RMB: Comprehensively Benchmarking Reward Models in LLM Alignment, 20241013 & ICLR 2025, Fudan
  • RMB 是一个全面且细粒度的奖励模型评估基准,核心亮点是创新性和贴近现实应用
  • RMB 涵盖 49 个现实世界场景,核心目标:
    • 有用性目标场景(Helpfulness, 75.5%): 37 个场景的 12 个任务
      • 代码(Code)、开放问答(Open QA)、封闭问答(Closed QA)、翻译(Translation)、内容生成(Generation)、推理(Reasoning)、角色扮演(Role Playing)、文本改写(Rewrite)、分类(Classification)、聊天对话(Chat)、摘要总结(Summarization)、头脑风暴(Brainstorming)
    • 无害性目标场景(Harmlessness, 24.5%): 12 个场景,构建了超 18000 个高质量偏好对
      • 暴力相关(Violent Crimes)、非暴力相关(Non-Violent Crimes)、性相关违法(Sex-Related Crimes)、未成年人保护(Child Sexual Exploitation)、专业建议规范(Specialized Advice)、隐私保护(Privacy)、知识产权保护(Intellectual Property)、危险武器相关(Indiscriminate Weapons)、仇恨言论相关(Hate)、自我伤害相关(Suicide & Self-Harm)、色情内容相关(Sexual Content)、混合场景(Multi)
  • RMB 通过真实世界的用户查询构建具有实际意义的测试任务,并借助14个大型语言模型生成回复并评级,最终形成超18000个高质量偏好对,可充分检验奖励模型的场景泛化能力
  • 评估范式:
    • pairwise 评估范式:传统的成对比较评估模式
    • Best-of-N 评估范式:测试奖励模型从多个候选回复中挑选最佳回应的能力
      • 经实验验证,其评估结果与奖励模型在下游对齐任务中的表现关联性强,能揭示现有模型的泛化缺陷,同时还强调了生成式奖励模型的发展潜力
  • 数据集详细分布见原始论文附录 A
1…121314…61
Joe Zhou

Joe Zhou

Stay Hungry. Stay Foolish.

608 posts
49 tags
GitHub E-Mail
© 2026 Joe Zhou
Powered by Hexo
|
Theme — NexT.Gemini v5.1.4