PPO(Proximal Policy Optimization)是 RLHF(Reinforcement Learning from Human Feedback)流程中最常用的强化学习算法:ChatGPT、Claude 早期的对齐训练都用它,把一个已经会说话的模型,调整成”说话方式更符合人类偏好”的模型。这篇文章按照”为什么需要它 → 怎么推导出来 → 怎么套进 LLM 训练”的顺序拆解 PPO,重点放在它在 LLM RLHF 场景下的具体形态,而不是经典 Atari/MuJoCo 场景。

1. RLHF 全貌与 MDP 形式化

典型的 RLHF 流程分三步:SFT——用人工标注的高质量问答对,对预训练模型做监督微调,得到一个”听得懂指令”的基础策略;训练 Reward Model(RM)——让标注员对同一个 prompt 的多个模型输出排序,训练出一个输入 prompt xx 与回应 yy、输出一个标量分数的评分模型 rϕ(x,y)r_\phi(x, y),代表”人类会有多喜欢这个回应”;PPO 阶段——把 SFT 模型当成初始策略,用 RM 给的分数当作强化学习的奖励信号,持续优化这个策略。PPO 只处理第三步,但它能不能训好,很大程度取决于前两步的质量——如果 RM 学得不准,PPO 会忠实地把策略优化到 RM 的盲点上(这就是后面会谈到的 reward hacking)。

要把语言生成套进强化学习的框架里,首先要把生成过程翻译成 MDP(马尔可夫决策过程)的语言:状态(state)sts_t 是 prompt 加上目前为止已生成的词元,即 st=(x,y<t)s_t = (x, y_{<t})动作(action)ata_t 是下一个要生成的词元,动作空间就是整个词表(通常有数万到十几万个词元);策略(policy)πθ\pi_\theta 就是 LLM 本身,πθ(atst)\pi_\theta(a_t \mid s_t) 是模型在目前上下文下、对下一个词元的概率分布;一个回合(episode)是一次完整的生成,从 prompt 开始直到 EOS 或截断为止。奖励(reward)是跟经典强化学习差异最大的地方:RM 通常只在生成结束后对整个序列打一次分,中间任何一个词元都不会从”环境”得到即时奖励,因此原始的 reward 是序列级、稀疏的,只落在最后一个词元上。这个结构——“每个词元是一步决策,但奖励只出现在最后”——决定了后面 GAE 与 KL 惩罚需要如何设计。

2. 从策略梯度到裁剪目标

强化学习最直接的做法是策略梯度:直接对期望回报 J(θ)=Eτπθ[R(τ)]J(\theta) = \mathbb{E}_{\tau \sim \pi_\theta}[R(\tau)] 取梯度。REINFORCE 给出的估计式是:

θJ(θ)=Et[θlogπθ(atst)Rt]\nabla_\theta J(\theta) = \mathbb{E}_t\big[\nabla_\theta \log \pi_\theta(a_t \mid s_t) \cdot R_t\big]

直觉很简单:如果采样到的一条轨迹的回报 RtR_t 为正,就提高这个被采样到的动作的概率;为负就降低。问题在于 RtR_t 来自蒙特卡洛采样,方差很高,序列一长(LLM 生成常常上百个词元)、动作空间一大(词表数万个词元),方差会被进一步放大。标准的解法是引入一个 baseline(通常是状态价值函数 V(st)V(s_t)),把原始回报换成优势函数(advantage)At=RtV(st)A_t = R_t - V(s_t)——衡量”这个动作比这个状态下的平均水平好多少”,均值为零,方差明显降低:

θJ(θ)=Et[θlogπθ(atst)At]\nabla_\theta J(\theta) = \mathbb{E}_t\big[\nabla_\theta \log \pi_\theta(a_t \mid s_t) \cdot A_t\big]

策略梯度还有一个更棘手的问题:更新幅度很难控制。学习率稍微大一点,单次梯度更新就可能让策略输出的分布产生剧烈变化——对 LLM 来说,这可能表现为模型几步之内就开始输出乱码,或者崩塌成一种能骗过 RM、但语言质量很差的退化模式。这种偏移一旦发生就很难恢复,因为下一批采样已经是从被破坏的策略生成的。TRPO(Trust Region Policy Optimization)的做法是显式约束新旧策略之间的 KL 散度,把每次更新限制在一个”信任域”内——但这需要求解一个带约束的二阶优化问题,实现复杂且开销大。PPO 的出发点是:能不能用更简单的一阶方法达到类似的效果?

PPO 的答案是用概率比来衡量新旧策略的差异:rt(θ)=πθ(atst)/πθold(atst)r_t(\theta) = \pi_\theta(a_t \mid s_t) / \pi_{\theta_{\text{old}}}(a_t \mid s_t)。如果直接优化 rt(θ)Atr_t(\theta) A_t,当 AtA_t 很大时,优化过程会激进地把 rt(θ)r_t(\theta) 往上推,导致策略一步就跨出信任域。PPO 的裁剪目标把这个比值压进一个固定区间:

LCLIP(θ)=Et[min(rt(θ)At, clip(rt(θ),1ϵ,1+ϵ)At)]L^{\text{CLIP}}(\theta) = \mathbb{E}_t\Big[\min\big(r_t(\theta)\, A_t,\ \text{clip}(r_t(\theta),\, 1-\epsilon,\, 1+\epsilon)\, A_t\big)\Big]

ϵ\epsilon 通常取 0.1 到 0.2 之间。当 At>0A_t > 0(这个动作比平均水平好)时,一旦 rt(θ)r_t(\theta) 超过 1+ϵ1+\epsilon,裁剪后的项就不再增长,梯度也就不再有诱因继续把 rtr_t 往上推;At<0A_t < 0 时对称成立——一旦 rt(θ)r_t(\theta) 低于 1ϵ1-\epsilon,目标函数也不会因为 rtr_t 继续降低而变得更好。取 min\min 而不是直接用裁剪后的值,是为了保证这个目标永远是真实目标的悲观下界——不管 AtA_t 的正负号是什么,实际被优化的永远是较保守的那个估计,避免模型根据单一样本的优势估计做出过度自信、不可逆的更新。这就是”proximal(近端)“名称的由来:用便宜的一阶方法,达到跟 TRPO”每次更新幅度有界”类似的效果。

3. GAE 与完整损失函数

裁剪目标里的 AtA_t 需要一个价值函数 Vϕ(st)V_\phi(s_t) 来估计。PPO 通常搭配 GAE(Generalized Advantage Estimation)来计算优势:

δt=rt+γVϕ(st+1)Vϕ(st),AtGAE(γ,λ)=l=0(γλ)lδt+l\delta_t = r_t + \gamma V_\phi(s_{t+1}) - V_\phi(s_t), \qquad A_t^{\text{GAE}(\gamma,\lambda)} = \sum_{l=0}^{\infty} (\gamma\lambda)^l\, \delta_{t+l}

δt\delta_t 是单步 TD 误差,λ\lambda 控制”往前看多远”:λ=0\lambda=0 时 GAE 退化成只用单步 TD 误差(低方差、高偏差);λ=1\lambda=1 时退化成蒙特卡洛优势估计(高方差、无偏),常用取值在 0.9 到 0.97 之间权衡。在 LLM RLHF 场景下,因为原始奖励只落在序列的最后一个词元,Vϕ(st)V_\phi(s_t) 需要学会”预测从目前生成位置到结束,最终会拿到的 RM 分数”——这正是为什么需要额外训练一个 Critic(价值模型),通常从 SFT 模型或 RM 初始化,把输出维度改成每个词元位置输出一个标量。

实务上 PPO 的总损失是三项的组合:

L(θ,ϕ)=LCLIP(θ)c1LVF(ϕ)+c2S[πθ](<st>)L(\theta, \phi) = L^{\text{CLIP}}(\theta) - c_1 \, L^{\text{VF}}(\phi) + c_2 \, S[\pi_\theta](<s_t>)

LCLIP(θ)L^{\text{CLIP}}(\theta) 是前面推导的裁剪策略目标,用来训练 Actor;LVF(ϕ)=(Vϕ(st)Vttarget)2L^{\text{VF}}(\phi) = (V_\phi(s_t) - V_t^{\text{target}})^2 是 Critic 的回归损失(其中 Vttarget=AtGAE+Vϕ(st)V_t^{\text{target}} = A_t^{\text{GAE}} + V_\phi(s_t));S[πθ](<st>)S[\pi_\theta](<s_t>) 是策略的熵,加上熵奖励是为了鼓励策略保留一定的随机性,避免过早坍缩成确定性输出。c1c_1c2c_2 分别控制价值损失与熵奖励的权重。

4. LLM RLHF 中的 PPO:四个模型与 KL 惩罚

经典强化学习场景(例如 MuJoCo)里,PPO 只需要 Actor 和 Critic 两个网络。LLM RLHF 的 PPO 阶段要同时在显存里保留四个模型,这是相比经典 PPO 最大的工程挑战:

模型是否训练角色
Policy / Actor训练目前正在优化的 LLM,从 SFT 模型初始化,负责生成回应
Reference Model冻结SFT 模型的只读副本,只用来计算 KL 惩罚,避免 Policy 偏离太远
Reward Model冻结单独训练好的评分模型,只在序列结尾给出一个标量分数
Critic / Value Model训练预测每个词元位置的价值 Vϕ(st)V_\phi(s_t),为 GAE 提供优势估计

四个模型的参数量往往相近(Critic 与 Reward Model 有时共享主干、只差输出头),这代表 PPO 阶段的显存和算力开销大致是纯推理的好几倍——这也是后续 GRPO 等变体想要砍掉 Critic 的直接动机。

只靠 RM 给的分数当奖励,很容易让策略往 RM 的盲点方向优化,而不是真的变好。RLHF 的标准做法是把 Policy 与 Reference Model 之间的 KL 散度当作惩罚项,加进奖励里:

rt=1[t=T]rϕ(x,y)只在最后一个词元给出    βlogπθ(atst)πref(atst)r_t = \underbrace{\mathbb{1}[t = T] \cdot r_\phi(x, y)}_{\text{只在最后一个词元给出}} \;-\; \beta \log\frac{\pi_\theta(a_t \mid s_t)}{\pi_{\text{ref}}(a_t \mid s_t)}

这个 KL 惩罚是逐词元计算的,每一步都会加进奖励里,而 RM 的分数只出现在最后一个词元——GAE 会透过时序差分的信用分配,自动把末尾的 RM 分数往前传播到较早的词元,而每一步自身的 KL 偏移也会立刻计入奖励,两者不冲突。β\beta 的取值非常敏感:太小起不到约束作用,容易 reward hacking;太大策略几乎不敢偏离 SFT 模型,学不到 RM 想教的偏好。InstructGPT 等工作用自适应 KL 控制:持续监控实际 KL 值与目标 KL 之间的偏差,用一个简单的比例控制器动态调整 β\beta,而不是固定成一个常数。

5. 实务上的坑与 PPO 之外的变体

工程实务上常见的坑:Reward hacking——策略找到 RM 评分函数的漏洞,而不是真的提升质量,常见表现是回应异常冗长(RM 偏好长回应)或堆砌 RM 恰好喜欢的措辞;缓解方式包括 KL 惩罚、RM 集成,以及定期用新数据重新训练 RM。PPO epoch 数太多是个问题——对同一批 rollout 数据重复太多次梯度更新,会让策略对这批数据过拟合,KL 也会很快失控;LLM RLHF 通常只做 1–2 个 epoch,比经典强化学习场景保守得多。Value Model 训练不稳定——Critic 要从头学会预测最终的 RM 分数本来就不容易,训练初期噪声很大的价值估计会污染优势估计,因此有些实践会先单独热身 Critic。奖励归一化——RM 的分数尺度在不同批次之间可能漂移,常用滑动平均/标准差归一化(whitening)来处理。采样参数——rollout 阶段用的 temperature/top-p 直接决定探索到的动作分布,进而影响优势估计的方差,是一个容易被忽略、但影响很大的超参数。

PPO 的主要代价是”重”:需要单独训练 RM、维护 Critic、同时跑四个模型。这两年的变体各自砍掉了一部分复杂度:**DPO(Direct Preference Optimization)**完全跳过显式的 RM 和 PPO 的采样-更新循环,直接从人类偏好对 (yw,yl)(y_w, y_l) 构造一个对比损失函数,用监督学习的方式优化策略——代价是假设了 Bradley-Terry 偏好模型,并且失去了在线探索的灵活性。**GRPO(Group Relative Policy Optimization,DeepSeekMath 提出)**保留了 PPO 的裁剪目标和在线 rollout,但去掉了 Critic——对同一个 prompt 采样一组输出,用这组输出的奖励均值和方差对每个输出的优势做归一化,直接省掉一个跟 Policy 同等规模的模型,是目前 LLM 推理能力训练中很受欢迎的简化方向。两者都是用”PPO 的通用性”换”更少的模型、更简单的流程”——PPO 依然是最成熟、最通用的基线,但如果偏好数据足够规整,或者可以接受用组内相对奖励做近似,更轻量的变体往往有更好的性价比。

归根结底,PPO 在 LLM RLHF 里做的事情,是在”用 RM 的分数改进模型”这件事上,套上”任何一次更新都不能偏离参考模型太远”的约束:裁剪代理目标限制单次更新的幅度,GAE 把稀疏的序列级奖励合理地分配到每个词元上,KL 惩罚在奖励层面再加一道防线。理解这三块各自解决什么问题,比背下最终目标函数的公式更重要。