前置阅读:本文假设你已经知道强化学习是什么、大模型为什么需要它,以及”策略/奖励/RLHF”这几个词的含义。如果还没有,强烈建议先读《示范教不会的东西:为什么大模型需要强化学习》——那篇不用一个公式,把这些地基铺好了再来,这篇会顺畅得多。
读 GRPO、RLOO、REINFORCE++ 这些新算法的论文时,PPO 总是那个被一句话带过的背景板:“传统 PPO 需要一个昂贵的 critic……”。但如果你只知道这一句,你其实读不懂它们在改什么。PPO 是现代 RL 后训练的坐标原点——之后所有算法都是对它训练循环里某个部件的替换或删减。这篇不背公式,我们从一个根本矛盾出发,把这个循环的每个部件推导出来:你会看到 ratio、clip、critic、GAE、多 epoch 复用,没有一个是拍脑袋加的,全是同一对矛盾的产物。
一句话主线
先把结论放在这里:
PPO 的一切设计都在解决同一对矛盾:采样太贵,所以必须把一批数据反复用;反复用会让策略偏离数据的来源,所以必须限制每次更新的偏离幅度。
“Proximal”(近端)这个词,说的就是后半句。带着这句话往下读,每个部件出场时你都能预判它为什么出现。
起点:REINFORCE,以及它的两个致命伤
策略梯度方法的原始形态是 REINFORCE(1992)。思路极简:让策略 跑出一条轨迹,拿到总回报 ,然后按回报大小调整每一步动作的概率——
直觉翻译:回报高的轨迹,里面每个动作的概率都调高;回报低的,都调低。 就这么朴素。
但这个公式里藏着两个致命伤,整篇文章后面的一切都是在治它们:
致命伤一:数据用一次就作废。 注意期望的下标是 ——梯度必须用当前策略自己采的数据来估计。你用这批数据做一次梯度更新, 变了,这批数据就不再来自当前策略,理论上就失效了。这叫 on-policy(同策略) 约束。
在经典控制任务里这只是低效;在大模型场景里这是灾难——采一条”轨迹”意味着让模型完整生成一段回答,rollout 的成本占了 RL 训练的大头。采一批昂贵的数据,只做一次梯度更新就扔,等于用茅台涮锅。
致命伤二:方差大到没法用。 是整条轨迹的回报,噪声极大。同一个好动作,出现在一条运气差的轨迹里就被惩罚,出现在运气好的轨迹里就被过度奖励。信号淹没在噪声里,训练慢且不稳。
治第一个伤,得到 ratio 和 clip;治第二个伤,得到 critic 和 GAE。我们一个一个来。
治”数据一次性”:从 ratio 到 clip
第一步:重要性采样,让旧数据合法
想复用旧策略 采的数据来更新新策略 ,统计学早有标准工具:重要性采样(importance sampling)——用一个分布的样本估另一个分布的期望,只要给每个样本乘上两个分布的概率比作为权重。于是目标函数变成:
这个 就是 PPO 公式里那个著名的比率(ratio)。它的含义很直白:新策略比旧策略更倾向这个动作多少倍。 是优势(advantage),先理解为”这个动作比平均水平好多少”,下一节细讲。
现在旧数据合法了,一批数据可以做多次更新。但新问题立刻出现——
第二步:偏得越远,估计越不可信
重要性采样有个众所周知的软肋:两个分布差得越远,权重方差越大,估计越不可信。放到这里:你用旧数据更新得越狠, 离 越远,这个目标函数就越是自欺欺人——它会告诉你”目标值在涨”,而真实性能可能已经跳崖。策略梯度训练里著名的”性能突然崩掉再也起不来”(policy collapse),根源多半在此。
所以必须给”偏离幅度”上一道约束。2015 年的 TRPO(Trust Region Policy Optimization,信赖域策略优化)给出了理论严格的答案:每次更新时约束新旧策略的 KL 散度不超过一个阈值,在这个”信赖域”内优化。理论漂亮,工程昂贵——它需要二阶优化(计算 Fisher 矩阵与共轭梯度),实现复杂,而且和参数共享、dropout 等常规技巧犯冲。
第三步:clip——用一行代码近似信赖域
PPO(2017,Schulman 等)的贡献,本质是把 TRPO 的二阶约束换成一个一阶的、写出来只有一行的目标函数:
通常取 0.2。这个公式值得拆开看,因为它是全文最容易被误解的地方。
常见的误解是:“clip 把 ratio 限制在 之间。” 不对。clip 不是投影、不是硬约束,ratio 完全可以跑出这个区间。它做的事情更微妙:取消超出区间之后的激励。
分两种情况看:
- (这个动作比平均好,想加大概率):当 涨过 ,clip 项变成常数 ,min 选中它——梯度变成零。优化器失去继续加大这个动作概率的动力,但也没有任何力量把它拉回来。
- (这个动作比平均差,想压低概率):当 跌破 ,同理梯度归零,停止继续压。
再看那个 min:它让目标函数成为原始目标的悲观下界——只在”更新让目标看起来变好”的方向上截断激励,而在”变差”的方向上不截断(比如 但 因为其他样本的梯度被带到了 以下,此时 min 选中未截断项,梯度仍然存在,会把它修正回来)。一句话:clip 对”贪功”设限,对”纠错”不设限。
这就是”Proximal”的全部含义:不硬性锁死策略,只是让每一批数据能提供的更新动力有个天花板,策略自然就走不远。代价是它只是信赖域的粗糙近似——所以成熟实现(如 OpenAI Spinning Up)还会额外监控新旧策略的近似 KL,超阈值就提前终止本批更新。工程上防线从来不止一道。
治”方差大”:critic 与 GAE
回头治第二个致命伤。REINFORCE 用整条轨迹的回报 当信号,噪声太大。降方差的思路是:别问”这个动作后来拿了多少分”,问”这个动作比我在这个状态下的平均预期好多少”。 后者就是优势:
减掉的 叫基线(baseline)。数学上可以证明减基线不改变梯度的期望,只降方差——这是免费的午餐。但 (“从这个状态出发,按当前策略平均能拿多少回报”)没人告诉你,得再训练一个网络去估计它。这个网络就是 critic(价值网络)。它不参与决策,只当”记分参谋”:告诉 actor 每个状态值多少分,好让 actor 知道自己每一步是超常发挥还是失常。
这就是 PPO 需要两个网络的原因——不是设计者贪心,是降方差的代价。GRPO 那句”省掉 critic”省的就是它,代价我们最后再算。
critic 有了,优势具体怎么算?这里有一个经典的偏差-方差光谱:
- 用完整回报减基线:无偏,但方差大(又回到老问题);
- 用单步 TD 残差 :方差小,但严重依赖 critic 估得准不准——critic 不准就有偏。
GAE(Generalized Advantage Estimation,广义优势估计)用一个参数 把两端连成滑竿:
退化为单步 TD(低方差高偏差), 退化为完整回报(高方差无偏),实践常取 0.95——这不是玄学调参,是你对”critic 有多可信”的显式表态:越信 critic, 越小。
把零件装回去:训练循环本体
现在所有零件都有了,而且每个零件的存在理由都清楚了。装配起来,这就是 PPO 的训练循环:
初始化 actor π_θ 和 critic V_φ
循环直到收敛:
# 阶段一:采集(rollout)—— 贵,所以后面要榨干
用当前策略 π_θ_old 与环境交互,采集一批轨迹
记录每一步的 (s_t, a_t, r_t) 和 log π_θ_old(a_t|s_t)
# 阶段二:打分 —— 为复用做准备,全部只算一次
用 critic 给每个状态估值 V_φ(s_t)
用 GAE 算出每一步的优势 A_t,并归一化
优势 + 估值 → 回报目标 R_t = A_t + V_φ(s_t)
# 阶段三:榨干这批数据 —— ratio 和 clip 在这里兑现
重复 K 个 epoch(典型 3~10):
把这批数据打乱、切成 minibatch,对每个 minibatch:
算 ratio r_t = π_θ(a_t|s_t) / π_θ_old(a_t|s_t)
L = L_CLIP(θ) # 策略损失(clip 目标)
- c1 · (V_φ(s_t) - R_t)² # critic 回归损失
+ c2 · 熵奖励 # 防止策略过早变成确定性
梯度下降更新 θ 和 φ
# 阶段四:换代
θ_old ← θ,旧数据作废,回到阶段一
对着主线复查一遍:阶段一贵,所以有阶段三的 K 个 epoch 复用;复用需要合法性,所以有 ratio;复用会偏离,所以有 clip;信号要降噪,所以阶段二有 critic 和 GAE。没有一行是多余的。
两个容易被忽略的细节,恰恰最能检验你是否真懂这个循环:
- 优势在阶段二算一次就冻结,K 个 epoch 里不重算。重算需要重新 rollout,那就失去复用的意义了。所以 epoch 越往后,你在用”越陈旧的优势”指导”越新的策略”——这正是 K 不能取太大的原因,clip 只能兜住一部分。
- 在采样时就记下来,阶段三只需要对新策略做前向。第一个 epoch 的第一个 minibatch 上 、clip 完全不起作用——clip 是为第 2 到第 K 个 epoch 准备的。如果你只打算每批数据用一次,PPO 就退化成带基线的 actor-critic,clip 形同虚设。
落到大模型:RLHF 里的同一个循环
上面的循环是通用形态。落到 RLHF(基于人类反馈的强化学习)训练大模型时,骨架一模一样,只是每个格子填入了具体内容——外加多出两个模型。
先对齐 MDP 的对应关系:状态 = prompt 加上已生成的 token 前缀,动作 = 生成下一个 token,一条轨迹 = 一次完整回答。奖励很稀疏:只有回答结束时,奖励模型(Reward Model)才给整段回答打一个分。
于是循环里同时站着四个模型:
| 角色 | 训练吗 | 在循环哪个阶段出场 | 干什么 |
|---|---|---|---|
| Actor(策略) | ✅ | 阶段一采样,阶段三更新 | 生成回答,是唯一最终交付的模型 |
| Critic(价值) | ✅ | 阶段二估值,阶段三更新 | 给每个 token 位置估”从这往后平均能拿几分” |
| Reward Model | ❄️ 冻结 | 阶段二 | 给完整回答打分,人类偏好的代理 |
| Reference(参考) | ❄️ 冻结 | 阶段二 | 初始 SFT 模型的副本,算 KL 用 |
Reference 模型是新面孔,它对应一道新增的防线:每个 token 的实际奖励里要扣一项与参考模型的 KL 惩罚,。注意它和 clip 管的不是同一件事:clip 管的是”这一批更新别离上一批太远”(步子别太大),KL 惩罚管的是”整场训练别离出发点太远”(别为了讨好奖励模型把语言能力练没了,即 reward hacking)。 一个是局部约束,一个是全局锚点,缺一不可。
四个模型(常常同样大)同时驻留显存、交替执行推理和训练——这就是 RLHF 工程上臭名昭著的复杂度来源,也是 verl 那篇里 RayPPOTrainer.fit() 要用一整套混合引擎调度的原因:那个 fit() 函数里的 generate → reward → advantage → update 各阶段,与上面伪代码的四个阶段一一对应。读懂了本文的循环,那个 1300 行的 fit() 就只是它的分布式翻译。
现在可以算 GRPO 那笔账了
回到开头那句”传统 PPO 需要昂贵的 critic”。读到这里你应该能精确说出这句话省略了什么。
DeepSeek-R1 那篇讲过,GRPO 对同一道题采一组回答,用组内平均分当基线替代 critic。现在你有了看清这笔交易的坐标系:GRPO 砍掉的是”治方差”支线上的 critic 和 GAE,换上了统计学里最朴素的基线——样本均值;而”治数据一次性”支线上的 ratio 和 clip 原封不动地保留。训练循环的四阶段骨架也没变,只是阶段二从”critic 估值 + GAE”换成了”组内减均值除标准差”。
这笔交易什么时候划算?看你需不需要 critic 提供的那个东西:逐 token 的信用分配(credit assignment)。
- 结果可验证、奖励只在结尾(数学题对错、代码过不过测试):整条回答共享一个分数,critic 试图估计的”中间每一步值多少分”本来就没有可靠的监督信号,估不准的 critic 只贡献偏差。砍掉它,用组内相对分,便宜且没损失什么——这就是 R1 的场景,GRPO 赢。
- 过程奖励密集、或轨迹极长、或每次采样极贵:你负担不起对每个 prompt 采一大组回答,或者你确实有逐步的奖励信号需要沿轨迹分摊——这时 critic 的估值是真金白银的方差削减,PPO 仍是更稳的选择。
换句话说,GRPO 不是 PPO 的升级版,是 PPO 在”结尾一锤定音式奖励”这个特定场景下的减配版——减得恰到好处,是因为那个场景里被减掉的部件本来就发挥不了作用。脱离场景说谁先进,是伪问题。
带走的模型
最后把这篇压缩成三条,方便你带去读任何后续算法:
- PPO = 策略梯度 + 数据复用(ratio)+ 偏离限制(clip)+ 方差控制(critic/GAE)。 四个部件对应两对矛盾,没有装饰品。
- 训练循环四阶段:采集 → 打分 → 榨干 → 换代。 从 CartPole 到千卡 RLHF 集群,骨架不变,变的只是每个阶段的实现规模。
- 读新算法只需要问一个问题:它替换了循环里的哪个部件,用什么替换的,在什么场景下这个替换不亏? GRPO 换掉 critic(用组均值),RLOO 换掉 GAE(用留一法基线),DPO 干脆把整个循环折叠成一个监督损失。坐标原点在手,变体就不再是一堆需要分别记忆的名词。
下一次在论文里撞见”我们对 PPO 做了如下简化”,你就不再是听结论的人,而是能自己验算这笔账的人。