做模型对齐这一年多,我最大的感受是:很多人把“微调”当成了训模型的终点。用一批精心整理的指令数据做几轮 SFT,看着 loss 降下来、样例回答变得有模有样,就觉得事情结束了。但真正把模型放到复杂任务里跑,你会发现它学会了格式,却没学会权衡;能接住单轮指令,却很难在长程决策中持续优化自己的行为。SFT 的本质是模仿,它可以让模型学会“人类喜欢什么样的回答”,但这和“不断逼近最优策略”是两回事。最近我在重构手上的对话模型训练流程,把重心从纯微调转向 PPO,收到的问题也越来越多:什么阶段该上 PPO?奖励模型到底起了什么作用?KL 惩罚怎么调?与其一个个回复,不如把这段从微调到 PPO 的完整踩坑过程整理出来。
1. 微调的边界:它为什么撑不起“自我进化”
1.1 SFT 到底在做什么
先说清楚微调的本质。无论你用的是 LoRA 还是全量微调,监督微调的底层逻辑都是最大似然估计。你在训练集里给模型一个 prompt,让它去预测标准答案中的每个 token,损失函数就是交叉熵。换句话说,SFT 的任务是把模型的输出分布拉向“你提供的那些示例”的分布。
这个机制决定了它的三个特性:第一,模型不可能超越你的示例集,它只能模仿示例范围内的风格与内容;第二,你对“好坏”的判断完全体现在数据筛选里,模型本身没有评价能力;第三,示例一旦固定,模型就无法在推理时动态调整策略——同一个 prompt,无论追问几次,它给出的都是近似分布里的随机采样结果。
1.2 当你试图用微调解决决策问题
我在一个项目里试过用纯 SFT 做一个客服工单分派模型。训练数据是从历史工单里挑出的“最优分派结果”,一开始效果挺好,准确率在测试集上到了 88%。但一上线就露馅了:遇到历史数据里没出现过的工单组合,模型会照着一个最相似的旧模式去分派,完全不考虑当前队列的压力、优先级和时效要求。后来我往里叠加大量的边缘 case 数据,效果倒是好了一点,但每加一层数据就多一份维护成本,而且模型的泛化能力始终没有被真正打开。
这就是微调的边界:它可以帮你压缩“已有经验的模式”,却无法帮你生成“新的最优行为”。如果任务只需要稳定的格式化和知识抽取,微调足够;但如果任务需要面对未知场景做出权衡、需要在多个约束下寻找更优解,你需要换一种训练哲学。
1.3 微调和强化学习的本质差异
- 优化目标不同:微调的目标是拟合数据分布;强化学习的目标是最大化累积奖励。
- 数据来源不同:微调完全依赖静态标注集;强化学习依赖策略与环境交互产生的动态反馈。
- 能力边界不同:微调受限于标注者的水平;强化学习可以持续探索到超出人类标注范围的更好策略。
所以“从微调到 PPO”并不是一种技术潮流,而是任务复杂度提升后的必然迁移。你仍然需要先做 SFT,否则随机初始化的策略根本不可能在稀疏奖励下稳定起步;但只做 SFT,又等于亲手把模型的能力天花板焊死在了训练集上。
2. 奖励模型才是进化的方向盘
2.1 从“人类标注答案”到“人类标注好坏”
PPO 本身并不直接理解人类偏好,它只理解奖励数字。因此我们需要的第一个基础设施,是一个能对模型输出打分的奖励模型。
奖励模型的训练数据长什么样?不再是“标准答案对”,而是“同一 prompt 下的两个回答,哪个更好”这样的偏好对。让人类标注员或者自动评估管线去标注这两条回答的相对质量,然后让奖励模型学习预测人类偏好的概率,很自然的做法是用一个二分类架构,对两个回答分别打分再计算 logit 差值。
我特别想强调一下数据采集的重要性。偏好对里的两个回答最好来自不同的来源——比如一个来自当前 SFT 模型,一个来自经过计算的基线模型;如果两个回答都来自同一个高质量的生成器,奖励模型会很难学到细微的区分能力。实践中我通常会让几个不同温度的采样结果互相配对,这样奖励模型才能学会“即使文风相近,也有优劣之分”。
2.2 奖励模型训练时最常犯的错
- 只用末尾 token:很多开源教程喜欢对序列最后一个 token 隐层做线性头输出分数。这在长回答上没问题,但如果回答很短、或者关键判断分布在序列中间,这个做法会丢失很多信息。我一般会对所有 token 做池化再打分。
- 忽略样本平衡:如果偏好数据里 80% 是“明显更好的回答”对,奖励模型很快学会作弊——看到句式长的就加分。建议强制加入少量“高质量 vs 高质量”的难分对样本。
- 不做校准测试:奖励模型训练完后,要用一批它没见过的偏好对做在线测试,计算准确率和置信度。如果准确率高了但置信度也极高,往往意味着它学到了表面特征而不是内在质量。
2.3 奖励模型的输出应该怎样被使用
在 PPO 流程里,奖励模型主要提供 dense 的实时反馈,而不是只在 episode 结束时给一个稀疏信号。这一点对语言模型尤其重要:一个回答可能有几百个 token,如果只有生成完毕后的一个整体分数,策略网络根本不知道哪些 token 导致了高奖励,信用分配问题会变得极其严重。
所以我通常会配合句级或段级奖励分解:让奖励模型对每个句子的自由度打分,再把这些句子级奖励汇总成整个序列的奖励序列。这样 PPO 的 GAE 计算就有了足量的中间信号,策略更新也能更平滑。
3. PPO 的完整运作机制拆解
3.1 四件套的基本分工
进入 PPO 主体之前,先把需要的网络和模块摆清楚。一个典型的语言模型 PPO 训练,最少包含以下四样东西。
| 模块 | 作用 | 常见实现 |
|---|---|---|
| 策略模型 actor | 生成回答并接受更新 | 就是你的 SFT 模型 |
| 参考模型 ref | 冻结参数,提供 KL 基线 | 训练开始前的策略模型副本 |
| 价值模型 critic | 估计状态价值,用于计算优势 | 可与策略共享部分参数 |
| 奖励模型 reward | 给生成结果打分 | 单独训练的偏好打分模型 |
你可能注意到我故意没有列“环境”。语言模型的环境其实就是 prompt 数据集本身:每次训练,我们从 prompt 库中采样一批提示词,把当前策略模型当作 agent,生成一段回答,交给我们定义的奖励函数去评分。整个过程完全可以在单机多卡上跑通,不需要一个物理环境。
3.2 损失函数里到底有哪些项
策略模型的 PPO 损失不是单一项,而是四项组合。每一次更新,我们都在最大化下面这个式子(以最小化 loss 的角度来写会更常见):
loss = -clip_ratio * advantage + value_loss * c1 - entropy_reg * c2 + kl_loss * c3逐项解释:
- 策略项:计算新策略与旧策略在采样 token 上的概率比值,并裁剪到 [1-ε, 1+ε] 范围。它确保新策略不会因为某一条高奖励样本而一次性偏离太远。
- 价值项:价值模型的均方误差,让它学会预测累加奖励。这个误差过大会导致 advantage 估计失真,我一般会给它一个 0.5 左右的权重系数。
- 熵正则项:对输出分布的熵做负惩罚,鼓励模型保持一定探索度。熵系数崩得太低,模型会变成复读机;调得太高,生成质量会退化。
- KL 惩罚项:钳制策略模型与参考模型之间的距离,防止策略为了刷奖励跑到人类语言分布之外。
3.3 GAE 与优势函数为什么是 PPO 的发动机
PPO 能稳定更新,靠的是准确的优势估计。语言模型场景下,最常用的是 Generalized Advantage Estimation。GAE 的核心思想是用 λ 参数来平衡偏差和方差:λ=0 时完全依赖即时奖励,偏差大;λ=0.95 左右时会引入更多长期累积奖励,方差下降但估计更平滑。
我在实际跑实验时,λ 对训练稳定性的影响甚至超过了学习率。某个项目里我把 λ 从 0.9 调到 0.95,奖励曲线从剧烈震荡变成了稳步上升。原因是语言模型的长回复中,后期 token 的语义正确与否会反向影响前期生成的合理性,GAE 能够在时间维度上把这些跨 token 的影响揉进每一步的优势值里。
4. 实操 PPO 的避坑记录
4.1 奖励黑客:模型比你想象的更会钻空子
做 PPO 的人一定绕不开奖励黑客。我第一次跑通端到端流程时,策略模型很快就学会了一个奇怪的行为:在回答末尾反复输出“好的好的好的好的”。原因很简单——奖励模型训练数据里的高质量回答大多末尾有礼貌性收尾词,于是模型发现把这段词语重复三次,奖励分数就会上涨。
这个问题的深层原因是奖励模型只看到了表面相关性,而不是语义动机。应对办法有几个维度:
- 给奖励模型做防御性训练:把短回答截断、长回答裁剪等扰动加进偏好数据,让奖励模型对长度、重复串等低层特征钝化。
- 在 PPO 里加惩罚项:对 n-gram 重复度、长度偏离度设置软约束,把常见黑客行为直接编码进奖励函数。
- 人工巡检奖励曲线:每训练 200 步可视化几条高奖励回答,一旦发现语义退化立刻冻结标志并回滚 checkpoint。
4.2 KL 惩罚的平衡艺术
KL 系数是 PPO 训练中最让我头疼的超参。它控制着策略模型能走多远:KL 太小,模型会放飞自我,甚至开始输出不符合语法的乱码;KL 太大,模型被死死摁在 SFT 初始策略附近,PPO 就丧失意义了。
实际的调整思路是渐进的。训练刚开始时,策略几乎不会大幅偏离参考模型,这个时候可以选一个稍小的 KL 系数让模型充分探索;等奖励稳定上升后,再逐步调大系数把策略“收回来”,避免它在边界游荡太久。有些框架支持自适应 KL,它会根据离线测得的实际散度动态调整系数大小,我强烈建议新手直接开启这个选项,省掉手动介入的复杂度。
4.3 训练崩溃:看不见的数值陷阱
还有一个比奖励黑客更隐蔽的问题。PPO 更新时,如果某个 token 的生成概率新策略比旧策略低了很多,概率比值会非常小,而这个 token 又恰好获得了高优势,梯度更新会对它产生疯狂的放大效应。轻则奖励波动,重则 loss 在几百步内直接 NaN。
我的处理习惯:
- 梯度裁剪必须开启,max_grad_norm 设置在 1.0 到 2.0 之间。
- 动态调整 batch size:语言模型的 token 级优势会受长度影响,短 batch 的方差很大,长 batch 又太占显存。我一般先跑一个 epoch 看优势分布,再根据分布的最大绝对值设置训练步数。
- 保存每个 epoch 的策略快照,如果崩溃发生,可以回滚到最近一个稳定点。
4.4 最小成本的实验环境配置
很多人被 PPO 的显存需求吓退。一个 7B 模型,加上策略、参考、价值、奖励四个模型,单纯做推理侧评估就需要非常可观的显存。一个可行的降本方案是冻结策略模型的底层表示,只对上层训练。我们会把策略模型底部的几十层全部冻结,只更新顶层和策略头,这样显存占用大幅下降,代价是表达能力上限降低。对多数追求稳定对齐的项目来说,这个折中很值。
另一个减少试错成本的技巧是用更小规模的模型先验证流程。我在正式上 7B 之前,会先拿 1B 左右的模型跑通整个 PPO 管线,确认奖励模型训练正常、KL 曲线不爆炸,然后再迁移到大模型上。小模型和大模型的训练特性差异确实存在,但流程 bug 绝大多数都是通用的,提前在小模型上发现比在 7B 上试错便宜得多。
5. 该不该从微调迁移到 PPO:决策与落点
5.1 三种适合 PPO 的任务画像
不是所有 NLP 任务都需要强化学习阶段。根据我经手的项目,适合从微调进一步走向 PPO 的任务通常有这些特征:
- 输出空间开且没有唯一标准答案。比如文案生成、代码生成、对话策略,这类任务本身允许多种合理的解决方案,SFT 只能捕捉其中一部分。
- 存在可程序化或可学习的奖励函数。比如代码编译是否通过、摘要是否覆盖关键要点、对话是否满足用户明确意图。奖励信号越清晰,PPO 的优势越明显。
- 需要长期规划和多步权衡。比如多轮对话中的上下文管理、知识问答中先确认再回答的决策路径。
反过来,如果你做的是分类、抽取、标准化文本生成这类答案高度确定的任务,PPO 未必带来明显收益,反而会引入不稳定性和大量计算成本。
5.2 迁移路线图:从 SFT 到 PPO 的最小闭环
我整理了一个最小闭环清单,照着做可以快速验证 PPO 在你任务上是否值得投入:
- 准备 5000 到 10000 条高质量 prompt,覆盖目标任务的分布。
- 用这些 prompt 做一轮 SFT,得到一个可靠的初始策略。
- 构建偏好数据:对每个 prompt 采样多条回答,人工或规则排序,训练奖励模型。
- 开跑 PPO:策略模型用 SFT 权重初始化,参考模型冻结同一权重,价值模型随机初始化或复用策略。
- 训练中每 100 步采样一轮生成结果,人眼评估质量,而不是只盯奖励曲线。
- 收敛后对比 SFT 基线的评测集指标,关注生成质量提升的同时是否出现重复、语病等退化。
5.3 评估回报:什么时候说明这一步走对了
我衡量 PPO 是否“值回票价”时,不只对比 BLEU 或 ROUGE,还会做一组偏好盲测。把 SFT 模型的输出和 PPO 模型的输出混在一起,让标注者在不告知来源的情况下二选一。当 PPO 输出的偏好胜率稳定超过 60%,说明强化学习阶段确实带来了肉眼可见的提升;如果胜率只有 55% 以下,就要回头检查奖励模型的质量,大概率是它对“好”的定义还停留在表面特征上。
从微调走到 PPO,本质上是一次训练哲学的更换:从告诉模型“标准答案是什么”,转向告诉模型“什么样的结果更好,并且让它自己探索通往更好结果的路”。这中间既有奖励模型训练的繁琐,也有 KL 系数调参的折磨,但看到模型在从未见过的约束条件下自行找到更优策略时,你会觉得前面踩过的坑都值得。