必读论文路线
论文不是收藏链接。对 verl 新手来说,每篇都要带着五个问题读:它解决什么问题,核心公式或思想是什么,小白怎么理解,verl 哪段源码在实现它,读完应该回到本站哪一节继续动手。
先验地图
| 层次 | 要学会的问题 | 必读材料 |
|---|---|---|
| Policy gradient 基础 | 为什么要有 ratio、advantage、clip | PPO、GAE |
| RLHF 范式 | SFT、RM、PPO、KL 是什么关系 | InstructGPT |
| RLVR / reasoning | 没有人类偏好模型时怎样用可验证奖励 | DeepSeekMath、DeepSeek-R1 |
| 无 critic baseline | 没 value model 时怎样降方差 | GRPO、RLOO、ReMax、REINFORCE++ |
| recipe 稳定性 | 长 CoT、动态采样、clip、overlong 怎么配 | DAPO、Dr.GRPO |
| 偏好优化侧翼 | 不 rollout 也能做偏好对齐吗 | DPO |
| 工程执行 | 为什么 verl 要这样拆 trainer、worker、rollout engine | HybridFlow / verl |
| 训练-推理不一致 | rollout policy 和 train policy 不一致怎么办 | Rollout correction |
PPO:让 policy 更新不要一脚踩太远
解决什么问题。 普通 policy gradient 更新太猛时,模型可能为了短期 reward 大幅偏离旧策略。PPO 用 clipped surrogate objective 限制每次 policy update 的幅度。
核心公式。
r_t(theta) = pi_theta(a_t|s_t) / pi_old(a_t|s_t)
L_clip = E_t[min(r_t A_t, clip(r_t, 1-eps, 1+eps) A_t)]LLM 里 a_t 就是第 t 个 response token。r_t 大于 1 表示新模型比旧模型更想生成这个 token;A_t 大于 0 表示这条轨迹值得鼓励。
小白版解释。 PPO 像是在说:答得好的输出可以提高概率,但一次别提高太多;答得差的输出可以降低概率,也别一刀切太狠。clip_ratio 就是这根安全绳。
verl 对应源码。
verl/trainer/ppo/ray_trainer.py::fit():先 rollout,再重算old_log_probs,再 update actor。verl/trainer/ppo/core_algos.py::compute_policy_loss_vanilla():计算ratio = exp(log_prob - old_log_prob)、actor/pg_clipfrac、actor/ppo_kl。verl/trainer/ppo/core_algos.py::agg_loss():把 token 或 sequence 级 loss 聚合成标量。examples/ppo_trainer/run_qwen3_8b_fsdp.sh:algorithm.adv_estimator=gae、ROLLOUT_N=1、actor/critic 都启用。
建议先读章节。 GSM8K PPO,再看 调试指标 里的 actor/pg_clipfrac 和 actor/ppo_kl。
GAE:把最终 reward 分摊到 token
解决什么问题。 PPO 需要 advantage。只有最终 reward 时,直接把 reward 当每个 token 的学习信号方差很大;critic/value baseline 可以降方差,但会引入一点偏差。GAE 用 gamma 和 lam 控制这个取舍。
核心公式。
delta_t = r_t + gamma V(s_{t+1}) - V(s_t)
A_t^GAE = delta_t + gamma * lam * delta_{t+1} + ...
return_t = A_t + V(s_t)小白版解释。 critic 像一个“预期分数估计器”。如果实际结果比它预期更好,advantage 为正;比预期更差,advantage 为负。
verl 对应源码。
verl/trainer/ppo/core_algos.py::compute_gae_advantage_return():倒序扫 response token,计算delta、lastgaelam、returns,最后对 advantage 做 masked whiten。verl/trainer/ppo/ray_trainer.py::compute_advantage():algorithm.adv_estimator=gae时调用 GAE。examples/ppo_trainer/run_qwen3_8b_fsdp.sh:PPO 示例有critic.model.path和critic.optim.lr,GRPO 示例没有 critic。
建议先读章节。 GSM8K PPO 的 advantage/update 部分。
InstructGPT / RLHF:SFT、奖励模型、PPO、KL 的经典三段式
解决什么问题。 预训练模型会续写文本,但不一定听用户指令。InstructGPT 把人工示范、偏好奖励模型、PPO 优化串成一条 RLHF pipeline,并用 KL 约束避免模型偏离 reference 太远。
核心思想。
SFT policy -> collect comparisons -> train reward model
PPO reward = reward_model_score - beta * KL(policy || reference)小白版解释。 先教模型“人类喜欢什么样的回答”,再让模型自己生成回答,用奖励模型打分,同时用 KL 拉住它别为了刷分变怪。
verl 对应源码。
verl/trainer/ppo/ray_trainer.py::apply_kl_penalty():token_level_rewards = token_level_scores - beta * kld。verl/trainer/ppo/core_algos.py::kl_penalty():支持kl、abs、mse、low_var_kl、full。docs/examples/config.rst:解释algorithm.use_kl_in_reward和actor_rollout_ref.actor.use_kl_loss。
建议先读章节。 如何写奖励函数 和 调试指标 的 KL 部分。
DeepSeekMath / GRPO:用组内相对分数替代 critic
解决什么问题。 PPO 的 critic 显存和训练成本都高。GRPO 对同一个 prompt 采样多条 response,用组内 reward 均值和标准差构造 advantage,不训练 value model。
核心公式。
G = rollout.n
A_i = (R_i - mean(R_1...R_G)) / (std(R_1...R_G) + eps)小白版解释。 同一道题让模型答 5 次。答得比自己这组平均好,就加概率;比平均差,就降概率。
verl 对应源码。
examples/grpo_trainer/run_qwen3_8b_fsdp.sh:algorithm.adv_estimator=grpo、actor_rollout_ref.rollout.n=${ROLLOUT_N:-5}、algorithm.use_kl_in_reward=False、actor_rollout_ref.actor.use_kl_loss=True。verl/trainer/ppo/ray_trainer.py::fit():先给每个原始 prompt 写uid,再repeat(repeat_times=rollout.n)。verl/trainer/ppo/core_algos.py::compute_grpo_outcome_advantage():按uid聚合 reward,得到组内相对 advantage。
建议先读章节。 GRPO 配置解读。
DAPO / Dr.GRPO:把 reasoning RL 从算法推到 recipe
解决什么问题。 纯 GRPO 还会遇到全对/全错组没有学习信号、长输出偏置、clip 不够灵活、过长回答刷 reward 等问题。DAPO 把动态采样、decoupled clip、token-level loss、overlong penalty 组合成可复现实验 recipe。
核心思想。
只训练“组内有差异”的 prompt
clip_ratio_low 和 clip_ratio_high 可分开
loss_agg_mode 控制 token/sequence 如何归一化
overlong_buffer 对接近最大长度的输出给线性惩罚小白版解释。 DAPO 不是只换一个公式,而是在训练现场加了很多“别浪费 batch、别奖励废话、别被长度骗”的规则。
verl 对应源码。
docs/algo/dapo.md:给出动态采样、clip-higher、token-level loss、overlong reward shaping 的配置形态。verl/trainer/ppo/core_algos.py::agg_loss():token-mean、seq-mean-token-sum、seq-mean-token-sum-norm、seq-mean-token-mean。verl/workers/reward_manager/dapo.py:overlong_buffer根据 response 长度扣分。verl/trainer/ppo/core_algos.py::compute_grpo_outcome_advantage():norm_adv_by_std_in_grpo=False对应 Dr.GRPO 风格。
建议先读章节。 GRPO 配置解读 的 loss_agg_mode 和 norm_adv_by_std_in_grpo。
DPO:不用 rollout 的偏好优化侧翼
解决什么问题。 RLHF 的 PPO 需要采样、reward model、KL、critic 或 advantage,训练链路复杂。DPO 从偏好数据直接优化 policy/reference 的 log-prob 差,不在训练中 rollout。
核心公式。
L_DPO = -log sigmoid(beta * [
log pi_theta(y_w|x) - log pi_ref(y_w|x)
- log pi_theta(y_l|x) + log pi_ref(y_l|x)
])小白版解释。 DPO 直接告诉模型:在同一个 prompt 下,chosen 的概率相对 rejected 应该更高;同时别偏离 reference 太远。
verl 对应源码。 DPO 不走 RayPPOTrainer.fit() 这条 rollout/reward/advantage 主线。读它的价值是帮你区分:RLVR 是在线采样后用可验证 reward;DPO 是离线偏好对。
建议先读章节。 学完 PPO/GRPO 后再读,不要一开始就混在一起。
RLOO / ReMax / REINFORCE++:无 critic 时怎样做 baseline
解决什么问题。 无 critic 的方法省资源,但 reward 方差会大。RLOO 用 leave-one-out 组内 baseline,ReMax 用 greedy rollout baseline,REINFORCE++ 系列改造 advantage 和 loss 来降低噪声。
核心思想。
REINFORCE: A_i = R_i
RLOO: A_i = R_i - mean(R_j, j != i)
ReMax: A_i = R_sampled - R_greedy_baseline小白版解释。 baseline 不是奖励本身,而是“本来预计能拿多少分”。减掉 baseline 后,训练信号变成“比预期好多少”。
verl 对应源码。
verl/trainer/ppo/ray_trainer.py::compute_advantage():非 GAE/GRPO 的 estimator 通过core_algos.get_adv_estimator_fn()分派。verl/trainer/ppo/ray_trainer.py::fit():AdvantageEstimator.REMAX会额外生成 greedy baseline,并写入reward_baselines。verl/trainer/ppo/core_algos.py:注册了 GRPO、RLOO、REINFORCE++ 等 advantage estimator。
建议先读章节。 先读 GRPO 配置解读,再回头比较这些 estimator。
DeepSeek-R1:为什么 RLVR 成为 reasoning 模型关键路径
解决什么问题。 DeepSeek-R1 展示了可验证任务上的大规模 RL 可以激发 self-reflection、verification、long CoT 等推理行为,也说明 reasoning post-training 不一定依赖人类标注的完整推理轨迹。
核心思想。 对数学、代码、STEM 这类可验证任务,用 reward 约束最终正确性和格式,让模型在 trial-and-error 中学会更强的推理策略。
小白版解释。 你不给模型“标准解题过程”,只给它“最后答案对不对”和一些格式规则;模型为了拿分,自己探索出更长、更会检查的思考过程。
verl 对应源码。
examples/data_preprocess/gsm8k.py、examples/data_preprocess/math_dataset.py:把可验证任务做成 parquet。verl/utils/reward_score/gsm8k.py、math_reward.py、math_dapo.py、math_verify.py:规则验证入口。examples/grpo_trainer/run_qwen3_8b_fsdp.sh:reasoning RLVR 的最小 GRPO 入口。
Rollout Correction:rollout policy 和 train policy 不一致怎么办
解决什么问题。 rollout 可能由 vLLM/SGLang 用低精度、异步或旧权重生成;actor update 时又用训练后端重算 logprob。两者概率不一致会让 on-policy 假设变弱。
核心思想。
log_ratio = old_log_probs - rollout_log_probs
IS weight = exp(log_ratio) 之后做截断
RS mask = 丢掉偏离过大的 token/sequence小白版解释。 训练时你以为“这是当前模型生成的回答”,但真实 rollout 可能来自另一个近似模型。correction 就是在估计“这条样本到底离训练 policy 有多远”。
verl 对应源码。
verl/trainer/ppo/rollout_corr_helper.py::compute_rollout_correction_and_add_to_batch():计算 IS weights、rejection mask、off-policy metrics。verl/trainer/ppo/ray_trainer.py::fit():在token_level_rewards之后、advantage 之前应用 rollout correction。verl/utils/debug/metrics.py::calculate_debug_metrics():记录 rollout 与 actor 概率差。
建议先读章节。 调试指标 的 rollout correction 部分。
HybridFlow / verl:为什么工程架构长这样
解决什么问题。 RLHF/RLVR 不是单模型训练:actor、critic、reference、reward、rollout engine 都要协作,且训练和推理需要不同并行策略。HybridFlow 用 single-controller + multi-controller 混合方式组织复杂 dataflow。
核心思想。 上层 trainer 用单控制器描述算法流程;底层 worker 负责分布式模型计算;rollout engine 和 training backend 通过 checkpoint/weight sync 连接。
小白版解释。 RayPPOTrainer.fit() 像导演,worker group 像各部门,vLLM/SGLang 像外景拍摄组。导演不亲自拍每一帧,但要决定什么时候生成、什么时候打分、什么时候更新权重。
verl 对应源码。
verl/trainer/ppo/ray_trainer.py::fit():PPO/GRPO dataflow 主线。verl/workers/reward_manager/*:rule reward / reward model 管理。verl/workers/engine_workers.py:actor update、logprob、reward/value 等 worker 入口。examples/ppo_trainer/run_qwen3_8b_fsdp.sh、run_qwen3_8b_megatron.sh:同一算法切换 FSDP/Megatron backend。
建议先读章节。 读完 examples 三页后再回来看 HybridFlow,不然架构词会显得很抽象。
本节参考与延伸阅读
- PPO: Proximal Policy Optimization Algorithms
- GAE: High-Dimensional Continuous Control Using Generalized Advantage Estimation
- InstructGPT: Training language models to follow instructions with human feedback
- DeepSeekMath / GRPO
- DAPO
- DPO
- ReMax
- Back to Basics / REINFORCE-style RLHF
- DeepSeek-R1
- HybridFlow / verl
- 本地源码:
verl/trainer/ppo/ray_trainer.py、core_algos.py、rollout_corr_helper.py、workers/reward_manager/*。 - 本地文档:
docs/algo/ppo.md、docs/algo/grpo.md、docs/algo/dapo.md、docs/examples/config.rst。