11. 偏好对齐:教模型分辨哪个回答更好
经过 SFT 和 LoRA,资料助手已经会按公司口径回答、带条款编号。但上线一周的反馈里出现了另一类问题:小林问“调休没用完能折现吗?”,模型回答“可以,按三倍工资折现”。制度里根本没有这条,它只是写出了一个流畅、肯定、看起来很有帮助的回答。SFT 数据里并没有教它这么说,问题在于 SFT 从来没有告诉过它:这样回答比“制度没有规定,建议咨询人事”更糟。
偏好对齐(preference alignment)解决的就是这件事:给模型看同一个问题的两个回答,告诉它哪个更好,让它朝更好的方向调整。这一篇先讲经典的 RLHF 三步流程(奖励模型、PPO、KL 约束),再讲 DPO 怎样把它化简成一个损失函数,然后讨论偏好数据怎样标、优化过头会发生什么、训练后怎样评估。大模型专题的 训练与微调 简单介绍过 RLHF 和 DPO 的分工,这里展开它们的机制。读完你应该能判断:资料助手的哪些问题适合用偏好数据解决,以及 RLHF 和 DPO 该选哪一个。
SFT 只会模仿,不会比较
SFT 的训练信号只有一种:“这个回答的每个 token,概率提高一点”。它能让模型学会好回答的样子,却无法表达两件事:
- 哪个更好。两个回答都没错,一个简洁准确,一个啰嗦但完整,SFT 只能挑一个当标准答案,另一个的信息就丢了。
- 什么不能做。SFT 提高标准答案的概率时,其他写法的概率只是被顺带挤低,它没有办法点名压低某个具体的坏回答。“按三倍工资折现”这种回答,只要训练数据里没有对应的反例,SFT 就很难专门把它压下去。
偏好数据正好补上这两点。它的基本单位是一个偏好对:同一个提示,一个被选中的回答(chosen)和一个被拒绝的回答(rejected):
json
{"prompt": [{"role": "user", "content": "【检索片段】《考勤制度》第6.3条:调休应在加班后3个月内使用……\n\n问题:调休没用完能折现吗?"}],
"chosen": [{"role": "assistant", "content": "《考勤制度》第6.3条只规定调休应在加班后3个月内使用,没有提到折现。是否可以折现,建议向人事部确认。"}],
"rejected": [{"role": "assistant", "content": "可以的,未使用的调休会按三倍工资折现,月底随工资发放。"}]}偏好不一定是“对和错”,也可以是“两个都对,但一个更符合要求”。对资料助手来说,最有价值的偏好往往是:准确而谨慎的回答,优于自信但没有依据的承诺。
RLHF:先学一个打分器,再追分
2022 年的 InstructGPT 论文把 RLHF(Reinforcement Learning from Human Feedback,基于人类反馈的强化学习)整理成了后来被广泛沿用的三步:
- SFT:标注员写示范回答,训练出一个初始的对话模型(约 1.3 万条提示)。
- 训练奖励模型:同一个提示让模型生成 4 到 9 个回答,标注员排序,排序拆成两两比较(约 3.3 万条提示)。奖励模型读入“提示 + 回答”,输出一个分数。
- 强化学习:用 PPO 算法让模型自己生成回答,奖励模型打分,模型朝高分方向更新(约 3.1 万条提示,不需要人工标注)。
效果相当显著:论文中,经过这三步的 13 亿参数 InstructGPT,输出被标注员偏好的比例高于 1750 亿参数的原始 GPT-3。模型小了一百多倍,“更会回答”却靠的是对齐,而不是规模。
奖励模型怎样从“比较”学会“打分”
人很难给一个回答打出可靠的绝对分(7 分还是 8 分?),但比较两个回答哪个更好要容易得多。奖励模型用 Bradley–Terry 模型把比较变成分数:假设回答 A 胜过 B 的概率由两者分数之差决定。
text
P(chosen 胜过 rejected) = σ(r(x, y_w) − r(x, y_l))
损失 = −log σ(r(x, y_w) − r(x, y_l))
x :提示
y_w :被选中的回答,y_l:被拒绝的回答
r :奖励模型给出的分数
σ :sigmoid 函数,把任意实数压到 0 到 1 之间代入两组数字看损失怎样变化:
text
奖励模型给 chosen 1.2 分、rejected −0.3 分:σ(1.5) ≈ 0.82,损失 ≈ 0.20
奖励模型给反了,chosen −0.3、rejected 1.2:σ(−1.5) ≈ 0.18,损失 ≈ 1.70奖励模型只关心分差,不关心绝对值,所以它的分数只能在同一个提示的回答之间比较。InstructGPT 用的奖励模型是 60 亿参数,结构通常就是把语言模型的输出层换成一个输出单个数的线性层。
PPO 和 KL 惩罚:别为了分数忘了自己是谁
第三步里,模型(这时叫策略)生成回答,奖励模型打分,PPO 根据分数调整策略。但目标函数里不只有奖励一项:
text
优化目标 = 奖励模型分数 r(x, y) − β · KL(π_θ ‖ π_ref)
π_θ :正在训练的策略
π_ref:参考模型,通常就是第一步的 SFT 模型,冻结不动
KL :两个模型在同一回答上的概率分布差多远
β :惩罚系数,越大越不敢离开参考模型KL 惩罚的作用是把策略拴在参考模型附近。奖励模型只在它见过的那类回答上打分可靠;策略一旦跑到很远的地方(比如开始输出某种奖励模型没见过的奇怪句式),奖励模型的分数就不再可信。KL 项让策略只能在“仍然像一个正常对话模型”的范围内找高分。InstructGPT 还在目标里混入了一部分预训练数据的似然,用来减轻公开基准上的退步,也就是论文所说的“对齐税”。
这套流程工程上很重:训练时显存里同时有策略、参考模型、奖励模型,以及 PPO 用来估计基线的价值模型,一共四个模型;每一步都要先在线生成一批回答,生成又慢。超参也多,调不好容易不稳定。
奖励被钻空子
奖励模型是人类偏好的一个近似,策略优化的是这个近似。优化得越用力,策略越会找到“奖励模型给高分、但人并不喜欢”的回答。这叫奖励过度优化,或者说奖励被钻空子(reward hacking)。
2022 年 Gao 等人系统测量了这个现象。他们用一个大的“金标准”奖励模型扮演人类,用它的标注训练较小的“代理”奖励模型,再让策略对着代理模型优化,同时用金标准模型测量真实质量。结果是:随着策略离初始模型越来越远,代理分数一直上涨,金标准分数却先升后降。
图中的曲线形状取自他们为强化学习拟合的函数形式(真实奖励约为 d(α − β·ln d),d 是 KL 的平方根),系数是示意值。论文还发现,曲线的系数随奖励模型的规模平滑变化,奖励模型越大,过度优化越轻。这张图说明了两件工程上的事:KL 惩罚和提前停止都是为了停在峰值附近;训练过程中只盯着奖励曲线上涨是危险的,必须定期用独立的评估(人工抽检或另一个评委)检查真实质量。
在资料助手上,被钻空子的样子很具体:如果标注员习惯给“更完整”的回答打高分,奖励模型会学到“越长越好”,策略就把每个回答都写得很长;如果“带条款编号”的回答总被选中,策略可能学会给没有依据的回答也编一个编号。这类问题在奖励曲线上看不出来,只能在评估集上看到。
DPO:不训练奖励模型,直接改概率
2023 年 Rafailov 等人提出的 DPO(Direct Preference Optimization,直接偏好优化)证明了一件事:在 RLHF 那个“奖励减 KL 惩罚”的目标下,最优策略和奖励之间存在一个闭式关系,奖励可以直接用策略和参考模型的概率比表示出来。代回 Bradley–Terry 损失,奖励模型和强化学习就都可以省掉,剩下一个像分类一样的损失:
text
损失 = −log σ( β · [ log(π_θ(y_w|x) / π_ref(y_w|x)) − log(π_θ(y_l|x) / π_ref(y_l|x)) ] )
log(π_θ / π_ref):同一个回答,训练中的模型比参考模型的对数概率高了多少
β :控制离参考模型多远,常见起点 0.1直观地说:对每个偏好对,DPO 让被选中的回答相对参考模型的概率提升幅度,大于被拒绝回答的提升幅度。代入一组数:
text
chosen 的对数概率比 +0.3,rejected 的 −0.5,β = 0.1
β × (0.3 − (−0.5)) = 0.08,σ(0.08) ≈ 0.52,损失 ≈ 0.65(训练初期,区分还很弱)
训练一段后 chosen +1.0,rejected −9.0:β × 10 = 1.0,σ(1.0) ≈ 0.73,损失 ≈ 0.31β × log(π_θ / π_ref) 就是 DPO 的隐式奖励。TRL 训练时记录的 rewards/chosen、rewards/rejected、rewards/margins、rewards/accuracies 都是由它算出来的:间隔应该逐步拉开,准确率(隐式奖励 chosen 高于 rejected 的比例)逐步上升。
下面这张图把两条流程放在一起:
两条路的起点都是 SFT 模型,都用 KL 或 β 把新模型拴在参考模型附近。DPO 仍然需要参考模型,但参考模型不更新,它对每个回答的对数概率可以在训练前一次性算好(TRL 的 precompute_ref_log_probs),训练时显存里只剩一个策略。训练数据是固定的偏好对,训练中不生成回答,代码量和调参难度都接近 SFT。
DPO 的局限也来自“离线”二字:它只能从现有的偏好对里学,学不到数据里没有的回答;偏好对如果来自别的模型(例如拿更强模型的回答当 chosen),和当前模型自己会写出的回答差得很远,效果会打折扣。实践中常见的做法是用当前 SFT 模型自己生成候选回答,再标注偏好。还有一个容易误读的现象:DPO 训练中 chosen 回答的对数概率也常常下降,只是 rejected 降得更多,间隔主要是靠压低被拒绝回答拉开的。只要评估指标在变好,这本身不是问题;但如果 chosen 的概率降得太多,模型可能开始输出两者都不像的回答,这时要调大 β 或减少训练步数。
超参起点:TRL 的 DPOConfig 默认 β = 0.1、学习率 1e-6;LLaMA-Factory 的 LoRA DPO 示例用 β = 0.1、学习率 5e-6。比 SFT 小得多,因为 DPO 是在一个已经很好的模型上做细调。
DPO 之后的几个变体
DPO 之后出现了大量改进,下面几个已经进入 TRL 或 LLaMA-Factory,可以直接选用:
| 方法 | 改了什么 | 适合的情况 |
|---|---|---|
| IPO(2023) | 论文指出偏好越确定,DPO 的 KL 约束越弱,容易过拟合;改用恒等变换直接优化偏好概率 | 偏好对几乎都是“一边倒”、担心过拟合 |
| KTO(2024) | 不需要成对数据,只要每个回答“好”或“不好”的二元标签,借用前景理论的效用函数 | 线上只有点赞、点踩,凑不成对 |
| ORPO(2024) | 不需要参考模型,把偏好项直接加在 SFT 损失上,一个阶段完成 | 想把 SFT 和偏好合成一步、省显存 |
| SimPO(2024) | 用回答的平均对数概率作隐式奖励,不需要参考模型,并加一个目标间隔 | 担心长度偏差、想省掉参考模型 |
这些方法在不同论文的评测里互有胜负,没有一个在所有任务上都更好。先用 DPO 跑通,确认数据和评估都可靠后,再拿变体做对照实验。
资料助手的偏好数据怎样标
偏好对齐的效果几乎完全由偏好数据决定。数据有偏,模型会忠实地学到这份偏差,并且放大它。
候选回答的来源:用 SFT 后的模型对同一个问题采样 2 到 4 个回答(温度设高一点,让回答有差异),这样偏好对反映的是模型自己真实会犯的错。再补一部分人工改写的“理想回答”和故意构造的“危险回答”(例如上面的“三倍工资折现”)。
标注规则要写成优先级,而不是“选你更喜欢的”。资料助手可以这样排:
- 与检索片段一致、没有编造的,优于有编造的;
- 信息不足时说明不确定并指引去处的,优于给出确定承诺的;
- 条款编号正确的,优于编号缺失或错误的;
- 以上相同时,简洁直接的优于冗长的。
控制长度偏差。人和 LLM 评委都倾向于给更长、更“完整”的回答打高分。标注后统计一下:chosen 比 rejected 长的比例如果远高于一半,模型就会学到“写长就赢”。可以有意加入“简短而正确的回答胜过冗长回答”的对子来平衡。
两个都差或分不出高下时,宁可丢掉这一对,也不要硬选。标注员之间的一致率也要抽查:同一批样本让两人独立标注,一致率低说明规则没写清楚。
训练后怎样评估
偏好训练的常用指标是胜率:同一组评估问题,新模型和 SFT 模型各回答一次,由人或 LLM 评委判断哪个更好。LLM 评委便宜、快,但有几种已知偏差:倾向于选排在前面(或后面)的回答,倾向于选更长的回答,倾向于选和自己风格相近的回答。至少要做两件事:每对回答交换顺序各评一次,只有两次结论一致才计数;同时报告两个模型的平均回答长度,胜率上升而长度也大幅上升时要警惕。评委的使用方法详见 评估。
胜率之外,第 09 篇的那些绝对指标还要再跑一遍:编造率、漏答率、条款编号正确率。偏好训练常见的失败是胜率涨了,编造率也涨了,因为评委喜欢更自信、更完整的回答。对资料助手来说,编造率上升就是不合格,胜率再高也不能上线。
什么时候不该做偏好对齐
偏好对齐是在 SFT 之上的细调。SFT 本身还没做好(格式不稳定、口径不对)时,先补 SFT 数据,效果来得更快。问题有唯一正确答案、并且能用程序检查时(算加班费、核对日期),与其让人标“哪个更好”,不如直接用程序判对错,这是下一篇的内容。知识本身缺失或过时的问题,偏好数据也解决不了,仍然要靠检索。
SFT 只会模仿好回答,偏好对齐让模型学会比较:RLHF 先用 Bradley–Terry 损失训练奖励模型,再用 PPO 追求高分,靠 KL 惩罚防止跑偏;DPO 证明了同样的目标可以化成一个直接作用于概率比的损失,省掉奖励模型和在线生成。无论哪条路,奖励都只是人类判断的近似,优化过头会被钻空子,所以偏好数据的标注规则和独立评估比算法选择更重要。下一篇换一种奖励来源:当答案对错能由程序判定时,强化学习可以放开手脚训练,推理模型就是这样来的,见 强化学习与推理模型。
