NLP——LLM-RL训练指标记录

注:本文包含 AI 辅助创作


整体说明

  • 本文汇总 LLM-RL 训练过程中需要关注的各种指标
  • RL 训练指标重要性:
    • 宗旨:能在训练指标上体现的问题,都不该留到下游测试集上再体现
    • Insight:全面的指标监控有助于快速发现各种可能存在的隐藏 Bug 或 找到改进点

训练日志落盘

  • 训练时在每一步都落表下面的内容
    • Prompt
    • Response/Rollout(每个 Prompt 可能多个)
    • 真实 Rewards 打分情况(每个 Response/Rollout 至少一个,若多个 Reward Model/Rule 则有多个)
    • final_rewards 打分情况(包含长度惩罚等后的最终奖励)

KL 散度(当前策略 vs Reference 策略)

  • KL 散度是最常见的观测指标,有助于判断当前策略偏离 Reference 策略多少了
  • KL 散度的监控上报值往往是直接估计值
    • 注:KL 散度完整值需要对全词表积分,除了用于偶尔对齐指标或训推不一致问题排查外,一般不需要使用
    • KL 散度的近似估计方式有 k1,k2,k3 三种,建议监控 k1 和 k3,其中 k1 的值会更稳定些,k3 可能存在较多 spike
  • 经验:
    • 好的 KL 散度曲线应该是缓慢的逐步增长(若设定 Target KL 来动态调整 KL 惩罚系数,会逐步收敛到 Target KL 区间内)
    • 模型的崩溃往往伴随着 KL 散度的突然崩溃
      • 遇到 KL 突然崩溃时,一般其他指标也会随着崩溃,包括熵、Rewards 曲线等
      • 若 KL 突然崩溃,即使训练时的 Rewards 等暂时没有崩溃,大概率也出现问题了
  • 注意:在使用了 Actor Warmup 的场景中,可能看到比较特别的表现:
    • 可能看到第一步大家都是 0,但第二步的 KL 散度是 0,KL_loss 不为 0
      • KL 散度是 Rollout 后立刻算出来的,第一步模型学习率为 0,此刻模型还没更新,所以第二步看到的指标中 KL 散度依然是 0
      • KL_loss 是梯度步更新计算的,一旦学习率不为 0(第二步),有梯度步开始更新以后,KL_loss 散度就不在是 0 了
    • 补充:这个前提是针对 Rollout 步上报机制的,且 Actor Warmup 是根据 Rollout Step 计算的(整个 Rollout 步的多次 梯度步 使用相同学习率),比如 VeRL 就是这样的

Response 长度

  • Response 长度代表着模型对长度偏好的变化,是非常重要的关注指标
  • Response 相关的指标还有 8K 截断率、16K 截断率、32K 截断率等
  • 经验:
    • 回复长度可能会持平、缓慢上涨、缓慢降低,看具体策略而言可能不同

Entropy

  • 当前策略的熵代表当前模型的探索能力(熵越大,不确定性越高,探索能力越强)
  • 经验:
    • 好的熵没有特别固定的形状,但有一些参考:
      • 可能逐步降低然后收敛(收敛过快往往不是好事,说明模型过快丢失探索能力)
      • 可能先降低,再提升,再收敛(观察到有时候提升时往往伴随着长度的增加)

真实奖励 rewards/score(不包含 kl_rewards 等)

  • ORM 场景中,真实奖励仅出现在最后一个 Token,这个奖励可以看做是 Sequence 粒度的真实奖励(必须监控)
    • 注:这个真实奖励有时也叫做 scores 等
  • 若使用了 PRM,也建议使用详细的上报保证可监控(建议分 Step 进行上报和监控)
  • 针对不同领域同时训练的场景,应该分领域上报该值
  • 对于 Rewards 是 0-1 的场景,应该同步上报每个 Prompt 的正确率等,否则需上报 Rewards 的分布情况
  • 经验:
    • 好的 rewards/score 曲线应该是逐步上涨然后逐步收敛的
    • 若训练过程中突然下降,大概率是模型崩溃了(此时往往伴随着 KL 散度崩溃)

loss 类

Actor Loss

  • Actor Loss 本身不太具备特殊含义,比如 REINFORCE 中的目标函数/损失函数更多只是为了凑出梯度来
  • 经验:
    • Actor Loss 这个值可能是波动的,看不到收敛迹象,但不太会逐步提升
    • 若 Actor Loss 先稳定,然后突然提升, 那么大概率是在这里发生模型崩溃了
    • 在 GRPO/PPO 中,actor loss 跟 advantages 的均值呈现反比(绝对值不同,但是趋势相反),因为 大约有如下公式(忽略 clip 等,不同算法略有不同,但基本差不多)
      $$ \text{loss} \approx - \text{advantages} \times \frac{\pi_\theta}{\pi_{\theta_\text{old}}} $$

Critic Loss

  • Critic Loss 代表了 Critic 模型拟合状态价值函数的能力
  • 经验:
    • 这个值在 Pretrain 阶段是大幅下降的,然后逐步收敛,在 Actor 和 Critic 交叉训练过程中保持收敛状态
    • 若在 Actor 和 Critic 交叉训练过程中, Critic Loss 逐步升高或明显高于 Pretrain 阶段的收敛值,说明当前 Critic 学习速度跟不上 Actor,需要 降低 Actor 学习率 or 提升 Critic 学习率 or 增加 Critic 训练步数(一步 Actor 梯度更新对应多步 Critic 梯度更新)

KL Loss

  • 与 kl 散度本质一致,更多是计算 k2 或 k3

Entropy Loss

  • 与 Entropy 指标一致
  • Rewards 曲线一般直接上报样在进行 Advantages 计算前的奖励情况,一般会上报多个相关值

NLL Loss

  • 仅对 reward > 0 的 token 计算 -mean(action_logprobs * nll_loss_mask),再乘以 rewards_mean,相当于仅更新 reward > 0 的 token,且按照 rewards_mean 加权的交叉熵损失(或负对数似然)更新

MTP Loss

  • MTP Loss

PPO PG IS Clip 相关

  • 重要性采样权重 Clip 相关的各种情况和比例

训推不一致监控和修正各种指标

  • 训推不一致的监控指标
    • training_ppl(包括 log_training_ppl
    • rollout_ppl(包括 log_rollout_ppl
    • training_rollout_ppl_diff(包括 training_rollout_log_ppl_diff
      • training_rollout_log_ppl_diff:一般是统计 Sequence 内部 Token 的 log_probs 均值作为 log_ppl
      • 所以 training_rollout_log_ppl_diff 和下面的 training_rollout_kl(k1) 可能完全相同(若 training_rollout_kl 也是先计算,公式上可看出是同一个值)
        • 注:与 training_rollout_kl(k3) 不同,但趋势几乎一致
    • training_rollout_kl(包含 k1,k3 都可以同时上报,这个值在一些论文中也称为类似 vllm_kl 的命名)
      • 经验:training_rollout_kl 指标在不同模型/显卡上表现不同:
        • 在 GPU H200 卡,Dense 20B 左右的小模型上几乎没有 spike(但训练过程中从 1.2e-3 提升到 2.1e-3 水平,说明这个指标除了与硬件、参数量、模型架构、引擎实现有关外,还与模型自身的输出分布有关)
        • 在 NPU 910B 卡,MoE 500B-A30B 左右的大模型上有许多 spike(训练过程中从 1.5e-3 级别会 spike 到 1.5e-2 级别,甚至偶尔到 5e-2 级别,但不会逐步提升,与 Dense 20B 不同)
  • 极端 Token(Rollout 或 Training 引擎上的低概率 Token 比例,一般如 < 1e-3 等)
  • IS(Importance Sampling)修复系数的统计(均值、标准差等)
    • Sequence-level 和 Token-level 的
  • RS(Rejection Sampling)过滤系数的统计(均值、标准差等)
    • 注:被拒绝等价于被 Mask
    • 按照配置上报,也可以同时上报下面三种:
      • Token-level:按照 Sequence-level 作为过滤粒度,Token 粒度的 IS 比例作为判断依据
      • Sequence-level:按照 Sequence-level 作为过滤粒度,序列内所有 Token 系数的乘积(联合概率分布)作为判断依据
      • Geometric(Sequence-level):按照 Sequence-level 作为过滤粒度,序列内所有 Token 系数的集合平均作为判断依据

Advantages

  • Advantages 表示了 Token 的打压 or 鼓励情况,是最关键的指标之一
  • PPO 的 Advantages 分为 Batch Norm 归一化前和归一化后的,要分别监控
  • 经验:
    • Advantages 的上报(比如均值,最大值等)应该完整符合推导结果(比如均值为 0, 最大值不能超过理论上限等)
    • PPO 中未经过归一化的 Advantages 均值也应该几乎贴近于 0,若大于 0 则大概率是 Critic 还没收敛

Gradient Norm

  • 这个指标需要从底层 Infra 去统计并上报
  • 经验:Gradient Norm 一开始会比较大(可能会涨一下),然后逐步下降
  • 若训练崩溃,则 Gradient Norm 会突然变的非常大,所以 Gradient Norm 变大要非常小心

附录:遇到过的各种指标异常现象总结

PPO 训练过程中 \((\pi_\theta - \pi_{old})\) 逐步增大意味着什么?

  • 首先:对于每个 Rollout Step,第一个梯度 Step 的 \((\pi_\theta - \pi_{old})\) 始终为 0
    • 因为在每次 Rollout 结束后,当前策略网络 \(\pi_\theta\) 的权重与用于采样的旧策略网络 \(\pi_{old}\) 完全一致
  • 若观察到随着 Rollout 步数(或训练 Epoch)的增加,第二个梯度 Step 的 \((\pi_\theta - \pi_{old})\) 的方差越来越大,这是一个需要警惕的信号
    • 通常意味着单次梯度更新对策略产生了越来越剧烈且不均匀的扰动
  • 注:此时 PPO 训练是使用了 Advantages Batch Normalization 的
    • 即:无论 Reward Model 给出的分数多么离谱,无论原始的 TD Error 方差有多大,输入到 Loss 函数中的 Advantage 的均值严格为 0,方差严格为 1
可能问题1:优势函数(Advantage)的方差在急剧扩大(若没有进行 Advantages Batch Normalization )
  • 在 PPO 中,策略更新的梯度方向和步长主要由优势函数 \(A(s,a)\) 决定
  • 若没有进行 Advantages Batch Normalization,且 \((\pi_\theta - \pi_{old})\) 的方差变大,说明在同一个 Batch 内,某些 Token 的概率被剧烈推高,而另一些被剧烈压低
  • 问题发生的链路:
    • 随着 Rollout 的进行,策略模型逐渐偏离初始的 SFT 模型,开始探索到奖励模型未曾见过的状态(OOD 数据)
      • 这会导致 RM 输出极端的、充满噪声的奖励(即已经发生了 Reward Hacking)
    • 进一步导致Critic 无法准确拟合这些极端的奖励,导致计算出的 Advantage 出现极大的方差
    • 高方差的 Advantage 直接导致了高方差的梯度更新
  • 可能导致这一点的原因包含:
    • KL Penalty 不足,导致模型偏离 Reference Model 太多,从而进入 RM 的 OOD 区间,打分开始变得激进且误差大
      • 实验验证:完全关闭 KL Penalty 后,Advantages 的方差 和 \((\pi_\theta - \pi_{old})\) 方差
  • 解法:增加 KL Penalty 约束,防止模型发生 Reward Hacking
可能的问题2:Critic 跟不上 Actor 的更新
  • Critic 的任务是预测当前状态的价值 \(V(s)\)
  • 如果 Actor 的策略变化过快,或者 Critic 的学习率/拟合能力不足,Critic 的预测就会产生滞后
  • 不准确的 \(V(s)\) 会导致 Advantage 估计错误
    • 随着 Rollout 步数增加,这种误差会累积,导致 Actor 在第二步更新时接收到极其嘈杂的梯度信号(有偏的,有误差的信号),从而表现为更新幅度的方差增大
  • 解法:提升 Critic 学习率、降低 Actor 学习率,或者让 Critic 多更新几步

GRPO 训练时 Advantage 均值不为 0

  • Advantage 的均值上报逻辑为:经过组内归一化,再按照序列平均(Token 相等,平均前后值不变),最后在 Sequence 间做归一化
    • 理论上,这个值肯定为 0,因为组内均值为 0,多个组均值也应该为 0
  • 发生问题的原因:
    • 当同一个组的 rewards 完全相同且是无理数时,可能因为精度问题导致组内归一化 Bug
      • 自身减去组内均值不为 0,是一个相同的 1e-8 量级的正数或者负数(具体正负和原始 rewards 值有关)
      • 1e-8 量级的数再除以 (std + 1e-8) 得到在 1 左右的 Advantage(且同一组内所有 Token 值相等)
  • 可选的解法(亲测:std 相关的解法本质都是避免针对过小的 std 组做过大的更新,这类解法可以大幅缩小训练过程中的 kl spike):
    • mask std 过小的组
    • 提升 std 的兜底值
    • 不使用 std 作为分母(如 Dr.GRPO)
    • (精确修复)提升归一化计算的精度(默认 torch 使用 float32,可以切换为 float64 计算)

附录:VeRL 框架上报指标汇总(20260423)

  • 本次梳理 commit_id 为:6daa6e0c1626567a90454a9a79076b30d34d4d8b
  • VeRL 所有指标最终通过下面代码统一上报到 wandb/swanlab 等实验跟踪后端
    1
    logger.log(data=metrics, step=self.global_steps)

Actor 相关指标

actor/pg_loss,策略梯度损失
  • 文件:verl/workers/actor/dp_actor.py:477

  • compute_policy_loss_* 系列函数计算后返回,以标准 PPO(vanilla)为例:

    1
    2
    3
    4
    5
    # 令 ratio = exp(log_prob - old_log_prob)(当前策略与旧策略的概率比)
    pg_losses1 = -advantages * ratio
    pg_losses2 = -advantages * clip(ratio, 1 - ε_low, 1 + ε_high)
    pg_losses= max(pg_losses1, pg_losses2) #(带 dual-clip 时取 min 与 c·A)
    pg_loss = agg_loss(pg_losses, response_mask, loss_agg_mode)
  • 其中 agg_loss 支持四种聚合模式(由配置决定):

    • token-mean:所有有效 token 的均值
    • seq-mean-token-sum:先对序列内的 token 求和,再对序列取均值
    • seq-mean-token-mean:先对序列内的 token 取均值,再对序列取均值
    • seq-mean-token-sum-norm:先对序列内的 token 求和,再除以最大响应长度(然后对序列取均值)
actor/pg_clipfrac,策略梯度上裁剪比例
  • 文件:verl/workers/actor/dp_actor.py:478

    1
    pg_clipfrac = masked_mean(pg_losses2> pg_losses1, response_mask)
    • ratio > 1 + ε_high 时被裁剪的token 比例(正优势方向被clip 的占比)
actor/pg_clipfrac_lower,策略梯度下裁剪比例(dual-clip)
  • 文件:verl/workers/actor/dp_actor.py:480

    1
    2
    3
    4
    pg_clipfrac_lower = masked_mean(
    (clip_pg_losses1 > pg_losses3) * (advantages< 0).float(), response_mask
    )
    # 其中 pg_losses3 = -advantages * clip_ratio_c(默认 c=3.0)
  • 仅在 advantages < 0(负优势)时,因 dual-clip 机制触发的 token 比例

    • GSPO/GPG/KL-Cov 等方法此项固定为 0
actor/ppo_kl,近似 KL 散度
  • 文件:verl/workers/actor/dp_actor.py:479

    1
    2
    3
    negative_approx_kl = clamp(log_prob - old_log_prob, -20, 20)
    ppo_kl = masked_mean(-negative_approx_kl, response_mask)
    = masked_mean(old_log_prob - log_prob, response_mask)
  • 这是 KL(π_old ‖ π) 的 token 级均值估计(一阶近似)

    • kl_cov 变体中,上报的是绝对值均值 masked_mean(|log_prob - old_log_prob|, response_mask)
actor/entropy,策略熵
  • 文件:verl/trainer/ppo/ray_trainer.py:1189

  • 在 recompute old_log_probs 阶段从 Actor 的 forward 中获取:

    1
    2
    token_entropy = logsumexp(logits) - sum(softmax(logits) * logits) # 得到每个 token 的熵,与熵的原始定义等价
    entropy_agg = agg_loss(entropys, response_mask, loss_agg_mode)
    • token_entropy 是 Token 粒度的熵
    • entropy_agg 是对 token 粒度的熵按指定 loss 聚合模式计算的标量值(这个计算公式和)
    • 理解:
      • 原始的计算公式中,直接对 softmax 结果取 log 在概率接近 0 时会出现 -inf
      • 这里用 logsumexp (内部用 max-shift 技巧稳定计算)和 pd * logitslogits 本身就是有限值)避免了 log(0) 问题
      • 整个计算流仅涉及稳定的算子,适合大规模训练中的低精度/混合精度场景
    • 注:在新版本的 verl.utils.torch_functional.entropy_from_logits 中,定义函数如下:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      def entropy_from_logits(logits: torch.Tensor) -> torch.Tensor:
      """Calculate Shannon entropy from unnormalized logits.

      Computes H(p) = -sum(p * log(p)) using the numerically stable formula:
      entropy = logsumexp(logits) - sum(softmax(logits) * logits)

      Args:
      logits: Unnormalized log-probabilities of shape (..., vocab_size).

      Returns:
      torch.Tensor: Entropy values with shape (...,), one per distribution.
      """
      pd = torch.nn.functional.softmax(logits, dim=-1)
      entropy = torch.logsumexp(logits, dim=-1) - torch.sum(pd * logits, dim=-1)
      return entropy
  • 补充:上述 token_entropy 的公式推导:
    $$
    \begin{align}
    \text{Entropy}
    &= -\sum_i p_i \log p_i \\
    &= -\sum_i p_i \log\left( \frac{e^{z_i}}{\sum_j e^{z_j}} \right) \\
    &= -\sum_i p_i \left( z_i - \log\sum_j e^{z_j} \right) \\
    &= -\sum_i p_i z_i + \log\sum_j e^{z_j} \cdot \sum_i p_i \\
    &= -\sum_i p_i z_i + \log\sum_j e^{z_j} \\
    &= \text{logsumexp}(z) - \sum_i \left( \text{softmax}(z)_i \cdot z_i \right)
    \end{align}
    $$

actor/kl_loss,KL 损失(有条件上报)
  • 文件:verl/workers/actor/dp_actor.py:465

  • 仅当 config.use_kl_loss = True 时上报:

    1
    2
    kld = kl_penalty(log_prob, ref_log_prob, kl_penalty_type)
    kl_loss = agg_loss(kld, response_mask, loss_agg_mode) * loss_scale_factor
    • 其中 kl_penalty_type 支持多种估计方式(详见 KL 惩罚计算
actor/kl_coef,KL 损失系数(有条件上报)
  • 文件:verl/workers/actor/dp_actor.py:466

    1
    actor/kl_coef = config.kl_loss_coef
    • 仅当 use_kl_loss = True 时上报,为超参数常量
actor/reward_kl_penalty,奖励中的 KL 惩罚均值
  • 文件:verl/trainer/ppo/ray_trainer.py:191

  • 仅当 algorithm.use_kl_in_reward = True 时,在 apply_kl_penalty 函数中计算:

    1
    2
    3
    4
    kld = kl_penalty(old_log_probs, ref_log_prob, kl_penalty_type)# token level
    kld = kld * response_mask
    current_kl = mean(masked_mean(kld, response_mask, axis=-1)) # 先对序列内平均,再对 batch 平均
    actor/reward_kl_penalty = current_kl
  • 最终 token 级奖励 = token_level_scores - beta * kld

actor/reward_kl_penalty_coeff, KL 惩罚自适应系数
  • 文件:verl/trainer/ppo/ray_trainer.py:191

    1
    actor/reward_kl_penalty_coeff = kl_ctrl.value #(beta,自适应调整)
  • 实际实现时是设定一个 kl 目标,若未达到目标,则通过提升或降低 kl_coef 来调整模型朝达到目标的方向走

actor/grad_norm,Actor 梯度范数
  • 文件:verl/workers/actor/dp_actor.py:486

    1
    actor/grad_norm = clip_grad_norm_(actor_module, max_grad_norm).item()
  • _optimizer_step() 在梯度裁剪时返回

actor/lr,Actor 学习率
  • 文件:verl/workers/fsdp_workers.py:727 / verl/workers/megatron_workers.py:610
    1
    actor/lr = actor_lr_scheduler.get_last_lr()[0]
perf/mfu/actor,Actor MFU(模型浮点利用率)
  • 文件:verl/workers/fsdp_workers.py:719
    1
    2
    estimated_flops = flops_counter.estimate_flops(global_num_tokens, delta_time)
    perf/mfu/actor = estimated_flops * ppo_epochs / promised_flops / world_size

Critic 相关指标

critic/vf_loss,价值函数损失
  • 文件:verl/workers/critic/dp_critic.py:254

    1
    2
    3
    4
    5
    vpredclipped = clip(vpreds, values - ε_v, values + ε_v)
    vf_losses1 = (vpreds - returns)**2
    vf_losses2 = (vpredclipped - returns)**2
    clipped_vf_losses = max(vf_losses1, vf_losses2)
    vf_loss = 0.5 * agg_loss(clipped_vf_losses, response_mask, loss_agg_mode) * loss_scale
    • 这个设计比传统 RL 中 PPO 算法的更优,详细分析可参考:NLP——LLM对齐微调-RLHF,简单理解如下:
      • 1)如果模型预期更新幅度大且之前已经改进过模型了(模型已经更贴近当前目标了) ,它会切断梯度(Loss 取 clipped_value_loss),让模型 “稳一点,别急”
      • 2)如果模型更新更新幅度大且之前往反方向更新过模型了(模型已经更远离当前目标了),它保留梯度(Loss 取 value_loss),让模型 “赶紧改错”
      • 3)如果模型在安全范围内更新,它就是普通的 MSE Loss
critic/vf_clipfrac,价值函数裁剪比例
  • 文件:verl/workers/critic/dp_critic.py:255

    1
    vf_clipfrac = masked_mean(vf_losses2 > vf_losses1, response_mask)
  • 即被 clip 的价值预测所占 token 比例

critic/vpred_mean,价值预测均值
  • 文件:verl/workers/critic/dp_critic.py:256

    1
    critic/vpred_mean = masked_mean(vpreds, response_mask)
  • 当前 mini-batch 中,Critic 对 response token 的价值预测均值

critic/grad_norm,Critic 梯度范数
  • 文件:verl/workers/critic/dp_critic.py:263
    1
    critic/grad_norm = clip_grad_norm_(critic_module, max_grad_norm).item()
critic/lr,Critic 学习率
  • 文件:verl/workers/fsdp_workers.py:1307 / verl/workers/roles/critic.py:178
    1
    critic/lr = critic_lr_scheduler.get_last_lr()[0]
perf/mfu/critic,Critic MFU
  • 文件:verl/workers/fsdp_workers.py:1304 / verl/workers/roles/critic.py:176
    1
    perf/mfu/critic = estimated_flops * ppo_epochs / promised_flops / world_size

奖励/优势/回报统计指标(compute_data_metrics

  • 这些指标在 verl/trainer/ppo/metric_utils.py:compute_data_metrics 中统一计算
得分(Score)指标
  • 具体指标:
    指标 计算方式
    critic/score/mean mean(token_level_scores.sum(-1)[non_aborted]),非中止样本的序列级得分均值,注意是对 Sequence 内部的 Token 做 sum
    critic/score/max max(token_level_scores.sum(-1)[non_aborted])
    critic/score/min min(token_level_scores.sum(-1)[non_aborted])
  • 注:token_level_scores 是 reward function 输出的原始得分(未减去 KL 惩罚)
奖励(Reward)指标
  • 具体指标
    指标 计算方式
    critic/rewards/mean mean(token_level_rewards.sum(-1)[non_aborted]),含KL 惩罚后的实际奖励均值
    critic/rewards/max max(token_level_rewards.sum(-1)[non_aborted])
    critic/rewards/min min(token_level_rewards.sum(-1)[non_aborted])
  • 注:token_level_rewards = token_level_scores - beta * kld(若开启 KL penalty)
优势值(Advantages)指标
  • 具体指标
    指标 计算方式
    critic/advantages/mean mean(advantages[response_mask]),所有有效 response token 的优势值均值
    critic/advantages/max max(advantages[response_mask])
    critic/advantages/min min(advantages[response_mask])
回报(Returns)指标
  • 具体指标:
    指标 计算方式
    critic/returns/mean mean(returns[response_mask]),有效 token 的回报均值(GAE 计算结果)
    critic/returns/max max(returns[response_mask])
    critic/returns/min min(returns[response_mask])
价值预测(Values)指标(仅 use_critic=True 时)
  • 具体指标
    指标 计算方式
    critic/values/mean mean(values[response_mask]),Critic 旧价值预测均值
    critic/values/max max(values[response_mask])
    critic/values/min min(values[response_mask])
    critic/vf_explained_var 1 - var(returns - values) / (var(returns) + 1e-5),Critic 价值函数解释方差,越接近 1 表示价值估计越准确

序列长度相关指标

  • 均在 verl/trainer/ppo/metric_utils.py:compute_data_metrics 中计算
    指标 计算方式
    response_length/mean mean(response_mask.sum(-1).float()),所有样本 response token 数均值
    response_length/max max(response_length)response_length=response_mask.sum(-1).float()
    response_length/min min(response_length)
    response_length/clip_ratio mean(response_length == max_response_length),达到最大长度被截断的样本比例
    response_length_non_aborted/mean 仅统计 response_length > 0 的样本的response 长度均值
    response_length_non_aborted/max 同上,取最大值
    response_length_non_aborted/min 同上,取最小值
    response_length_non_aborted/clip_ratio 非中止样本中达到最大长度的比例
    response/aborted_ratio mean(response_length == 0),响应长度为 0(中止/异常)的样本比例
    prompt_length/mean mean(prompt_mask.sum(-1).float()),prompt token 数均值
    prompt_length/max max(prompt_length)
    prompt_length/min min(prompt_length)
    prompt_length/clip_ratio mean(prompt_length == max_prompt_length),prompt被截断的比例

多轮对话相关指标(按条件展示)

  • 当 batch 中存在 __num_turns__ 字段时上报(多轮对话场景):
    指标 计算方式
    num_turns/mean num_turns.mean(),每条样本的对话轮数均值
    num_turns/max num_turns.max()
    num_turns/min num_turns.min()
  • 当 batch 中存在 tool_call_counts 字段时上报(工具调用场景):
    指标 计算方式
    tool_call_counts/mean tool_call_counts.mean()
    tool_call_counts/max tool_call_counts.max()
    tool_call_counts/min tool_call_counts.min()

时序性能指标

  • verl/trainer/ppo/metric_utils.pycompute_timing_metricscompute_throughout_metrics 中计算
各阶段耗时(timing_s/*
  • 具体逻辑:

    1
    timing_s/{stage_name} = 该阶段实际执行时间(秒)
  • 阶段名称包括:

    • gen(生成)
    • ref(参考策略前向)
    • values(价值估计)
    • adv(优势计算)
    • update_critic(Critic 更新)
    • update_actor(Actor 更新)
    • reward(奖励计算)
    • old_log_prob(log prob 重计算)
    • testing(验证)
    • save_checkpoint(保存检查点)
    • step(总步时间)等
每 token 耗时(timing_per_token_ms/*
  • 具体计算逻辑:

    1
    2
    timing_per_token_ms/gen = timing_s["gen"] * 1000 / num_response_tokens
    timing_per_token_ms/{其他阶段} = timing_s[stage] * 1000 / (num_prompt_tokens + num_response_tokens)
  • 注:gen 阶段只用 response token 数作分母,其他阶段用全序列 token 数

吞吐量与性能指标

  • 吞吐量相关指标:
    指标 计算方式
    perf/total_num_tokens sum(batch.meta_info["global_token_num"]),本step 处理的 token 总数
    perf/time_per_step timing_raw["step"],每个训练步的总耗时(秒)
    perf/throughput total_num_tokens / (time * n_gpus),每 GPU 每秒处理的 token 数
    perf/max_memory_allocated_gb torch.device.max_memory_allocated() / 1024³,GPU峰值显存分配(GB)
    perf/max_memory_reserved_gb torch.device.max_memory_reserved() / 1024³,GPU 峰值显存预留(GB)
    perf/cpu_memory_used_gb psutil.virtual_memory().used / 1024³,CPU 内存占用(GB)

训练状态指标

  • verl/trainer/ppo/ray_trainer.py:1343 处直接赋值:
    指标 计算方式
    training/global_step 当前全局训练步数 self.global_steps
    training/epoch 当前 epoch 编号

批次负载均衡指标(按条件展示)

  • trainer.balance_batch = True 时,在 _balance_batch 方法中调用 log_seqlen_unbalanceverl/utils/seqlen_balancing.py:194):
    指标 计算方式
    global_seqlen/min 各DP rank 的序列长度之和中的最小值(均衡前)
    global_seqlen/max 各 DP rank 的序列长度之和中的最大值(均衡前)
    global_seqlen/minmax_diff max - min(不均衡程度,越小越好)
    global_seqlen/balanced_min 均衡后各 rank 的序列长度最小值
    global_seqlen/balanced_max 均衡后各 rank 的序列长度最大值
    global_seqlen/mean 各 rank 序列长度总和的均值

Debug 调试指标(按照条件展示)

  • 当 batch 中存在 rollout_log_probs 字段时(开启 rollout vs actor prob 对比),在 verl/utils/debug/metrics.py:calculate_debug_metrics 中计算:
    指标 计算方式
    training/rollout_probs_diff_valid 固定为 1,表示输入有效
    training/rollout_probs_diff_max `max(
    training/rollout_probs_diff_mean `mean(
    training/rollout_probs_diff_std `std(
    training/rollout_actor_probs_pearson_corr corrcoef(actor_probs, rollout_probs)[response_mask],Pearson 相关系数,参考 arXiv:2506.13585

验证集指标(按条件展示)

  • _validate() 方法中,通过 process_validation_metrics 函数处理后以多种统计形式上报:

    1
    2
    3
    4
    5
    6
    7
    8
    val-core/{data_source}/{var_name}/mean@N 		# N 个 rollout 的均值
    val-core/{data_source}/{var_name}/std@N # N 个 rollout 的标准差
    val-core/{data_source}/{var_name}/best@N/mean # bootstrap 采样最优值的均值(pass@N变体)
    val-core/{data_source}/{var_name}/best@N/std # bootstrap 采样最优值的标准差
    val-core/{data_source}/{var_name}/worst@N/mean # bootstrap 采样最差值的均值
    val-core/{data_source}/{var_name}/worst@N/std # bootstrap 采样最差值的标准差
    val-core/{data_source}/{var_name}/maj@N/mean # 多数投票结果的均值(如有 pred字段)
    val-core/{data_source}/{var_name}/maj@N/std # 多数投票结果的标准差
  • 其中 N 以 2 的幂次递增(2, 4, 8, …, n_resps),bootstrap 采样 1000 次。val-aux/ 前缀同理,还额外包含:

    • val-aux/num_turns/mean|max|min(多轮验证时)

补充:KL 散度计算方式汇总

  • kl_penalty 函数(core_algos.py:1272)支持多种 KL 估计变体:
    类型 公式
    kl / k1 logprob - ref_logprob(一阶线性近似)
    abs `
    mse / k2 0.5 * (logprob - ref_logprob)²
    low_var_kl / k3 exp(clamp(ref - log, -20, 20)) - (ref - log) -1(低方差估计,来自 Schulman 2020)

VeRL 中的训推不一致相关指标

符号约定

  • 为统一定义公式方便后续表达,这里我先约定以下符号:
    符号 含义 verl 原始对应代码
    \(B\) batch size
    \(T\) 序列长度
    \(M\in\{0,1\}^{B\times T}\) 矩阵,response_mask,valid token 掩码 response_mask
    \(\pi_{\text{tr} }\) 训练策略(如 FSDP FP32)
    \(\pi_{\text{ro} }\) 采样策略(如 vLLM BF16)
    \(a_{i,t}=\log\pi_{\text{tr} }(y_t\mid y_{ < t})\) 训练侧 log 概率,部分开发框架可能叫做 action_log_prob old_log_prob
    \(r_{i,t}=\log\pi_{\text{ro} }(y_t\mid y_{ < t})\) 采样侧 log 概率 rollout_log_prob
    \(\ell_{i,t}=a_{i,t}-r_{i,t}=\log\rho_{i,t}\) log 重要性比 log_ratio
    \(\rho_{i,t}=e^{\ell_{i,t} }=\pi_{\text{tr} }/\pi_{\text{ro} }\) 重要性比 raw_rollout_is_weights
    \(\tau_u,\tau_l\) IS 上下/下界阈值 rollout_is_threshold_upper/lower
    \(N_i=\sum_t M_{i,t}\) 第 \(i\) 序列有效 token 数
  • 带掩码算子:
    $$
    \begin{align}
    \text{mMean}(x,M)&=\frac{\sum_{i,t}M_{i,t}x_{i,t} }{\sum_{i,t}M_{i,t}+10^{-8} }\\
    \text{mSum}_i(x,M)&=\sum_t M_{i,t}x_{i,t}\\
    \text{mMean}_i(x,M)&=\frac{\text{mSum}_i(x,M)}{N_i+10^{-8} }
    \end{align}
    $$
  • 注意:全部指标最终在 ./verl/trainer/ppo/rollout_corr_helper.compute_rollout_correction_and_rejection_mask 中统一加 rollout_corr/ 前缀
    • 也就是说:所有的训推不一致相关指标都在 rollout_corr/ 这个 前缀下

Off-policy 诊断指标

  • 代码见:./verl/trainer/ppo/rollout_corr_helper.compute_offpolicy_metrics
  • 这一组指标最直接刻画训推不一致 ,无论是否启用 IS/RS 都会计算
  • 假设 Sequence \(i\) 的平均 log 概率:
    $$\bar{a}_i=\text{mMean}_i(a,M),\quad \bar{r}_i=\text{mMean}_i(r,M)$$
训练策略 PPL
  • 序列级训练困惑度定义为:\(PPL_{tr}^{(i)} = \exp(-\bar{a}_i)\)
  • 总体平均指标:
    指标 公式
    training_ppl \(\displaystyle \frac{1}{B}\sum_{i=1}^B \exp(-\bar{a}_i)\)
    training_log_ppl \(\displaystyle \frac{1}{B}\sum_{i=1}^B (-\bar{a}_i)\)
采样策略 PPL
  • 序列级采样困惑度定义为:\(PPL_{ro}^{(i)} = \exp(-\bar{r}_i)\)
  • 总体平均指标:
    指标 公式
    rollout_ppl \(\displaystyle \frac{1}{B}\sum_{i=1}^B \exp(-\bar{r}_i)\)
    rollout_log_ppl \(\displaystyle \frac{1}{B}\sum_{i=1}^B (-\bar{r}_i)\)
KL 散度估计
  • 直接估计量
    $$\texttt{kl}=\text{mMean}(r-a,M)=\mathbb{E}_{\text{token} }\left[\log\frac{\pi_{\text{ro} } }{\pi_{\text{tr} } }\right]\approx\text{KL}(\pi_{\text{ro} }\Vert\pi_{\text{tr} })$$
    • 正值表示采样策略比训练策略”更自信”(即训推不一致,训练侧分配概率偏低)
  • K3 KL 估计量(Schulman k3,小 KL 下更稳定)
    $$\texttt{k3_kl}=\text{mMean}\left(e^{\ell}-\ell-1,M\right)=\mathbb{E}_{\text{token} }[\rho-\log\rho-1]\approx\text{KL}(\pi_{\text{ro} }\Vert\pi_{\text{tr} })$$
序列级 log-PPL 差
  • 定义 Sequence \(i\) 的差值 \(\Delta_i=\bar{r}_i-\bar{a}_i=\log\frac{\text{PPL}_{\text{tr} }^{(i)} }{\text{PPL}_{\text{ro} }^{(i)} }\):
    指标 公式
    log_ppl_diff \(\displaystyle \frac{1}{B}\sum_i \Delta_i=\mathbb{E}\left[\log\frac{\text{PPL}_{\text{tr} } }{\text{PPL}_{\text{ro} } }\right]\)
    log_ppl_abs_diff \(\displaystyle \frac{1}{B}\sum_i \lvert\Delta_i\rvert\)
    log_ppl_diff_max \(\max_i \Delta_i\)
    log_ppl_diff_min \(\min_i \Delta_i\)
PPL 比值
  • 序列级 PPL 比值 定义 ppl_ratio_i
    • 对于第 \(i\) 个序列,定义其 PPL 比值为:
      $$
      \text{ppl_ratio_i} = \exp(\Delta_i) = \frac{PPL_{tr}^{(i)} }{PPL_{ro}^{(i)} }
      = \exp(\bar{r}_i - \bar{a}_i)
      $$
  • 几何重要性采样权重
    • 序列级的几何平均 IS 权重定义为每个 token 重要性比 \(\rho_{i,t} = e^{\ell_{i,t} }\) 的几何平均:
      $$
      \text{geometric_is_i} = \left( \prod_{t:M_{i,t}=1} \rho_{i,t} \right)^{\frac{1}{N_i} }
      = \exp\left( \frac{1}{N_i} \sum_{t} \ell_{i,t} \right)
      = \exp(\bar{a}_i - \bar{r}_i)
      $$
      • 几何平均相对直接连乘最大的优点是方差极小,且方差随着序列长度的增加而减小
  • 倒数关系
    • 由上两式直接得到:
      $$
      \text{ppl_ratio_i} = \frac{1}{\text{geometric_is_i} }
      $$
    • 注:这与原始代码注释 ppl_ratio_i = 1 / geometric_is_i 完全一致
  • PPL 比值 总体平均 ,上报指标为 rollout_corr/ppl_ratio
    • 所有序列的平均 PPL 比值定义为 ppl_ratio
      $$
      \texttt{ppl_ratio} = \frac{1}{B} \sum_i \text{ppl_ratio_i}
      = \frac{1}{B} \sum_i \exp(\Delta_i)
      = \mathbb{E}\left[\frac{PPL_{tr} }{PPL_{ro} }\right]
      $$
    • 总体 PPL 比值 rollout_corr/ppl_ratio 的物理意义
      • 当训推分布一致时,\(\text{geometric_is_i} \approx 1\),故 \(\text{ppl_ratio_i} \approx 1\)
      • 若训练侧平均概率低于采样侧(\(\bar{a}_i < \bar{r}_i\)),则几何 IS 权重小于 1,PPL 比值(rollout_corr/ppl_ratio)大于 1
\(\chi^2\) 散度(衡量 IS 权重方差,越大越 off-policy)
  • token 级 \(\chi^2\) 散度 chi2_token
    $$\texttt{chi2_token}=\text{mMean}(\rho^2,M)-1=\mathbb{E}_{\text{token} }[\rho^2]-1=\chi^2(\pi_{\text{tr} }\Vert\pi_{\text{ro} })$$
    • 其中:
      $$\rho=e^\ell=\frac{\pi_{\text{tr} }}{\pi_{\text{ro} }}$$
      • 再次注明:\(\ell_{i,t}=a_{i,t}-r_{i,t}=\log\rho_{i,t}\) 是 log 重要性比(log_ratio
    • 理解:
      • chi2_token 在衡量 Token 粒度的 比值的平方与 1 之间的差异,这个值为 0 时说明没有训推没有任何误差
      • 当 \(\mathbb{E}[\rho] = 1\) 时(一般都满足),\(\texttt{chi2_token}=\text{mMean}(\rho^2,M)-1=\chi^2(\pi_{\text{tr} }\Vert\pi_{\text{ro} })\) 的值总是大于 0
        $$
        \begin{aligned}
        \texttt{chi2_token} &= \mathbb{E}[\rho^2] - 1 \\
        &= \mathbb{E}[\rho^2] - (\mathbb{E}[\rho])^2 \quad \left( \text{因为 } \mathbb{E}[\rho] = \int \frac{\pi_{\text{tr}}}{\pi_{\text{ro}}} \pi_{\text{ro}} dx = \int \pi_{\text{tr}} dx = 1 \right) \\
        &= \text{Var}(\rho) \\
        &= \mathbb{E}[(\rho - 1)^2] \ge 0.
        \end{aligned}
        $$
  • 序列级 \(\chi^2\) 散度 chi2_seq
    $$\texttt{chi2_seq}=\frac{1}{B}\sum_i \exp\bigl(2\text{mSum}_i(\ell,M)\bigr)-1=\mathbb{E}_{\text{seq} }[(\rho_i^{\text{seq} })^2]-1$$
    • 其中:
      $$ \rho_i^{\text{seq} }=\prod_{t:M_{i,t}=1}\rho_{i,t}=\exp(\text{mSum}_i(\ell,M)) $$
      • 相对 Token 粒度指标 \(\rho=e^\ell=\frac{\pi_{\text{tr} }}{\pi_{\text{ro} }}\) 的差异是 Sequence 粒度指标先对 log_ratio \(\ell\) 求和,然后再取指数
  • 理解;
    • 训推完全一致时 :两者都等于 0(因为 \(\rho=1\),方差为零)
    • 训推不一致时
      • chi2_token :随单步偏差小幅增长(反映局部噪音)
      • chi2_seq :因长度累乘效应呈指数级爆炸(远大于 chi2_token),序列越长、微小偏差累积越大,直接反映全局分布已严重漂移

IS 权重指标

  • 代码在 verl/trainer/ppo/rollout_corr_helper.compute_is_metrics 函数中
  • verl/trainer/ppo/rollout_corr_helper.compute_rollout_correction_weights 函数计算的权重 \(w\)(截断/IcePop 后)和原始权重 \(w_{\text{raw} }\):
    • token 级:
      $$w_{\text{raw} }=e^{\text{clamp}(\ell,\pm 20)}$$
    • sequence 级(计算后广播到所有 token):
      $$w_{\text{raw} }=e^{\text{clamp}(\text{mSum}_i(\ell,M),\pm 20)}$$
      • 注:Token 级和序列级上报的参数是同一个,所以选择 Token 级和 序列级后,得到的这个指标会变成对应的粒度的值
    • TIS:
      $$w=\min(w_{\text{raw} },\tau_u)$$
    • IcePop(MIS):
      $$w=w_{\text{raw} }\cdot\mathbb{1}[\tau_l\le w_{\text{raw} }\le\tau_u]$$
  • 记后续的多个值为:
    $$
    \begin{align}
    w^c=\text{clamp}(w,0,\tau_u) \\
    \tilde{w}=\frac{w^c}{\text{mMean}(w^c,M)}
    \end{align}
    $$
    • 注:这里是归一化权重
中心趋势
  • 这个是非常核心的指标,rollout_is_mean 一般需要在 1 附近,如果太小(比如 Sequence 粒度下,经过累加以后得值就很容易很小或者太大(一般是很小),可能导致模型梯度很小或者很大
    指标 公式
    rollout_is_mean \(\text{mMean}(w,M)\)
    rollout_is_max \(\max_{i,t:M=1} w_{i,t}\)
    rollout_is_min \(\min_{i,t:M=1} w_{i,t}\)
    rollout_is_std \(\sqrt{\text{mMean}\bigl((w^c)^2,M\bigr)-\text{mMean}(w^c,M)^2}\)
  • 注意:代码实现中 sequence 级 rollout_is_max/min 用 log 空间计算以避免溢出
越界比例(衡量 off-policy 比例)
  • 记录超过设定阈值的比例,这几个比例,谁高都不行,最好是都比较低
    级别 指标 公式
    token rollout_is_ratio_fraction_high \(\text{mMean}(\mathbb{1}[w_{\text{raw} }>\tau_u],M)\)
    token rollout_is_ratio_fraction_low \(\text{mMean}(\mathbb{1}[w_{\text{raw} }<\tau_l],M)\)
    seq rollout_is_ratio_fraction_high \(\frac{1}{B}\sum_i\mathbb{1}[\text{mSum}_i(\ell,M)>\log\tau_u]\)
    seq rollout_is_ratio_fraction_low \(\frac{1}{B}\sum_i\mathbb{1}[\text{mSum}_i(\ell,M)<\log\tau_l]\)
有效样本量 ESS
  • 记录有效的样本数量 rollout_is_eff_sample_size
    $$\texttt{rollout_is_eff_sample_size}=\frac{1}{\text{mMean}(\tilde{w}^{2},M)}\in(0,1]$$
    • ESS 越接近 \(1\) 说明权重越均匀(训推一致),越小则 IS 方差越大
    • 注意:名字虽然叫做 ESS,但是实现时是标准的 ESS 除以了 N 的,所以得到的是 0-1 之间的值
附录:ESS 的代码及数学证明详情
  • ESS 计算源码

    1
    2
    3
    4
    5
    6
    7
    # Compute Effective Sample Size (ESS) for truncated weights
    weights_for_ess: torch.Tensor = rollout_is_weights.clamp(min=0.0, max=rollout_is_threshold)
    mean_for_ess: torch.Tensor = verl_F.masked_mean(weights_for_ess, response_mask)
    is_weights_normalized: torch.Tensor = weights_for_ess / (mean_for_ess + 1e-8) # Avoid division by zero
    metrics["rollout_is_eff_sample_size"] = (
    1.0 / verl_F.masked_mean(is_weights_normalized.square(), response_mask).item()
    )
  • 公式定义:

    • 原始权重为 \(w_i\)(代码中的 weights_for_ess
    • 批量大小为 \(N\)(有效 Token 总数)
    • 权重均值为 \(\bar{w} = \frac{1}{N}\sum w_i\)(代码中的 mean_for_ess
    • 归一化权重为 \(\tilde{w}_i = \frac{w_i}{\bar{w} }\)(代码中的 is_weights_normalized
  • 代码最终输出的指标为
    $$
    \text{ESS}_{\text{code} } = \frac{1}{\frac{1}{N}\sum_i \tilde{w}_i^2}
    $$

  • 代入 \(\tilde{w}_i = w_i / \bar{w}\) 展开
    $$
    \begin{aligned}
    \text{ESS}_{\text{code} }
    &= \frac{1}{\frac{1}{N}\sum_i \left(\frac{w_i}{\bar{w} }\right)^2} \\
    &= \frac{1}{\frac{1}{N}\sum_i \frac{w_i^2}{\bar{w}^2} } \\
    &= \frac{1}{\frac{1}{\bar{w}^2} \cdot \frac{1}{N}\sum_i w_i^2} \\
    &= \frac{\bar{w}^2}{\frac{1}{N}\sum_i w_i^2} \\
    &= \frac{(\frac{1}{N}\sum_i w_i)^2}{\frac{1}{N}\sum_i w_i^2} \quad (\text{将 }\bar{w}\text{ 还原为均值}) \\
    &= \frac{(\sum_i w_i)^2}{N \cdot \sum_i w_i^2} \quad (\text{分子分母同乘 } N^2)
    \end{aligned}
    $$

  • 对比标准 ESS 的归一化比例

    • 标准的绝对 ESS 为 \(\frac{(\sum w_i)^2}{\sum w_i^2}\),除以总样本数 \(N\) 后就是 \(\text{ESS}_{\text{code} }\):
      $$
      \text{ESS}_{\text{code} } = \frac{(\sum w_i)^2}{N \cdot \sum w_i^2}
      $$
      • 所以代码中的指标在 0-1 之间 :
  • 补充: 关于 标准的绝对 ESS 为 \(\frac{(\sum w_i)^2}{\sum w_i^2}\) 的证明:

    • 本节在证明一个本质:用权重的平方和与总权重的比值,来反推这批加权样本在统计精度上“相当于”多少个纯随机均匀样本
    • 标准绝对 ESS 公式的根源来自于“方差等价”原则
      • 加权估计量的方差等于多少个体积为 1 的均匀样本的方差
    • 第一步:加权估计量的方差
      • 假设用重要性权重 \(w_i\) 去估计某个统计量(比如均值),其估计量的方差与权重的平方和有关
      • 对于加权估计量 \(\hat{\mu}_w = \frac{\sum w_i x_i}{\sum w_i}\),在大样本下,其渐近方差近似为:
        $$
        \text{Var}(\hat{\mu}_w) \propto \frac{\sum w_i^2}{(\sum w_i)^2} \cdot \sigma^2
        $$
        • 其中 \(\sigma^2\) 是原始分布的方差
    • 第二步:均匀样本的方差
      • 如果有 \(N_{\text{eff} }\) 个理想的无权重均匀样本(即每个样本的权重都为 1),那么其估计量 \(\hat{\mu} = \frac{1}{N_{\text{eff} } } \sum x_i\) 的方差为:
        $$
        \text{Var}(\hat{\mu}) = \frac{1}{N_{\text{eff} } } \cdot \sigma^2
        $$
    • 第三步:令两者相等,解出 \(N_{\text{eff} }\)
      • 让上面两个方差相等(即让加权样本的估计精度“等效”于多少个均匀样本):
        $$
        \frac{\sum w_i^2}{(\sum w_i)^2} \cdot \sigma^2 = \frac{1}{N_{\text{eff} } } \cdot \sigma^2
        $$
      • 消掉共同的 \(\sigma^2\),交叉相乘即得:
        $$
        N_{\text{eff} } = \frac{(\sum w_i)^2}{\sum w_i^2}
        $$
        • 这个值就可以用来估计 ESS
    • 直观理解:
      • 当所有 \(w_i\) 都相等(都为常数 \(c\))时:
        • 分子 \(= (N \cdot c)^2 = N^2 c^2\),分母 \(= N \cdot c^2\)
        • 比值 \(= N\)
        • 权重均匀,100 个样本就是 100 个有效样本
      • 当权重极度不均衡(一个 \(w=100\),其余 99 个 \(w \approx 0\))时:
        • 分子 \(\approx 100^2 = 10000\),分母 \(\approx 100^2 = 10000\)
        • 比值 \(\approx 1\)
        • 虽然采了 100 个样,但真正起作用的其实只相当于 1 个样本
IcePop 越界比例(仅 lower_upper 阈值时存在)
  • rollout_is_oob_ratio 的公式
    $$\texttt{rollout_is_oob_ratio}=\text{mMean}\bigl(\mathbb{1}[w_{\text{raw} }< \tau_l \lor w_{\text{raw} }>\tau_u],M\bigr)$$
  • 物理含义:
    • 超出 IcePop(MIS)的比例
序列级权重统计
  • 提前计算 Sequence \(i\) 的权重为(Token 粒度的均值)
    $$\bar{w}_i=\text{mMean}_i(w,M)$$
  • Sequence 粒度指标上报为(注意不管使用 token 还是 sequence 粒度都会上报):
    指标 公式
    rollout_is_seq_mean \(\frac{1}{B}\sum_i \bar{w}_i\)
    rollout_is_seq_std \(\text{Std}_i(\bar{w}_i)\)
    rollout_is_seq_max \(\max_i \bar{w}_i\)
    rollout_is_seq_min \(\min_i \bar{w}_i\)
    rollout_is_seq_max_deviation \(\max_i \lvert\bar{w}_i-1\rvert\)
    rollout_is_seq_fraction_high \(\frac{1}{B}\sum_i\mathbb{1}[\bar{w}_i>\tau_u]\)
    rollout_is_seq_fraction_low \(\frac{1}{B}\sum_i\mathbb{1}[\bar{w}_i<\tau_l]\)
批归一化因子(仅 rollout_is_batch_normalize=True 时上报)
  • rollout_is_batch_norm_factor 会上报真实用于归一化权重的那个值(最终权重会被除以该因子使均值为 1)
    指标 公式
    rollout_is_batch_norm_factor \(\bar{w}=\text{mMean}(w,M)\)(token 级)或 \(\frac{1}{B^*}\sum_{i:N_i>0}\bar{w}_i\)(sequence 级)
    • 其中 \(B^*\) 为有效序列数

RS 拒绝采样指标

  • 代码在下面两个函数中:
    • verl/trainer/ppo/rollout_corr_helper.compute_rollout_rejection_mask
    • verl/trainer/ppo/rollout_corr_helper.compute_rs_metrics
三种基础 token 级散度
  • 记 \(\ell_s=\text{clamp}(\ell,\pm 20)\):
    选项后缀 公式 理想值 取值范围
    k1 \(k_1=-\ell_s=\log\frac{\pi_{\text{ro} } }{\pi_{\text{tr} } }\) \(0\) \(\mathbb{R}\)(有符号)
    k2 \(k_2=\tfrac{1}{2}\ell_s^2\) \(0\) \(\geq 0\)
    k3 \(k_3=e^{\ell_s}-\ell_s-1=\rho-\log\rho-1\) \(0\) \(\geq 0\)
  • 三者都用于检测训推散度
    • k1 是 log-比本身(理想=0,方向敏感)=
    • k2 是 Pearson \(\chi^2\) 的一半
    • k3 是 Schulman k3 KL 估计量
聚合粒度
  • 对每个 token 级统计量 \(k\in\{k_1,k_2,k_3\}\),可做三种序列级聚合:
    前缀 公式
    token_* 直接用 \(k_{i,t}\)(无聚合)
    seq_sum_* \(K_i^{\text{sum} }=\text{mSum}_i(k,M)\)
    seq_mean_* \(K_i^{\text{mean} }=\text{mMean}_i(k,M)\)
    seq_max_* \(K_i^{\text{max} }=\max_{t:M_{i,t}=1} k_{i,t}\)(仅 k2/k3)
  • 共 11 个支持选项
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    SUPPORTED_ROLLOUT_RS_OPTIONS: set[str] = {
    "token_k1",
    "token_k2",
    "token_k3",
    "seq_sum_k1",
    "seq_sum_k2",
    "seq_sum_k3",
    "seq_mean_k1",
    "seq_mean_k2",
    "seq_mean_k3",
    "seq_max_k2",
    "seq_max_k3",
    }
    TOKEN_LEVEL_ROLLOUT_RS_OPTIONS: set[str] = {"token_k1", "token_k2", "token_k3"}
阈值过滤规则
  • k1 类:双向阈值 \(\text{lower}\le k_1\le\text{upper}\)(在 log 空间比较)
  • k2/k3 类:单向上界 \(k\le\text{upper}\)
  • 多选项时各选项 mask 做逻辑 AND 累乘
每选项指标(<opt> 为具体选项名,如 token_k1seq_sum_k3
  • 记该选项的统计量为 \(s\)(token 级或序列级),阈值 \(\theta_u,\theta_l\)(k1 类 \(\theta_l\) 为 log 下界,否则 \(\theta_l=0\)),\(s^c=\text{clamp}(s,\theta_l,\theta_u)\):
    指标 公式
    rollout_rs_<opt>_mean \(\text{mMean}(s,M)\)
    rollout_rs_<opt>_max \(\max(s)\)(序列级仅在有效序列上)
    rollout_rs_<opt>_min \(\min(s)\)
    rollout_rs_<opt>_std \(\sqrt{\text{mMean}((s^c)^2,M)-\text{mMean}(s^c,M)^2}\)
    rollout_rs_<opt>_fraction_high 越上界比例(token 用 mMean,seq 用均值)
    rollout_rs_<opt>_fraction_low 越下界比例(仅 k1 类)
    rollout_rs_<opt>_seq_mean \(\frac{1}{B^*}\sum_i s_i\)
    rollout_rs_<opt>_seq_std \(\text{Std}_i(s_i)\)
    rollout_rs_<opt>_seq_max / _seq_min \(\max_i s_i\) / \(\min_i s_i\)
    rollout_rs_<opt>_seq_max_deviation \(\max_i \lvert s_i-0\rvert=\max_i\lvert s_i\rvert\)
    rollout_rs_<opt>_seq_fraction_high \(\frac{1}{B^*}\sum_i\mathbb{1}[s_i>\theta_u]\)
    rollout_rs_<opt>_seq_fraction_low \(\frac{1}{B^*}\sum_i\mathbb{1}[s_i<\theta_l]\)
    rollout_rs_<opt>_masked_fraction 本选项过滤后token 级被 mask 比例 \(\text{mMean}(1-\text{keep},M)\)
    rollout_rs_<opt>_seq_masked_fraction 本选项过滤后序列级被 mask 比例 \(\frac{\sum_i\mathbb{1}[\exists t:\neg\text{keep}_{i,t}\land M_{i,t}]\cdot\mathbb{1}[N_i>0]}{\sum_i\mathbb{1}[N_i>0]}\)
RS 相关的汇总指标
  • 所有选项 AND 合并后会得到 RS 相关的汇总指标
    指标 公式
    rollout_rs_masked_fraction \(\text{mMean}(1-\text{final_keep},M)\),最终 token 级被拒比例
    rollout_rs_seq_masked_fraction \(\frac{1}{B}\sum_i\mathbb{1}[\exists t:\neg\text{final_keep}_{i,t}\land M_{i,t}]\),至少一个 token 被拒的序列占比

RS 和 IcePop 辨析

一些历史
  • 注:最早 IcePop(蚂蚁百灵团队)中介绍的方法 IcePop 本质就是一个 MIS,但没有强调权重被置为 0 的样本是否走 mask 矩阵,即分母上是否还在
  • 在早期 TRL 的 PR 中 [GRPO] Sequence-level TIS + MIS #4493 中,会明确提到,MIS 就是来源于 IcePop 的介绍
  • 在 VeRL 中,Sequence-level 的 MIS 一般可以理解为指的是 Sequence-level IS + Sequence-level RS 一起实现的一种方式(当然 IcePop 的实现也是类似的,但是有是否走 mask 的区别)
    • 注意:单独的 RS 实现不了 MIS,因为 MIS 下,阈值内的 IS 是保留的
    • Sequence-level MIS,即对 IS 比率超过阈值 \(C\) 的序列的策略损失进行 Mask:
      $$ \rho(y|x) \gets \rho(y|x) \mathbb{I}\{\rho(y|x) \le C\} $$
      • 注意:MIS 对阈值内的部分是保留了梯度的,和 VeRL 中 RS 的实现不同(RS 不保留 IS,仅仅 Mask)
      • 注意:由于使用了 RS,所以本质上是走 Mask 矩阵(即分子分母都 Mask 的实现)
    • 详情见:NLP——LLM对齐微调-RL-Collapse-Training-Inference-Mismatch(Sequence-level-MIS)
IcePop 是 RS 的一种特殊实现吗?
  • 答案是不可以,IcePop 属于 IS 家族,不是 RS 的实现 ,两者是代码和理论上都独立的机制
  • 文档 github.com/verl-project/verl/blob/main/docs/algo/rollout_corr.md 明确说明:
    • Separate from rejection sampling (controlled by rollout_rs parameters)
    • Unlike rollout_rs, IcePop does not modify response_mask; it only changes the IS coefficients
  • 在 VeRL 中实现的核心区别对比
    维度 IcePop RS
    理论归属 IS 家族(TIS 的变体) Rejection Sampling
    代码位置 compute_rollout_correction_weights compute_rollout_rejection_mask
    作用对象 IS 权重 \(w\) response_mask \(M\)
    输出类型 连续权重(含 0) 二值 mask
    配置入口 rollout_is_threshold: "0.5_5.0" rollout_rs + rollout_rs_threshold
    • IS 中
      • rollout_is_threshold 只有一个值时默认为 upper,默认生效 TIS
      • 如果有两个值时默认为 lower_upper,且识别到 lower 存在时默认生效 IcePop
  • 机制对比
    • IcePop
      $$
      w_i=\begin{cases}\rho_i & \text{if }\tau_l\le\rho_i\le\tau_u\\ 0 & \text{otherwise}\end{cases}
      $$
      • 越界样本权重置 0 ,但 token 仍在 batch 中(仍占显存、仍参与序列长度统计)
      • 界内样本保留实际权重值 (连续)
    • RS
      $$
      M_{i,t}^{\text{new} }=M_{i,t}\cdot\mathbb{1}[\text{stat}_{i,t}\in[\theta_l,\theta_u]]
      $$
      • 直接修改 response_mask,被拒 token 完全从训练中剔除
      • 二值过滤,不保留任何权重信息
VeRL 数学文档中给出的说明
  • 数学文档:github.com/verl-project/verl/blob/main/docs/algo/rollout_corr_math.md
  • 数学文档将两者明确分类:
    • IS :对应连续权重
      $$w_{\text{seq} }(\theta)=\min\bigl(\prod_t\rho_t,C_{\text{IS} }\bigr) $$
    • RS :对应二值拒绝掩码
      $$ w_{\text{seq} }(\theta)\in\{0,1\} $$
  • 也就是说:
    • IcePop 输出的是连续权重(含 0),属于 IS 框架
    • RS 输出二值 mask,是独立机制
总结 RS and IcePop:特别注意
  • IcePop 把越界权重设为 0,效果上类似”软拒绝”(这些样本对梯度无贡献),所以直觉上容易和 RS 混淆,但本质区别是:
    • IcePop 的 0 权重样本仍占用 batch slot ,只是梯度贡献为 0
      • RS 直接从 mask 中删除,会影响有效样本数 \(N\) 的统计
    • IcePop 界内样本用实际 \(\rho\) 值(保留 IS 的方差减少特性)
      • RS 界内样本权重恒为 1(纯过滤,无 IS 校正)
    • 两者可同时启用compute_rollout_correction_and_rejection_mask 中 IS 和 RS 是两个独立 step,先算 IS 权重,再算 RS mask,互不干扰
      • 注:IS 和 RS 本来就是可以同时开启使用的
VeRL 中的 Seq-MIS 示例
  • 代码示例:

    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
    # https://github.com/verl-project/verl/blob/main/docs/algo/rollout_corr.md 中推荐使用的 MIS 方式
    config = RolloutCorrectionConfig.decoupled_seq_is_rs() # Seq-MIS

    # 函数实现:verl.trainer.config.algorithm.decoupled_seq_is_rs 实现如下
    def decoupled_seq_is_rs(
    cls,
    is_threshold: float = 2.0,
    rs_threshold: Optional[str | float] = "0.5_2.0",
    ) -> "RolloutCorrectionConfig":
    """Decoupled Mode with Sequence-level IS + Rejection Sampling.

    Sequence-level IS with sequence-level rejection sampling in decoupled mode.
    Rejects entire sequences based on sequence-level IS weight.

    Args:
    is_threshold (float): Upper threshold for IS weights. Default: 2.0
    rs_threshold (Optional[Union[str, float]]): Upper threshold for rejection sampling. Default: 0.5_2.0

    Returns:
    RolloutCorrectionConfig configured for decoupled mode with sequence IS + RS
    """
    return cls(
    rollout_is="sequence",
    rollout_is_threshold=is_threshold,
    rollout_rs="seq_sum_k1",
    rollout_rs_threshold=rs_threshold,
    )
  • 解读:

    • VeRL 中推荐的 MIS 实现是通过 RS 来实现(rollout_rs_threshold="0.5_2.0"),而不是 通过 IS(IcePop)来实现(is_threshold=2.0 没有下界,不会触发 IcePop)

附录:Temperature 如何影响 rollout_corr/kl

  • 假设:
    • 其他配置完全相同(同权重、同 batch、top_p=1rollout_is=tokenrollout_is_threshold=2.0bypass_mode=false),仅修改 actor_rollout_ref.rollout.temperature,观察训练早期(step 0 附近)的 rollout_corr/kl
    • 注:训推 两侧都对 temperature 做了修正 ,verl 会读取 rollout.temperature 给训练侧
  • 回顾:rollout_corr/kl 是 KL 的 k1 估计器:
    $$
    \text{kl} = \frac{1}{|\mathcal{T}|}\sum_{t\in\mathcal{T} }\Big(\log \pi_{\text{rollout} }(a_t)-\log \pi_{\theta}(a_t)\Big)
    \xrightarrow[\ |\mathcal{T}|\to\infty\ ]{} \text{KL}\big(\pi_{\text{rollout} } | \pi_{\theta}\big)
    $$
    • 所以它们的目标分布是同一个
      $$ \pi \propto \exp(z/T) $$
  • 训推的不同计算路径:
    • 训练(Megatron):
      • logits.div_(temp) → vocab-parallel CE,bf16 除法.float() 上转发生在其后
    • Rollout(SGLang)
      • logits.div_(temperatures)softmaxlogfp32 除法 ,logits 已 .float()
    • 说明: \(\text{kl}\) 不度量真实的 off-policy 程度(当前两者理论上策略完全对齐),只度量两个引擎的数值失配
  • 注意:一般来说若某一侧漏了温度修正,\(T=0.6\) 时 \(\text{kl}=\mathbb{E}[\log p_1-\log p_{0.6}]\) 会是 \(10^{-1}\sim 10^{0}\) 量级
核心推导:\(\text{kl}\) 是 logit 误差的二阶量,且被 \(1/T^2\) 放大
  • 设 raw logit 为 \(z\),推理引擎的 logit 为 \(z+\varepsilon\)(\(\varepsilon\) 来自 kernel/并行布局/精度差异)
  • 两侧分布:
    $$
    \begin{align}
    \pi_\theta(a)=\frac{e^{z_a/T} }{\sum_b e^{z_b/T} } \\
    \pi_{\text{rollout} }(a)=\frac{e^{(z_a+\varepsilon_a)/T} }{\sum_b e^{(z_b+\varepsilon_b)/T} }
    \end{align}
    $$
  • 记:
    $$ u_a=\frac{\varepsilon_a}{T} $$
  • 则:
    $$ \log\pi_{\text{rollout} }(a)-\log\pi_\theta(a)=u_a-\log\mathbb{E}_{\pi_\theta}[e^{u}]$$
    • 证明:
      $$
      \begin{align}
      \log\pi_{\text{rollout}}(a)-\log\pi_\theta(a)
      &= \left[\frac{z_a+\varepsilon_a}{T} - \log\sum_b e^{(z_b+\varepsilon_b)/T}\right] - \left[\frac{z_a}{T} - \log\sum_b e^{z_b/T}\right] \\
      &= \frac{\varepsilon_a}{T} - \left[\log\sum_b e^{(z_b+\varepsilon_b)/T} - \log\sum_b e^{z_b/T}\right] \\
      &= u_a - \log\left(\frac{\sum_b e^{(z_b+\varepsilon_b)/T}}{\sum_b e^{z_b/T}}\right) \\
      &= u_a - \log\left(\sum_b \frac{e^{z_b/T}}{\sum_c e^{z_c/T}} \cdot e^{\varepsilon_b/T}\right) \\
      &= u_a - \log\left(\sum_b \pi_\theta(b) \cdot e^{u_b}\right) \\
      &= u_a - \log\mathbb{E}_{\pi_\theta}\left[e^{u}\right]
      \end{align}
      $$
  • 代入 KL 定义根据泰勒展开(\(m=\mathbb{E}_\pi[u]\),\(s^2=\text{Var}_\pi(u)\))有:
    $$
    \begin{align}
    \mathbb{E}_{\pi_{\text{rollout} } }[u] &\approx m+s^2 \\
    \log\mathbb{E}_{\pi_\theta}[e^{u}] &\approx m+\tfrac12 s^2 \\
    \text{KL}\big(\pi_{\text{rollout} }|\pi_\theta\big) &\approx\tfrac12 \text{Var}_\pi(u)=\frac{\text{Var}_\pi(\varepsilon)}{2T^{2} }
    \end{align}
    $$
    • 证明:
      • 第 1 步:把 KL 拆成“原始期望”减“归一化 log”
        • 由之前的恒等式:
          $$
          \log\pi_{\text{rollout} }(a)-\log\pi_\theta(a)=u_a-\log\mathbb{E}_{\pi_\theta}[e^{u}]
          $$
        • 两边对 \(\pi_{\text{rollout} }\) 求期望:
          $$
          \begin{align}
          \text{KL}\big(\pi_{\text{rollout} }|\pi_\theta\big)
          &= \mathbb{E}_{\pi_{\text{rollout} } }[u] - \mathbb{E}_{\pi_{\text{rollout} } }\big[\log\mathbb{E}_{\pi_\theta}[e^{u}]\big] \\
          &= \mathbb{E}_{\pi_{\text{rollout} } }[u] - \log\mathbb{E}_{\pi_\theta}[e^{u}]
          \end{align}
          $$
          • 注:第二项中 \(\log\mathbb{E}[e^u]\) 是一个标量常数 ,与变量 \(a\) 无关,所以对它求期望等于它本身
      • 第 2 步:引入归一化常数 \(Z\),把 \(\pi_{\text{rollout} }\) 改写成关于 \(\pi_\theta\) 的形式

        • $$
          Z = \mathbb{E}_{\pi_\theta}[e^{u}] = \sum_b \pi_\theta(b) e^{u_b}
          $$
        • 则:
          $$
          \pi_{\text{rollout} }(a)=\frac{\pi_\theta(a)e^{u_a} }{Z}
          $$
        • 那么:
          $$
          \mathbb{E}_{\pi_{\text{rollout} } }[u] = \sum_a \frac{\pi_\theta(a)e^{u_a} }{Z} \cdot u_a = \frac{\mathbb{E}_{\pi_\theta}[u e^{u}]}{Z}
          $$
      • 第 3 步:对 \(e^u\) 做二阶泰勒展开(假设 \(u\) 是小量)
        • 定义:
          $$
          m = \mathbb{E}_{\pi_\theta}[u], \quad s^2 = \text{Var}_{\pi_\theta}(u)=\mathbb{E}_{\pi_\theta}[u^2]-m^2
          $$
        • 泰勒展开保留到二阶(\(\mathbb{E}[u^3]\) 及更高阶忽略):
          • 1)展开分子 \(\mathbb{E}_{\pi_\theta}[u e^{u}]\):
            $$
            \begin{align}
            \mathbb{E}[u e^{u}] &\approx \mathbb{E}\big[u(1+u+\frac{u^2}{2})\big] \\
            &= \mathbb{E}[u] + \mathbb{E}[u^2] + o(\frac12\mathbb{E}[u^3]) \\
            &= m + (s^2 + m^2) = m + s^2 + m^2
            \end{align}
            $$
          • 2)展开分母 \(Z = \mathbb{E}_{\pi_\theta}[e^{u}]\):
            $$
            Z \approx \mathbb{E}\big[1+u+\frac{u^2}{2}\big] = 1 + m + \frac12 (s^2 + m^2)
            $$
          • 3)**展开 \(\frac{1}{Z}\)**(利用 \((1+x)^{-1}\approx 1-x\),保留到二阶):
            $$
            \frac{1}{Z} \approx 1 - m - \frac12 s^2 - \frac12 m^2 + m^2 = 1 - m - \frac12 s^2 + \frac12 m^2
            $$
            • 注意这里做了二阶精度下的化简,交叉项被忽略
          • 4)相乘得到
            $$ \mathbb{E}_{\pi_{\text{rollout} } }[u] \approx \frac{m + s^2 + m^2}{Z} $$
            • 保留到二阶:
              $$
              \frac{m + s^2 + m^2}{1 + m + \frac12(s^2+m^2)} \approx (m + s^2 + m^2)(1 - m) = m + s^2 + o(m^2 - m^2) = m + s^2
              $$
            • 即:
              $$
              \color{red}{\mathbb{E}_{\pi_{\text{rollout} } }[u] \approx m + s^2}
              $$
      • 第 4 步:展开 \(\log Z\)
        $$
        \log Z = \log\mathbb{E}_{\pi_\theta}[e^{u}] \approx \log\big(1 + m + \frac12(s^2+m^2)\big)
        $$
        • 利用 \(\log(1+x)\approx x - \frac{x^2}{2}\),取 \(x = m + \frac12(s^2+m^2)\):
          $$
          \log Z \approx \big(m + \frac12 s^2 + \frac12 m^2\big) - \frac12 m^2 = m + \frac12 s^2
          $$
        • 即:
          $$
          \color{red}{\log\mathbb{E}_{\pi_\theta}[e^{u}] \approx m + \frac12 s^2}
          $$
      • 第 5 步:两者相减得到最终的 KL 近似
        $$
        \text{KL} = \mathbb{E}_{\pi_{\text{rollout} } }[u] - \log Z \approx (m + s^2) - (m + \frac12 s^2) = \frac12 s^2
        $$
        • 代回 \(u_a = \varepsilon_a / T\),因为方差的性质 \(\text{Var}(\varepsilon/T) = \text{Var}(\varepsilon)/T^2\),最终得到:
          $$
          \color{red}{\text{KL}\big(\pi_{\text{rollout} }|\pi_\theta\big) \approx \frac12 \text{Var}_{\pi_\theta}(u) = \frac{\text{Var}_{\pi_\theta}(\varepsilon)}{2T^2} }
          $$
直观理解
  • 一阶项 \(m\) 被完美抵消 :它在 \(\mathbb{E}_{\pi_{\text{rollout} } }[u]\) 和 \(\log Z\) 中都出现,相减后消失
    • 这解释了为什么 KL 对噪声的方向(正负)不敏感,只取决于噪声的散布程度
  • KL 是纯二阶量
    • 所以理论上它恒为正 ,且完全由方差决定
  • 三个直接推论:
    • 1)一阶项完全抵消 ,\(\text{kl}\) 是严格二阶量 \(\Rightarrow\) 恒为正,且对噪声方向不敏感
    • 2)温度在 softmax 之前作用 ,同一份 \(\varepsilon\) 在 log-prob 空间被放大 \(1/T\),KL 被放大 \(1/T^2\)
    • 3)同阶下 \(\text{KL}(p|q)\approx \text{KL}(q|p)\approx\tfrac12 s^2\),所以 klk3_kl 应当近似相等
      • 在这是”失配确实很小、展开有效”的自检项
  • 反解 logit 空间的失配幅度(跨 \(T\) 可比的量):
    $$
    \text{std}(\varepsilon)\approx T\sqrt{2 \text{kl} },
    \qquad
    \text{kl}\cdot T^{2}=\tfrac12\text{Var}_\pi(\varepsilon)=\text{const(理想情况)}
    $$
  • Temperature 为 0.6 和 Temperature 为 1.0 时的 KL 散度差异
    • temperature=1.0 时,\(1.00\times\)
    • temperature=0.6 时,\(2.78\times\)
    • 一般来说 temperature 1.0 时的 rollout/kl 更小,但现实中不一定小 2.78 倍
      • 实测中低于理论 \(2.78\times\):原因是概率质量集中效应
        • 低温下 \(\pi\) 更尖锐,有效支撑集变窄,\(\text{Var}_\pi(\varepsilon)\) 随之下降,部分抵消 \(1/T^2\)
      • PS:一般似乎差不多两倍左右
不同 Temperature 的可能带来的其他误差
  • 除了上述推导的放大效应,\(T \ne 1\) 还会包含一些其他计算精度误差:
    • 比如 训练侧的除法在 bf16 上原地完成,把每个 \(z_a/T\) 重新舍入到 bf16 网格,而 SGLang 在 fp32 完成同一步
    • 在 temperature=1.0 时一般不会引入误差, 但在 0.6 时则更容易引入误差

附录:Temperature 对 grad-norm 的影响

  • 一般来说,理论上:
    • 由于梯度系数中包含温度项 \(\frac{1}{T}\),所以在提升温度时,grad-norm 大概率是会降低的,且降低幅度和温度上升幅度差不多
  • 但是现实场景中
    • 如果温度降低导致 GRPO 等出现 Advantage 差异过小等问题,则会因为温度降低而丢失部分样本的差异性,所以观察到的 grad-norm 降低幅度不一定跟温度对齐
  • 但是实测下来:
    • 大部分情况下,温度从 1.0 降低到 0.6 后,grad-norm 都是提升的
  • 补充:训练过程中,似乎发现温度更低的,grad-nrom 下降更快
    • 理解:低温下,策略更尖锐,grad-norm 更大,可能导致更新幅度更大,下降更快
  • 补充:低温下,看到的熵是更小的