跳转至

PPO 算法核心组件

写出 PPO 的裁剪目标函数,并解释其每一项的含义。

PPO 的裁剪目标函数 (Clipped Surrogate Objective) 是用于更新 Actor (策略网络) 的核心损失函数。它的标准形式如下:

image.png

每一项的含义:

  • rt(θ):重要性采样比率 (probability ratio)。定义新旧策略在状态 st 下选择动作 atat 的概率之比:

image.png

image.png


PPO 中,为什么需要对重要性采样比率进行裁剪?不裁剪会出现什么问题?

image.png

不裁剪的第一个问题:策略崩溃与高方差

image.png

不裁剪的第二个问题:破坏信任区域

image.png

裁剪所起的作用:

image.png


裁剪率 ε 通常设为多少?过大或过小会怎样?

在 RLHF 的 PPO 实现中,ϵϵ 通常设在 0.1 到 0.2 之间,0.2 是最经典和常见的默认值。这个值是在大量实验基础上找到的一个平衡点。

如果 ε 过大(例如 0.5 甚至 1.0):

  • 裁剪约束变得非常宽松,几乎退化为普通的策略梯度方法。

  • 策略可能在单次更新中发生巨大跃迁,过大的步长会导致训练剧烈波动,出现“悬崖式”的性能崩塌,即 loss 或奖励突然变成 NaN。

  • 语言模型特别脆弱,一个过大的 ε 可能让模型一夜之间学会奖励黑客行为(如无意义地反复重复某个高分 token),而由于 KL 惩罚没有相应加强,模型可能迅速陷入这个局部最优并无法自拔。

如果 ε 过小(例如 0.01):

  • 裁剪约束过于严格,策略几乎被冻结在原地。

  • 每次更新的幅度极小,模型需要极多的训练步数才能有可见的优化效果,收敛速度极慢。

  • 在存在奖励模型噪声的情况下,过于保守的更新可能让模型完全停滞,无法有效放大那些微弱的正奖励信号,导致训练陷入瓶颈,无法达到更好的对齐效果。

自适应 ε 的高级实践:

一些工程实践中会引入动态调整 ε 的策略。例如,当监控到 KL 散度持续超出阈值时,主动降低 ε;当优化进展缓慢且 KL 很低时,可以适当增大 ε 以加快进度。但大多数稳健的基线仍使用固定的 0.2,并配合强大的 KL 惩罚。


什么是优势函数 A(s,a)?在 PPO 中如何利用它来降低梯度方差?

优势函数 A(s,a)A(s,a) 衡量的是“在状态 ss 下采取动作 aa,比在状态 ss 下的平均预期回报高出了多少”。其标准定义为:

image.png

image.png

在 PPO 中的应用: 在 PPO 的损失函数中,我们直接用优势 A^tA^t 作为每个 token 更新幅度的权重。这样一来,梯度更新不再关注那些“虽然回报高但本就是板上钉钉的事”,也不会错误惩罚那些“虽然回报低但已经尽力止损”的动作。梯度的噪声因此大大降低,训练过程更加稳定,模型能够更快地聚焦在真正有提升空间的决策点上。


写出 GAE(广义优势估计)的计算公式,并说明 λ 参数如何在偏差和方差之间权衡。

GAE 计算公式:

image.png

在实际实现中,由于序列长度有限,是反向迭代计算。

image.png

在 RLHF 中的实践: RLHF 中通常使用 λ≈0.95。因为 RLHF 的奖励是极度稀疏的(只在序列末尾有 RM 评分),单步 TD(λ=0)几乎无法在中间步骤学到任何有意义的信号,因此需要较大的 λ 让终点的奖励信息能够更充分地传播回前面的 token。


在语言生成任务中,GAE 的计算是如何沿着序列反向进行的?

语言生成是一个从左到右的序列,但 GAE 的计算必须从后往前逆向进行。其核心是反向迭代计算累积的 TD 误差。

image.png

实现细节: 在代码层面,我们会先创建一个与序列等长的全零张量 advantages,然后从 T-1 向下循环到 0,每一步都将当前计算的 At 填入对应的位置。同时,为了稳定训练,通常还会在批次内对所有优势进行标准化(减去均值除以标准差)。这种反向计算方式使得长序列的信用分配成为可能,将最后的奖励信号平滑地分配给了过程中的每一个 token。


PPO 中 Critic 的损失函数是什么?为什么通常使用 MSE 来拟合价值回报?

Critic 的损失函数:

image.png

有时也会对 Critic 的更新进行裁剪(value clipping),以防止对某个状态的价值估计发生剧烈跳变。其裁剪后的损失与 PPO 的 Actor 损失形式类似:

image.png

为什么使用 MSE: MSE 损失是一个凸函数,且梯度连续,非常易于优化。在强化学习的状态价值估计中,我们的目标 Gt 是一个连续且可能具有噪声的变量。MSE 天然适合拟合这种连续目标。它惩罚大的估计误差更重(平方惩罚),能强制 Critic 快速向真实的回报期望收敛。此外,MSE 的梯度简洁,计算开销极低,非常适合与 Actor 的损失一起进行大规模并行训练。


Critic 网络在 RLHF 中是否有必要?能否不用 Critic,而只使用蒙特卡洛回报?有什么代价?

Critic 网络在 RLHF 中是强烈建议保留的,放弃它将付出重大的代价。 我们可以完全不用 Critic,直接用序列的完整蒙特卡洛(MC)回报 Gt 减去一个基线来估计优势,例如 REINFORCE 算法。

代价一:方差爆炸

RLHF 的动作空间巨大,同样一个 prompt,模型可能生成完全不同质量的回答。最终回报的波动性非常大。如果我们使用 MC 回报,那么优势估计的方差就等于整个轨迹回报的方差。这意味着,在训练中,优势信号会极其嘈杂。模型可能会因为一次偶然采到的高分而被过度奖励,下一次又因为一次偶然的低分而被过度惩罚。这将导致训练震荡剧烈,收敛极其缓慢,甚至无法收敛。

代价二:无法进行逐 token 的信用分配

MC 回报是序列结束后才计算的,它在所有中间步骤上给的都是同一个回报值。这意味着,Critic 所起到的“预测未来”能力完全丧失。Actor 无法得知在生成第 5 个 token 时,它的决策是让后续走向了更好还是更差的境地。它只能盲目地根据最终结果的好坏来整体调整所有 token 的概率。这种粗粒度的信用分配会严重拖慢学习进度,特别是对于长序列生成。

代价三:无法利用 TD 学习的 bootstrap 能力

Critic 的引入使得 GAE 可以利用后续状态的估计来“截断”优势的传播,这就是 bootstrap。不使用 Critic,就完全失去了这种能力,GAE 的优势也退化为单纯的 MC 估计,失去了其在偏差-方差之间灵活权衡的优势。

因此,虽然理论上可以去掉 Critic,但在 RLHF 这种高方差、长序列的任务中,Critic 是保证训练稳定和高效的核心组件。


为什么 PPO 需要维护一个“旧策略”(π_old)?在线策略更新的稳定性体现在哪里?

image.png

在线策略更新的本质:

image.png

稳定性的体现——信任区域:

image.png

实践中的维护:

在 RLHF 代码中,“旧策略”通常是当前 Actor 在做采样之前的一份参数快照。采样完成后,这个快照被冻结,用于整个批次的后续多轮 PPO 更新。批次更新完后,这份快照即被丢弃,下一轮采样前再生成新的快照。这就是 PPO 在线策略特性的体现。


说明 PPO 中“多轮更新”(Multiple Epochs)的做法:一个经验批次通常被重复使用多少次?为什么这样能提高样本效率但又带来风险?

多轮更新的做法:

image.png

提高样本效率的原理:

image.png

PPO 的应对措施:

  1. 裁剪机制:这是核心防线。当策略偏离过大时,裁剪会截断梯度,阻止更新,从而在用旧数据多次训练时,自动限制策略的变化范围。

image.png

  1. 适中的 K 值:经过大量实验,2-4 个 epoch 是在风险和收益之间的平衡点。过多的 epoch 几乎必然导致策略崩溃。

PPO 的样本效率和离线 RL 算法(如 DQN)相比如何?为什么 RLHF 通常用 PPO 而不是 Q-learning?

样本效率对比:

PPO 是一种 on-policy 算法,其样本效率远低于 DQN 等 off-policy 算法。DQN 使用经验回放池,可以将过去很久的数据都存储起来反复使用,数据利用率极高。而 PPO 必须使用由当前最新策略生成的数据(或经过重要性采样修正的、非常近期的数据),一旦策略更新,旧数据就被丢弃。PPO 的每一次参数更新,消耗的样本都是“一次性”的。

为什么 RLHF 不用 Q-learning 而用 PPO?

image.png

  1. 对分布变化的鲁棒性:RLHF 的奖励模型是一个静态的、冻结的网络。在训练中,策略(Actor)的生成分布会逐步漂移,进入 RM 的分布外区域。Off-policy 的 Q-learning 由于依赖大型的经验池,其中会同时包含来自新、旧多种策略的样本,这种分布的不一致性会让 Q 值估计变得极度不稳定。PPO 的 on-policy 特性意味着它的每次更新只基于当前最新策略产生的、同分布的样本,避免了这种分布不匹配问题,训练更稳定。

  2. 与 SFT 起点的兼容性:RLHF 必须从一个强大的 SFT 模型开始。PPO 的 Actor 可以直接继承 SFT 的参数,Critic 也可以从 SFT 初始化。这与策略梯度架构无缝衔接。而将 SFT 模型改造成一个 Q 网络,则会面临更大的架构鸿沟和训练不稳定性。

因此,尽管 PPO 样本效率低,但它是目前能够稳定处理超大规模动作空间和复杂策略的最佳选择。


在 RLHF 中,PPO 是如何处理动作空间(词表)巨大的问题的?重要性采样在词表上如何计算?

处理巨大词表的核心手段——直接在概率分布上操作: PPO 的策略 πθπθ 是一个语言模型,其输出就是词表大小的 logits。我们不需要像 DQN 那样去穷举所有动作的值,而是只关注实际被采样到的那个 token。在计算损失时,我们只提取模型在那个被采样 token 上的对数概率。对于其他 49999 个没有被选中的 token,它们的概率变化不会影响这个特定样本的损失。这使得计算量与动作空间的大小解耦。

重要性采样比率的计算:

image.png

这个计算极其高效,因为它只涉及两个向量的查值和求差。它完美避开了对整个词表 softmax 的遍历,使得在巨大词表上的 PPO 训练成为可能。


如果策略的熵在训练中急剧下降,说明了什么?应如何处理?

熵急剧下降的警示信号:

image.png

说明了什么:

  1. 模式坍塌的前兆:模型可能已经发现了一条能稳定获得高奖励的“捷径”(例如,总是回答“当然,我很乐意帮助您...”的客套话模板),并迅速将所有概率质量集中到这条捷径上,完全丧失了探索其他更优策略的可能性。

  2. 过拟合到奖励模型的表面特征:奖励模型可能对某些特定的 token(如特定的礼貌用词)有着异常高的偏好。策略很快抓住了这一点,并疯狂地、重复地利用它,导致输出单一化。

  3. KL 惩罚与奖励信号的失衡:KL 惩罚是为了防止策略偏离太远,但如果它设得太小,或者奖励模型的信号过于强烈,策略就会不惜一切代价去追逐高分,哪怕输出变得极其单调,从而体现为熵的崩塌。

应对策略:

image.png

  1. 重新调校 KL 惩罚系数 β:增大 β 可以更强地约束策略不要偏离参考模型太远,因为参考模型通常具有健康的熵。

  2. 降低裁剪率 ε:减小 ε 可以限制每步更新的最大幅度,防止策略过快地涌入一个狭窄的高奖励区域。

  3. 检查并改进奖励模型:分析是哪些回答让策略的熵降低了,很可能发现了 RM 的系统性偏见(如长度偏见、格式偏见)。需要用更鲁棒的对抗样本来修复 RM。


PPO 中是否可以使用“预训练梯度混合”(PPO-ptx)来防止语言能力退化?具体怎么加?

可以,这正是在 InstructGPT 论文中使用的一个重要技巧,被称为 PPO-ptx。 其目的是为了防止模型在 RLHF 的对齐过程中,遗忘其在预训练阶段获得的广泛世界知识和语言流畅性。

具体做法:

在 PPO 的最终损失函数中,混入一个额外的、标准的语言模型预训练损失(即 SFT 损失)。这个预训练损失是在一个通用的、大规模的文本语料库(如预训练数据集)上计算的,目标是最小化下一个 token 的负对数似然。

修改后的总损失函数:

image.png


什么是“价值函数裁剪”?为什么要在 PPO 中对 Critic 的更新也进行裁剪?

价值函数裁剪(Value Clipping) 是一种应用于 Critic 网络更新的正则化技术,其思想与 Actor 的 PPO 裁剪目标一脉相承,旨在防止价值估计在单步更新中发生剧烈的跳变。

image.png

image.png


分析 PPO 更新中可能出现的高方差问题,以及如何通过优势归一化(Advantage Normalization)来缓解。

高方差的来源:

在 RLHF 中,PPO 更新的高方差主要源于两个层面。

  1. 动作空间层面:词表巨大,重要性采样比率可能在部分 token 上出现剧烈波动。

  2. 任务层面:同一个 prompt,模型可以生成完全不同的回复,获得的奖励可能有天壤之别。即使在同一个批次的样本中,不同轨迹的优势 AtAt 数值范围也可能非常分散。如果不加处理,少数几个具有极大正优势或负优势的样本会主导整个批次的梯度更新,使得优化方向左右摇摆,收敛缓慢。

优势归一化的操作:

image.png

缓解机制:

  1. 稳定更新步长:归一化后,优势的分布变为零均值、单位方差。这意味着,我们不再关心“这个动作的绝对优势有多大”,而是关注“在这个批次中,它比平均好多少(或差多少)”。这确保了每次参数更新的量级是相对稳定的,不受优势值绝对尺度漂移的影响。

  2. 降低方差:通过缩放,优势的波动范围被限制在一个合理的区间内。这避免了模型因个别样本的巨大优势而剧烈调整参数,使得整体梯度信号的方差大大降低,训练过程变得更加平稳、高效。


为什么在计算重要性采样比率时,需要对每个 token 的概率进行乘积,然后裁剪,这相当于对轨迹的概率比进行裁剪?具体实现上有什么技巧?

乘积与轨迹概率比:

image.png

根据重要性采样原理,轨迹级别的概率比就是每个 token 级别概率比的乘积。因此,PPO 对每个 token 的比率 rt进行裁剪,实际上就等价于在逐个 token 地约束整条轨迹的概率比,防止新策略与旧策略在生成整个序列时的总体概率发生过于剧烈的变化。

具体实现上的技巧:

在代码中,我们从不直接计算和存储每个 token 的概率值(因为容易下溢),而是全程在对数空间中操作。

image.png

另一个重要技巧是聚合时的损失平均。在计算整个批次的 PPO 损失时,我们通常是对所有有效 token(排除 prompt 和 padding)的损失取平均,而不是求和。取平均可以使得损失的量级独立于批次大小和序列长度,让学习率等超参数的设置更加通用和稳定。


PPO 是否适用于多轮对话的 RLHF?每轮是否需要各自计算奖励和 KL?

完全适用,而且是必不可少的。 多轮对话是 RLHF 最重要的应用场景之一。

多轮对话的 MDP 定义:

image.png

每轮是否需要各自计算奖励和 KL?

从数学上说,应该在 token 级别上统一处理。 整个多轮对话就是一个长的 token 序列。奖励模型可以在任何我们设计的时间点插入评分。在实践中,通常有两种做法:

  1. 单轮奖励:在每个模型回复(一轮对话)结束后,RM 立即给出该回复的评分。这个评分会作为该回复最后一个 token 的奖励,并通过 GAE 传播到该回复的所有 token 上。每轮对话的 KL 惩罚也像单轮一样,施加在模型生成的每个 token 上。

  2. 全局奖励:仅在整段对话结束时,RM 给出一个关于整段对话质量的综合评分。这个评分只能直接指导最后一轮的最后 token,然后通过 GAE 的巨大魔力,逐渐向前传播到对话的最开头。

处理上的注意事项:

最关键的是,KL 惩罚必须永远只施加在模型生成的那些 token 上,绝不能施加在用户 prompt 的 token 上。用户的部分是环境,不是策略的决策。因此,在多轮对话中,我们需要精心维护一个掩码(mask),标记出哪些 token 是模型生成的(is_response),然后只在这些 token 上计算 PPO 的损失和 KL 惩罚。


能否对 PPO 进行简化,例如去掉 Critic,直接使用奖励信号作为优势?这在 RLHF 中可行吗?

image.png

因此,PPO 中的 Critic 不是可选项,而是应对高方差和信用分配问题的核心组件。它在 RLHF 中是成功的必需品。


如何理解 PPO 的“信任区域”思想?它与 TRPO 的关系是什么?

信任区域是一种优化哲学:在强化学习中,我们不应盲目地沿着梯度方向迈出一大步,因为我们的梯度估计(基于有限样本)往往存在偏差和方差。一个看似很好的梯度方向,可能只需要一小步就能到达一个悬崖边,而一大步会直接坠入性能崩溃的深渊。信任区域的思想是:我们只在一个我们能够“信任”其梯度估计准确性的、与当前策略足够接近的局部区域内进行策略更新。

PPO 与 TRPO 的关系:

  • TRPO 是信任区域思想的严格实现。它在每个更新步,显式地约束新旧策略之间的 KL 散度不能超过某个阈值。这构成了一个带约束的优化问题,TRPO 通过泰勒展开和对偶梯度法来近似求解,计算复杂且开销大。

  • PPO 是 TRPO 的简化、高效和近似实现。它放弃了求解复杂的约束优化问题,而是将约束“内化”到了目标函数中。通过裁剪重要性采样比率,PPO 以一种巧妙的方式实现了与 TRPO 类似的信任区域效果:当策略更新过大时,梯度信号会被自动截断,从而强制更新步长保持在安全范围内。PPO 以极低的计算代价,模拟了 TRPO 的核心思想——保守的策略迭代,因此在实践中大获成功。


PPO 训练中,如果 Actor 和 Ref 模型的概率差异过大,甚至 log_ratio 溢出,应如何处理?

这种“概率差异过大”通常是语言崩溃或奖励黑客的前兆,意味着策略已经严重偏离了正常语言分布。

应急处理措施:

  1. 立刻暂停或回滚:当前的训练检查点很可能已经损坏。应立即停止训练,回滚到上一次 KL 散度正常的检查点。

  2. 紧急增强 KL 惩罚 β:这是最直接的补救措施。将 KL 惩罚系数 β 大幅提高(例如乘以 10),强制让策略在下一次更新时被猛拉回参考模型附近,重新锚定在安全区域。

  3. 在计算中进行裁剪和过滤:在计算对数比率 logrt 时,可以使用 torch.clamp(log_ratio, min=-10, max=10) 这样的操作,防止因为溢出而出现 NaN。但这是治标不治本,只是为了防止程序崩溃。

根本性调整策略:

  1. 降低学习率:Actor 的学习率可能过大,导致单步更新就冲出了安全区。

  2. 减小 PPO 的裁剪率 ε:让裁剪机制变得更“紧”,更早地截断过大的更新。

  3. 采用更保守的优化器设置:例如增大 Adam 的 ϵ,或者使用 SGD 等更温和的优化器。

  4. 重新审视奖励模型:这通常是根本原因。需要检查 RM 是否对某些异常回答给出了不合理的超高奖励,导致 Actor 疯狂地想要模仿那些“畸形”回复。需要用对抗样本重新训练 RM 来修复这个漏洞。


在 PPO 的目标函数中,最小值操作 min(ratio * advantage, clipped_ratio * advantage) 是如何实现保守更新的?

这个最小值操作是 PPO 裁剪目标实现“保守更新”的数学精髓。

image.png

总结:最小值操作确保了无论是大的正向更新还是大的负向更新,只要超出了信任区域,梯度就会被切断,从而强制策略更新保持在一个安全、保守的范围内。


分析 PPO 在实际代码中的实现:token-level 的 PPO loss 是如何聚合的?是平均还是求和?

在实际代码(如 Hugging Face TRL 或 DeepSpeed-Chat)中,PPO 损失是在 token 级别计算的,然后通过平均进行聚合。

计算流程:

image.png

  1. 将这些 token 级的损失用一个掩码过滤,只保留那些“有效 token”(即模型生成的、非 padding 的 token)。

  2. 最终返回的批次损失是这些有效 token 损失的算术平均值。

为什么是平均而不是求和?

  • 超参数鲁棒性:求和会让总损失的大小依赖于序列长度和批次大小。这意味着,如果采用求和,我们在不同批次大小下就需要调整学习率,否则梯度规模会完全不同。取平均使得单个 token 的期望损失贡献与序列长度和批次大小解耦,极大地增加了超参数(特别是学习率)在不同实验设置下的可迁移性和稳定性。

  • 平衡长短序列:如果不取平均,长序列对总损失的贡献会远大于短序列,导致模型主要被长序列驱动,忽视在短回复上的优化。

具体代码实现示例(伪代码):

# log_ratio, advantages, mask 均为 (batch_size, seq_len) 的张量
ratio = torch.exp(log_ratio)
pg_losses1 = -advantages * ratio
pg_losses2 = -advantages * torch.clamp(ratio, 1.0 - epsilon, 1.0 + epsilon)
pg_losses = torch.maximum(pg_losses1, pg_losses2)  # 注意这里是最大化,因为我们对损失取负,等价于最小化负目标

# 聚合
ppo_loss = (pg_losses * mask).sum() / mask.sum()  # 有效token的平均损失