Elea Notes.

小模型的 PPO 不是不稳定,是接线错了:三个可复现的失败模式

「PPO 在小模型上不稳定」是个流传很广的说法,通常的结论是:70M–500M 这个量级要做对齐,就别用 PPO,改用 SFT 或 DPO。

先说结论

  • 「PPO 在小模型上不稳定」是个流传很广的说法,通常的结论是:70M–500M 这个量级要做对齐,就别用 PPO,改用 SFT 或 DPO。这篇论文把「不稳定」拆开,发现里面有三个可复现的工程缺陷,不是一个理论极限。
  • 三个缺陷:LoRA 的参数在 PPO 循环里被静默注册成不可训练(训练照跑,策略不动);bfloat16 算重要性比值时发生灾难性抵消,比值冲到 10610^6奖励模型的长尾误差把策略推进参考模型的低概率区,输出退化。
  • 逐个修掉之后,15 个(模型 × 语料)配置全部稳定收敛。
  • 真正可迁移的结论不是「PPO 能用」,而是一条判据:PPO 能不能带来提升,取决于监督微调后的困惑度,而不是参数量。PPL < 20 值得跑,20–50 之间提升有限甚至倒退,> 50 基本别指望。

这三个 bug 长什么样

设定先讲清楚。要用强化学习对齐一个语言模型,标准流程是三段:先用监督数据微调出一个基线(SFT),再训一个奖励模型给输出打分,最后用 PPO 让模型去最大化这个分数,同时用 KL 惩罚拴住它、不让它跑得离基线太远。

论文跑的是 Pythia-70M/160M/410M 和 SmolLM2-135M/360M,语料是 TinyStories(简单叙事)、CNN/DailyMail(新闻)、Wikitext-103(技术文本),15 个组合,超参数全程不变——这样才能把「某个配置的偶发问题」和「结构性失败」分开。

一、梯度被静默掐断

微调大模型时很少动全部参数,通常插一组小的低秩矩阵(LoRA),只训这一组。问题出在这组适配器进入 PPO 循环的时候:在部分 PEFT/TRL 实现里,适配器参数被注册成了 不可训练

后果很隐蔽:

采样 rollout  ✓ 正常
算 loss      ✓ 有数值,还会下降
反向传播      ✓ 不报错
策略分布      ✗ 一动不动

日志一切正常,曲线也不难看,但模型压根没在学。这是最坏的一类 bug——它不崩,它骗你。修法是 merge-and-reinitialize:把已有适配器权重合并进基座,再重新初始化一组干净的、确实带梯度的适配器。

二、bfloat16 把两个相近的数相减

PPO 的核心是重要性比值,衡量新策略相对旧策略把某个 token 的概率改了多少:

ρt=exp(logπθlogπSFT)\rho_t = \exp\left(\log \pi_\theta - \log \pi_{\text{SFT}}\right)

训练刚开始时 πθπSFT\pi_\theta \approx \pi_{\text{SFT}},所以括号里是两个很接近的数相减。这正是浮点运算最脆弱的地方:有效位在相减时被大量抵消(灾难性抵消)。bfloat16 只有 7 位尾数,抵消之后剩下的几位误差被 exp\exp 放大。

论文观察到:参数量低于 200M 的模型,头几步优化里比值就能超过 10610^6,直接触发硬件级异常。本该恒等于 1 附近的量,冲到了百万量级。

注意这里的因果不是「小模型脆弱」,而是小模型的 logits 分布更容易让这个减法落进精度陷阱。修法很朴素:PPO 循环里所有张量(策略、参考模型、value head、奖励模型)一律用 float32。省显存省错了地方。

三、奖励模型的长尾把策略推下悬崖

奖励模型自己也是学出来的,会犯错,而且错误分布是长尾的——偶尔给某个烂输出打一个极高的分。当优势估计远超裁剪范围 ϵ\epsilon 时,优化器就朝着参考模型认为极不可能的区域猛推一步,输出随即退化成不成句的东西。

三层防护,按「先防、再挡、最后回滚」排:

① 奖励白化 + 3σ 裁剪   → 把长尾压回来,限制优势估计的量级
② 重要性比值阈值 5      → 超过就整个 mini-batch 跳过
③ 权重回滚             → 每步前存一份;检测到 NaN/Inf 就还原

第三层是承认前两层可能漏。这个结构值得记住:不是找一个足够好的阈值,而是叠一条失败也能兜住的链。

消融实验把三层的必要性单独验了一遍(Pythia-70M / TinyStories,250 步):

配置结果
朴素 PEFT,直接把 SFT 适配器丢给 trainer奖励增量恰好为 0——梯度没流过去
手动解冻 LoRA,但不带参考模型第一个 mini-batch 就炸(数值不稳 + 没有参考分布)
三层机制全开250 步跑完,无灾难性失败

第一行那个「恰好为 0」是整篇论文里最有说服力的一个数字:它把「不稳定」这个模糊说法,钉死成了一个可复现的接线错误。

为什么判据是困惑度,不是参数量

论文提出的容量余量假设(capacity-headroom hypothesis)大意是:PPO 在这个尺度上能不能起作用,取决于两件事的组合——SFT 基线是否足够流畅,奖励信号是否足够有判别力——而不取决于参数量。

为什么是困惑度?因为奖励模型是在某个分布上训出来的。如果策略采样出来的东西太差、落在奖励模型没见过的地方,那么奖励模型给的分数就是噪声,PPO 优化的是一个假信号。困惑度在这里是「我采的样还在奖励模型的认知范围内吗」的代理指标。

落到可执行的判断,就是三档:

SFT 后 PPL < 20     → 值得跑 PPO
PPL ∈ [20, 50]      → 提升有限,甚至倒退
                      钱花在改 SFT 数据质量 / 提高 LoRA rank 更划算
PPL > 50            → 别指望 PPO

中间那档有个实测例子:Pythia-410M 在 Wikitext-103 上出现了倒退。注意它是这批模型里最大的一个——参数量最多的配置反而退步了,这正是「别拿参数量当判据」的直接证据。技术文本的困惑度本来就高,容量余量不够。

有个定性例子能看出 PPO 修的是什么。TinyStories 上同一个提示(Pythia-410M):SFT 的续写陷进了一个说不完的循环(小男孩反复说「我不会洗衣服」),PPO 的续写则保持在题目上往下走。对应的奖励从 4.28-4.28 升到 2.92-2.92PPO 在这里治的不是「不会说话」,是「说着说着卡住了」。

边界

几条得说清楚,否则容易把这篇的结论用过头:

  • 只验了单轮。 论文明确把经验结论限定在单轮 MDP:动作空间就是词表,一个回合,奖励只在终止状态给。多轮的部分只放出了一个前向兼容框架和 15 个 checkpoint,没有多轮的实验结论。对 agent 场景这是个大限制。
  • 偏好数据是合成的。 奖励模型的训练对由三种降级方式构造:截断、句子乱序、跨样本错配。这保证了可复现,但它不是人类偏好,覆盖的失败模式窄。
  • 安全机制救不了容量不足。 论文自己写明:三层防护能缓解、但不能真正解决底层的容量限制。别把它当万能补丁。
  • PPL 的阈值 20 / 50 是这批语料上的经验值,换 tokenizer、换领域都会漂。判据的形式(看基线流畅度)比这两个具体数字更值得迁移。

来源

  1. Towards Robust Reinforcement Learning for Small-Scale Language Model AgentsarXiv