Skip to content

调试指标:从异常现象定位问题

LLM post-training 的调试不要只盯最终 accuracy。RL 训练像一条流水线:数据、rollout、reward、KL、advantage、loss、显存和吞吐任何一段出问题,最后曲线都会很怪。

这页的目标是帮你做到两件事:

  • 看到一个指标,知道它从哪段源码产生。
  • 看到一个异常,知道先看样例、再看分布、最后改哪个配置。

指标从哪里来

训练主循环在 verl/trainer/ppo/ray_trainer.py::fit()。一轮 step 大致是:

text
rollout -> reward -> old_log_prob -> ref_log_prob -> values -> KL/reward -> advantage -> update_critic -> update_actor -> metrics

关键指标来源如下:

指标源码来源说明
critic/score/*metric_utils.compute_data_metrics()token_level_scores.sum(-1),原始 rule reward/RM 分数
critic/rewards/*metric_utils.compute_data_metrics()token_level_rewards.sum(-1),真正用于 advantage 的 reward
critic/advantages/*compute_advantage() + core_algos.pyPPO 是 GAE;GRPO 是组内相对分数
critic/returns/*core_algos.compute_gae_advantage_return()critic 学习目标;GRPO 通常等于 advantages
critic/vf_explained_varmetric_utils.compute_data_metrics()只对有 critic 的 PPO 有意义
actor/entropyray_trainer.py::_compute_old_log_prob() 后聚合old policy 在 response token 上的熵
actor/pg_clipfraccore_algos.compute_policy_loss_vanilla()PPO clip 被触发的 token 比例
actor/ppo_klcore_algos.compute_policy_loss_vanilla()当前 actor 与 old policy 的近似差异
actor/reward_kl_penaltyray_trainer.py::apply_kl_penalty()reward-side KL 时记录
response_length/*metric_utils.compute_data_metrics()response token 长度统计
timing_s/*ray_trainer.py 里的 marked_timer()各阶段 wall time
perf/throughputmetric_utils.compute_throughout_metrics()每 GPU token/s
training/rollout_probs_diff_*verl/utils/debug/metrics.pyrollout logprob 与 actor old logprob 差异
rollout_corr/*rollout_corr_helper.pyrollout correction 开启后才有

reward manager 的额外返回也会进入指标链路。比如 reward function 返回 {"score": 1.0, "acc": True, "pred": "42"}score 用于训练,acc/pred 会进入验证聚合,产生 val-core/{data_source}/acc/mean@Nbest@Nmaj@N 等。

第一原则:先分清 score、reward、advantage

名字代表什么什么时候会不一样
scorescorer 原始分reward manager 直接写入 token_level_scores
reward训练实际用分algorithm.use_kl_in_reward=True 时会扣 KL
advantagepolicy gradient 的方向和强度PPO 用 GAE;GRPO 用组内相对分

如果 algorithm.use_kl_in_reward=Falsecritic/rewards/* 通常等于 critic/score/*。Qwen3 GRPO 示例就是这种:KL 放在 actor loss 侧,而不是 reward 侧。

Reward 排查树

看到 critic/score/mean 长期全 0,不要先调学习率。按这个顺序:

  1. 抽样打印 prompt、response、ground truth、parsed answer、score。
  2. 检查 parquet 字段:data_source 是否能被 default_compute_score() 分派,reward_model.ground_truth 格式是否和 scorer 对齐。
  3. 检查格式:GSM8K 默认找 #### number,MATH 默认找最后一个 \boxed{}
  4. 检查长度:response_length/clip_ratio 高时,最后答案可能被截断。
  5. 检查 scorer 返回:dict 必须有 score;最好同时返回 accpredformat_reward

常见修法:

现象优先改什么
全 0修 parser、prompt instruction、data_sourceground_truth
很快全 1查验证泄漏、reward 是否太宽、任务是否太简单
格式错很多强化 prompt 格式,或在 reward 里拆 format_reward / accuracy_reward
答案被截断增大 data.max_response_length,或加 overlong/length 策略

KL 排查树

先判断 KL 放在哪里:

位置配置指标
reward-side KLalgorithm.use_kl_in_reward=Trueactor/reward_kl_penaltycritic/rewards 低于 critic/score
loss-side KLactor_rollout_ref.actor.use_kl_loss=Trueactor loss 侧生效,GRPO 示例常用

KL 快速升高时按顺序查:

  1. actor 学习率 actor_rollout_ref.actor.optim.lr 是否太大。
  2. kl_coef 是否太小:reward-side 看 algorithm.kl_ctrl.kl_coef,loss-side 看 actor_rollout_ref.actor.kl_loss_coef
  3. old_log_probs 是否正确重算;如果启用 rollout correction,再看 rollout_log_probs 是否存在。
  4. rollout 权重是否及时同步,异步/离线/多后端时看 training/rollout_probs_diff_*rollout_corr/*
  5. reward 是否太尖锐:全靠单一 0/1 reward 时,policy 可能猛追少数高分样本。

常见修法:

现象可尝试配置
KL 暴涨actor_rollout_ref.actor.optim.lr,升 kl_loss_coefalgorithm.kl_ctrl.kl_coef
reward-side KL 后 reward 变负很多algorithm.kl_ctrl.kl_coef,或改用 loss-side KL
rollout/train 概率差大actor_rollout_ref.rollout.calculate_log_probs=True,再配置 algorithm.rollout_correction

Advantage 排查树

PPO 和 GRPO 最大差异在这里。

PPO/GAE。 algorithm.adv_estimator=gae,需要 critic。core_algos.compute_gae_advantage_return()valuesgammalam 把最终 reward 传播到 response token。

排查顺序:

  1. critic/score/* 是否有分布。
  2. critic/rewards/* 是否被 KL 扣到没信号。
  3. critic/advantages/max/min 是否几乎为 0。
  4. critic/vf_explained_var 是否长期很差。
  5. critic/vf_clipfraccritic/vf_loss 是否异常大。

GRPO/RLVR。 algorithm.adv_estimator=grpo,不训练 critic。compute_grpo_outcome_advantage()uid 分组:

text
A_i = (R_i - group_mean) / (group_std + eps)

排查顺序:

  1. actor_rollout_ref.rollout.n 是否大于 1。
  2. 同组 reward 是否有差异;全对/全错都会让 advantage 弱。
  3. algorithm.norm_adv_by_std_in_grpo 是否符合 recipe。
  4. loss_agg_mode 是否让长回答权重异常。

常见修法:

现象PPOGRPO/RLVR
advantage 很弱查 critic/value、KL、reward增大 rollout.n,提高采样温度,考虑 DAPO 动态采样
advantage 极端降 reward 尖锐度,查 value检查组内 std 很小导致归一化放大
critic 指标很差调 critic lr/batch,检查 value lossGRPO 没 critic,不看 vf 指标

Clipfrac 与学习率

actor/pg_clipfrac 来自 core_algos.compute_policy_loss_vanilla(),表示 PPO clipped objective 有多少 token 触发了 clip。它高时说明新旧策略差异太大,PPO 经常踩刹车。

排查顺序:

  1. actor/pg_clipfrac 高,同时 actor/ppo_kl 高:actor 更新太猛。
  2. actor/pg_clipfrac 高,但 reward 不涨:可能 advantage 噪声大或 reward 错。
  3. actor/pg_clipfrac 长期接近 0:可能学习率太小、advantage 太弱、clip 太宽或 reward 没信号。

常见修法:

现象可尝试配置
clipfrac 很高actor_rollout_ref.actor.optim.lr,降 ppo_epochs,增大 batch,增强 KL
clipfrac 接近 0 且 reward 不动查 reward/advantage,再考虑升 lr
长回答导致 clip 异常检查 loss_agg_moderesponse_length/clip_ratio

Entropy:探索是否过早塌缩

actor/entropyray_trainer.py 重算 old logprob 时从 entropys 聚合得到。它反映 response token 分布是否还保留多样性。

排查顺序:

  1. entropy 快速下降,KL 上升:policy 可能过快变窄。
  2. entropy 很高但 reward 不涨:采样很随机,但没有学到有效偏好。
  3. GRPO 中 entropy 低且组内全相同:rollout.n 再大也没有差异。

常见修法:

现象可尝试配置
entropy 掉太快降 actor lr,增强 KL,必要时设 actor_rollout_ref.actor.entropy_coeff
entropy 太高降采样温度或检查 reward 信号是否太弱
GRPO 组内太像提高 rollout sampling 多样性,确认 rollout.n 生效

Response Length 排查树

长度指标来自 metric_utils.compute_data_metrics()

指标含义
response_length/mean平均生成长度
response_length/clip_ratioresponse 打满 max_response_length 的比例
response_length_non_aborted/*排除 0 长 response 后的长度
response/aborted_ratioresponse 长度为 0 的比例
prompt_length/clip_ratioprompt 是否接近最大长度

排查顺序:

  1. response_length/clip_ratio 高:答案可能被截断,reward parser 也可能拿不到 final answer。
  2. response 越来越长但 reward 不涨:reward 可能鼓励啰嗦,或 loss 聚合偏向长回答。
  3. response/aborted_ratio 高:rollout 后端、EOS、采样参数或服务状态要查。
  4. prompt_length/clip_ratio 高:prompt 被截断会直接破坏题目。

常见修法:

现象可尝试配置
大量打满长度增大 data.max_response_length,或使用 DAPO overlong_buffer
输出越来越长检查 reward,调 loss_agg_mode,加入长度惩罚
prompt 被截断增大 data.max_prompt_length,清洗过长样本,谨慎设置 data.truncation

Throughput 与显存排查树

性能指标来自 metric_utils.compute_timing_metrics()compute_throughout_metrics()

指标先看什么
timing_s/genrollout backend、max_response_lengthrollout.n、vLLM/SGLang 并行
timing_s/refreference logprob micro batch / dynamic batch
timing_s/valuesPPO critic 负担;GRPO 没有这一段
timing_s/update_actoractor micro batch、sequence length、FSDP/Megatron 并行
timing_s/update_criticcritic micro batch、critic model size
perf/throughput总 token/s/GPU 低,要结合各阶段 timing 看

显存和 batch size 要分四层:

  1. data.train_batch_size:prompt 数。
  2. actor_rollout_ref.rollout.n:response 数放大倍数。
  3. actor_rollout_ref.actor.ppo_mini_batch_size:全局 actor mini-batch。
  4. ppo_micro_batch_size_per_gpuppo_max_token_len_per_gpu:单 GPU 真实容量。

常见修法:

现象可尝试配置
rollout OOMactor_rollout_ref.rollout.gpu_memory_utilization,降 max_response_length,调 TP
actor update OOMactor_rollout_ref.actor.ppo_micro_batch_size_per_gpu,或降 ppo_max_token_len_per_gpu
critic OOMcritic.ppo_micro_batch_size_per_gpu,启用 checkpointing/offload
gen 很慢rollout.tensor_model_parallel_sizemax_num_seqsmax_num_batched_tokens,缩短 response
update_actor 很慢use_remove_padding=Trueuse_dynamic_bsz=True,调 micro batch

docs/perf/perf_tuning.rst 的核心建议是:算法 batch 是全局概念,micro batch / max token len 是性能和显存概念。不要用“改 micro batch”来期待算法收敛行为发生本质变化。

PPO vs GRPO/RLVR 的调试差异

问题PPO/GAEGRPO/RLVR
是否有 critic通常没有
重点 advantage 指标critic/vf_explained_varvaluesreturns组内 reward 差异、rollout.n
KL 常见位置reward-side 或 loss-side 都可能Qwen3 GRPO 示例是 loss-side
batch 直觉rollout.n 常为 1rollout.n 是核心
常见失败critic 学不好、KL/reward 尺度不合全对/全错组、reward 方差不足、长度偏置
先看源码compute_gae_advantage_return()compute_grpo_outcome_advantage()

如果你用 PPO 脚本调 GRPO 思路,会误盯 critic;如果你用 GRPO 思路调 PPO,又会忽略 value baseline 和 critic loss。这是新手最常见的错位。

一个实际排查顺序

每次训练不对劲,按这个顺序走:

  1. 看样例:prompt、response、ground truth、parsed pred、score。
  2. 看 reward 分布:critic/score/mean/max/min
  3. 看 reward 是否被改写:critic/rewards/* vs critic/score/*
  4. 看 advantage:PPO 查 value/returns,GRPO 查组内差异和 rollout.n
  5. 看 policy update:actor/pg_clipfracactor/ppo_klactor/entropy
  6. 看长度:response_length/clip_ratioresponse/aborted_ratio
  7. 看性能:timing_s/*perf/throughput
  8. 最后再改配置,每次只改一两个变量。

本节参考与延伸阅读

  • verl/trainer/ppo/ray_trainer.py
  • verl/trainer/ppo/core_algos.py
  • verl/trainer/ppo/metric_utils.py
  • verl/trainer/ppo/rollout_corr_helper.py
  • verl/utils/debug/metrics.py
  • verl/workers/reward_manager/naive.py
  • verl/workers/reward_manager/dapo.py
  • examples/ppo_trainer/run_qwen3_8b_fsdp.sh
  • examples/grpo_trainer/run_qwen3_8b_fsdp.sh
  • docs/perf/perf_tuning.rst
  • docs/perf/best_practices.rst
  • docs/algo/ppo.md
  • docs/algo/grpo.md
  • docs/algo/dapo.md
  • PPO 论文
  • DeepSeekMath / GRPO 论文
  • DAPO 论文

面向源码阅读的 verl 学习文档。