时长 7 小时:§1 SFT 的天花板(1h)· §2 奖励模型(2.5h)· §3 RLHF 的全貌与过度优化(1.5h)· §A–§C(2h)
对应正课 Lecture 5 的前半部分:偏好微调、RLHF 概览、奖励建模。学习计划点名要会的:为什么 SFT 不够;RLHF 三阶段每一步的输入输出;Bradley-Terry 损失为什么是这个形式;KL 惩罚去掉会怎样。
配套代码
w3day2_checks.py(纯 numpy):Bradley-Terry 从成对比较恢复分数、标注噪声给准确率设的天花板、奖励模型学到标注者的偏见、best-of-n 的过度优化、KL 约束最优策略随 β 的变化§B 参考实现(
code/目录):w3d2_reward_model.py(在 mini-GPT 上训一个玩具奖励模型,供 Day 3 的 PPO 使用;在 Qwen2.5-0.5B-Instruct 上用 UltraFeedback 训练真实的奖励模型,并检查长度偏见)。今天的目标:能说清 SFT 缺了什么;能从”潜在效用 + 噪声”推出 Bradley-Terry 模型和它的损失;能解释奖励模型 70% 的准确率意味着什么;能画出 RLHF 的完整数据流;能说清过度优化是怎么发生的,以及 KL 惩罚为什么能缓解它。
§0 这一周在做什么
第 2 周结束时,你用 SFT 把一个续写器变成了助手(Week 2 Day 6 §1)。这一周回答两个问题:
- 怎样让助手的回答”更好”,而不只是”像训练数据”? 这是 Lecture 5:偏好微调。今天讲奖励模型和 RLHF 的全貌,Day 3 讲强化学习(PPO),Day 4 讲绕过强化学习的 DPO。
- 怎样让模型”更会想”? 这是 Lecture 6:推理模型。Day 5 讲可验证奖励的强化学习(RLVR)与 GRPO,Day 6 讲测试时缩放。
两讲共用一套数学:在 KL 约束下最大化期望奖励。今天 §3.3 引入它,Day 3 用 RL 求近似解,Day 4 推出它的闭式解,Day 5 换成可自动验证的奖励。
今天要用到的旧结论:交叉熵与最大似然(Week 0 Day 1);逻辑回归与 sigmoid 的梯度(Week 0 Day 1 §10.1 的 log-sum-exp);SFT、chat template 与 loss 掩码(Week 2 Day 6 §1);温度与采样(Week 2 Day 3 §2);自洽性投票(Week 2 Day 3 §5.3)。
§1 为什么 SFT 不够
1.1 SFT 只能模仿
SFT 的损失是 :让模型模仿示范回答 。它有三个根本局限:
- 没有负信号。SFT 只告诉模型”这样写”,从不告诉它”不要这样写”。模型有害的、编造的、啰嗦的输出,在 SFT 里没有任何惩罚,除非恰好和示范冲突。
- 表达不了”A 比 B 好”。很多时候没有唯一正确的回答,只有更好和更差。两个都说得过去的回答,SFT 没法表达偏好哪一个。
- 示范的上限就是示范者。模仿学习最多学到和示范一样好。而且模型训练时只见过”完美的前文”,推理时却要在自己生成的前文上继续,一步错就可能步步错(和 Week 0 Day 4 §4.3 的 exposure bias 是同一个问题)。
1.2 比较比写容易
关键观察:让人写一个好回答很难、很贵;让人比较两个回答哪个更好,容易得多,也更一致。这是”生成-验证差距”的一个实例:很多问题上判断比创造容易。
于是换一种数据:给定提示 ,模型生成几个回答,标注者给出排序或两两比较。这种数据里既有”好”也有”不好”,还有它们之间的相对关系。
1.3 从”像人写的”到”人喜欢的”
目标变了:SFT 优化”像示范”,偏好微调优化”人更喜欢”。怎么把一堆两两比较变成可以优化的目标?有两条路:
- 先学一个奖励模型,把偏好压缩成一个打分函数 ,再用强化学习最大化它。这是 RLHF(今天 §2–§3,Day 3)。
- 直接用偏好数据训练策略,跳过奖励模型和强化学习。这是 DPO(Day 4)。它之所以可能,是因为第 1 条路的最优解有闭式形式。
InstructGPT(Ouyang et al. 2022)是第 1 条路的标杆:经过 RLHF 的 1.3B 模型,其输出比 175B 的 GPT-3 更受人类标注者偏好。对齐带来的提升超过了一百多倍的参数量。
§2 奖励模型
2.1 偏好数据
一条偏好数据是 :提示 ,被选中的回答 (winner),被拒绝的回答 (loser)。
InstructGPT 的做法:对每个提示采样 到 个回答,标注者给出完整排序,于是一个提示产生 个比较对( 时 6 对, 时 36 对)。一个细节:同一提示的所有比较对放进同一个 batch 元素里一起算,而不是打散成独立样本。否则每个回答在一个 epoch 里被看 次,奖励模型很快过拟合。
现代的公开数据集:Anthropic HH-RLHF(有用 / 无害两类)、UltraFeedback(由 GPT-4 按细则打分再二值化成偏好对,参考代码 --hf 用它)、HelpSteer 等。
2.2 Bradley-Terry:从”偏好”到”分数”
动机:我们想要一个标量打分函数 ,但数据只有两两比较。需要一个模型把两者联系起来。
直觉:设每个回答有一个潜在的”效用”,标注者比较时感受到的是效用加上一点随机噪声,选感受更高的那个。
推导:设标注者感受到的效用是 ,噪声 独立同分布于标准 Gumbel 分布。两个独立 Gumbel 之差服从 logistic 分布,于是
这就是 Bradley-Terry 模型(1952)。换成正态噪声会得到 probit 模型( 代替 ),形状几乎一样;选 logistic 是因为梯度简洁。
损失:对偏好数据做最大似然,
它就是一个逻辑回归:输入是”两个回答的分数差”,标签是”第一个更好”。梯度
奖励模型排错得越厉害(),梯度越大;已经排对了的对,梯度趋于 0。 这个”按错误程度加权”的形状,在 Day 4 的 DPO 里会再出现一次。
w3day2_checks.py 第 1 组:20 个回答,按 BT 模型随机生成比较,再用最大似然恢复分数:
| 比较次数 | 恢复的分数与真值的相关系数 | 均方根误差 |
|---|---|---|
| 200 | 0.8763 | 0.831 |
| 2000 | 0.9485 | 0.185 |
| 20000 | 0.9961 | 0.051 |
只能确定到一个常数:所有分数同时加 3.7,所有 的变化是 。损失只看同一个提示下两个回答的分数差,所以不同提示之间奖励的绝对值没有意义。RL 之前通常把奖励归一化(减均值、除以标准差),或者减去一个参考回答的奖励。
2.3 结构:语言模型 + 标量头
奖励模型通常就是一个语言模型(常从 SFT 模型初始化),把输出词表的 lm_head 换成一个输出标量的线性层,取最后一个 token 的隐状态打分:
为什么是最后一个 token:因果注意力下只有最后一个位置看到了完整的提示和回答(Week 1 Day 1 §3.1)。
为什么从 SFT 模型初始化:奖励模型要”读懂”回答才能打分,这需要语言理解能力,而且它要评价的正是 SFT 模型会产出的那类文本。InstructGPT 用了一个 6B 的奖励模型去训练 175B 的策略,并报告更大的奖励模型训练不稳定。
2.4 准确率的天花板
奖励模型在留出的偏好数据上的准确率通常只有 65%–75%。这是不是说明它很差?
不一定。标注本身就不一致。 InstructGPT 报告的标注者之间一致率约 73%。第 2 组:设真实奖励差 、标注者以 的概率选对,这时两名标注者的一致率正好是 72.6%,而任何模型预测标注的准确率上限(贝叶斯最优)是 :
| 训练对数 | 留出集准确率 | 上限 | 与真实奖励的排序一致 |
|---|---|---|---|
| 100 | 0.786 | 0.801 | 0.922 |
| 1000 | 0.794 | 0.801 | 0.951 |
| 10000 | 0.799 | 0.801 | 0.986 |
意义:准确率碰到上限之后就不再上升,但奖励模型和”真实偏好”的吻合程度(最后一列)还在变好。而这一列在实践中是测不到的,我们只能看到前一列。所以奖励模型的好坏,最终要靠”用它训练出来的策略好不好”来检验。RewardBench 这类基准用更干净、有明确答案的比较对来测,结果比在噪声数据上测的准确率更有参考价值。
2.5 奖励模型会学到标注者的偏见
奖励模型拟合的是**“标注者会选哪个”,不是”哪个真的更好”**。第 3 组:让标注者除了看质量,还有一点偏爱更长的回答(偏好系数 0.5):
| 标注者的长度偏好 | 奖励模型学到的权重(质量 / 长度) | 比值 |
|---|---|---|
| 0 | 1.49 / −0.01 | −0.01 |
| 0.5 | 1.50 / 0.76 | 0.50 |
它把标注者的偏好比例原样学了过去。 真实的标注者偏爱更长、更自信、格式更花哨、更认同自己观点的回答(sycophancy,谄媚),奖励模型就会奖励这些特征。如果只收集长度相同的回答对,长度这一项就没有信号,学到的权重是 0.00。控制偏见要从数据入手:长度匹配、清晰的标注指南、多名标注者。
其他常见做法(知道即可):
- Llama 2 训练了两个奖励模型(有用性、安全性),并在损失里加了一个与”偏好强度”相关的间隔项:,标注者说”明显更好”时 更大。
- K 个回答的完整排序可以直接用 Plackett-Luce 模型(BT 的多元推广)建模,而不是拆成两两比较。
§3 RLHF 的全貌
3.1 三个阶段的数据流
学习计划的自测题:“画出 RLHF 的完整数据流,标出每一步训练哪个模型、用什么数据。”
阶段 1 SFT
数据:示范 (x, y*) ← 标注者写
训练:预训练模型 → π_SFT 损失 −log π(y*|x),只在回答上算(Week 2 Day 6 §1.3)
阶段 2 奖励模型
数据:x → π_SFT 采样 K 个回答 → 标注者排序 → 偏好对 (x, y_w, y_l)
训练:π_SFT 的主干 + 标量头 → r_φ 损失 −log σ(r_w − r_l)(§2.2)
阶段 3 强化学习(PPO,Day 3)
数据:只有提示 x(不需要任何回答和标注)
循环:π_θ 采样 y → r_φ(x, y) 打分 → 减去 β·KL(π_θ ‖ π_ref) → 用 PPO 更新 π_θ
同时在显存里:π_θ(训练)、π_ref = π_SFT(冻结)、r_φ(冻结)、价值模型 V(训练)
几个要点:
- 阶段 3 的数据是策略自己生成的(on-policy)。这是它和 SFT、奖励模型训练最大的区别,也是它慢、贵、难调的原因。
- 参考模型 通常就是 ,冻结不动,用来算 KL。
- 四个模型同时在显存里,这是 PPO 工程上难的根本原因(Day 3 §4.3)。
- InstructGPT 还在 RL 的目标里混入了一部分预训练数据的语言模型损失(PPO-ptx),用来减轻”对齐税”:RLHF 之后在一些公开 NLP 基准上的退化(Day 4 §5.2)。
- 实践中这个循环会迭代:用新策略采样、收集新的偏好、更新奖励模型、再做 RL(Llama 2 做了五轮)。
3.2 过度优化:奖励模型只是一个代理
动机:奖励模型 是真实偏好的一个有噪声的近似。我们对它做优化,优化得越狠,越可能找到”它打高分、但人并不喜欢”的回答。这就是 Goodhart 定律:“当一个度量变成目标,它就不再是好的度量。”
直观的例子:奖励模型学到了”长一点好”(§2.5)。RL 很快发现:写得越长,分越高。最后的模型长篇大论、反复罗列。这就是 reward hacking。
第 4 组用 best-of- 演示:每个回答有真实质量 和一个”可钻空子”的特征 (比如啰嗦程度),奖励模型给的是 ,真实质量是 (适度最好,过了就是坏事)。从参考模型采 个回答,挑奖励模型打分最高的那个:
| KL(上界 ) | 代理奖励 | 真实质量 | |
|---|---|---|---|
| 1 | 0.00 | 0.00 | −0.29 |
| 4 | 0.64 | 1.46 | 0.34 |
| 16 | 1.84 | 2.50 | 0.58 |
| 64 | 3.17 | 3.31 | 0.65 |
| 128 | 3.86 | 3.69 | 0.68 |
| 256 | 4.55 | 3.98 | 0.63 |
| 1024 | 5.93 | 4.60 | 0.54 |
代理奖励单调上升,真实质量先升后降。 越大,挑出来的越是”在奖励模型的误差方向上走了极端”的回答。
Gao et al. 2022(Scaling Laws for Reward Model Overoptimization)在真实模型上系统地测了这件事:用一个大的”金标准”奖励模型当人类的替身,用小的代理奖励模型做优化。以 为横轴,金标准奖励的形状是:best-of- 为 ,RL 为 。两者都是先升后降,而且代理奖励模型越大、数据越多,峰值越高、越靠后。
(表里的 KL 用的是常见公式 ,它其实是 best-of- 真实 KL 的一个上界,在大部分情况下已经足够准确。)
3.3 KL 约束:不要离参考模型太远
目标:RLHF 实际优化的不是”最大化奖励”,而是
为什么需要 KL 项:
- 奖励模型只在参考模型的分布附近可信。它是在 的样本上训练的,离得越远,它的判断越没有依据,误差越容易被利用(§3.2)。
- 防止模式坍缩:没有 KL 项,最优策略是把全部概率放在奖励最高的一个回答上,多样性归零。
- 保留语言能力:KL 项把策略拴在一个会说人话的模型附近。
这个目标有闭式最优解(Day 4 §1 推导):
也就是在参考模型上按奖励做指数加权: 大,几乎不动;,全部概率压到奖励最高的回答上。第 5 组用同一批回答算出每个 下的最优策略(这个设定下有解析解:均值平移 ,):
| KL(采样 / 解析) | 代理奖励 | 真实质量(采样 / 解析) | |
|---|---|---|---|
| ∞ | 0.00 / 0.00 | 0.00 | −0.30 / −0.30 |
| 2.0 | 0.25 / 0.25 | 1.00 | 0.12 / 0.12 |
| 1.0 | 0.99 / 1.00 | 1.99 | 0.40 / 0.40 |
| 0.6 | 2.66 / 2.78 | 3.25 | 0.52 / 0.53 |
| 0.4 | 5.23 / 6.25 | 4.49 | 0.35 / 0.33 |
| 0.3 | 7.42 / 11.11 | 5.26 | 0.04 / −0.30 |
太大学不到东西,太小就过度优化。 最优点在 KL 约 2.8 附近,和第 4 组 best-of- 的最佳点在同一个量级。 很小时采样估计比解析值低很多:40 万个样本里根本没有那么极端的回答,这也说明了”离参考模型太远的区域,参考模型根本采不到,奖励模型也从没见过”。
回答学习计划的自测题”KL 惩罚如果去掉会发生什么?” 策略会一路朝奖励模型的误差方向走:代理奖励持续上涨,真实质量先升后降(上表 很小的几行)。表现为 reward hacking:回答越来越长、反复讨好用户、堆砌格式、重复某些”高分短语”,甚至输出不再通顺;多样性坍缩到少数几种模板。Day 3 的参考代码会在 mini-GPT 上让你亲眼看到它。
3.4 对抗 reward hacking 的工具箱
| 手段 | 做什么 | 出处 |
|---|---|---|
| KL 惩罚 | 把策略拴在参考模型附近 | §3.3 |
| 奖励归一化 | 减均值、除标准差,让 的尺度有意义 | §2.2 |
| 更大、更多数据的奖励模型 | 推迟过度优化的拐点 | §3.2 |
| 奖励模型集成 | 取多个奖励模型的均值或最小值,一个模型的误差不容易被利用 | |
| 长度惩罚 / 长度控制 | 直接对付最常见的一种 hacking | §2.5 |
| 迭代收集数据 | 用新策略的样本重新标注、更新奖励模型,补上盲区 | §3.1 |
| 早停 | 按人工评测或留出的金标准奖励选检查点 |
§A 奖励模型:API 用法与边界
A.1 用 AutoModelForSequenceClassification 做奖励模型
model = AutoModelForSequenceClassification.from_pretrained(name, num_labels=1, torch_dtype=torch.bfloat16)
model.config.pad_token_id = tok.pad_token_id
r = model(input_ids=ids, attention_mask=att).logits.squeeze(-1) # (batch,) 每条一个标量
loss = -F.logsigmoid(r_chosen - r_rejected).mean()
num_labels=1让分类头输出一个标量。加载时会提示score.weight是新初始化的,这是正常的。- 用 LoRA 训练时把
task_type设成"SEQ_CLS",peft 会自动把分类头score设为可训练。
A.2 取”最后一个 token”的坑
- HF 的序列分类模型靠
config.pad_token_id找每行最后一个非 padding 的 token。不设pad_token_id时,batch 大于 1 会直接报错。 - 参考代码的
last_token_index用attention_mask.sum(1) − 1,只在右补齐时成立。左补齐时最后一个 token 永远在最后一列。换补齐方向前先确认取的位置对不对。 - pad token 不要和文本里会出现的 token 相同。如果把
eos当 pad、而 chat template 里本来就有eos,“找第一个 pad”的逻辑会取错位置。
A.3 数据集
ds = load_dataset("HuggingFaceH4/ultrafeedback_binarized")
ds["train_prefs"][0]["chosen"] # [{"role": "user", ...}, {"role": "assistant", ...}] 完整对话
chosen和rejected都是完整的对话(包含同一个提示),直接用 chat template 渲染即可。- 训练前先检查两类回答的平均长度:如果被选中的回答系统性地更长,奖励模型一定会学到长度偏好(§2.5)。参考代码训练完会打印奖励与长度的相关系数。
A.4 trl 的 RewardTrainer
trl 提供了 RewardTrainer,内部就是上面的 BT 损失。和 Week 2 Day 6 §A.4 一样,它的参数名和数据格式要求在不同版本间变过,用之前打印一条处理后的样本,确认 chosen / rejected 各自的 input_ids 和它取分数的位置。
§B 实操
- 跑通
w3day2_checks.py,5 组结论都看懂。第 4、5 组把 KL 和真实质量画成曲线,和 Gao et al. 2022 的图对照。 - 推导(1h):(a) 从 Gumbel 噪声推出 Bradley-Terry;(b) BT 损失对 、 的梯度;(c) best-of- 的 KL 上界 (提示:best-of- 的概率是 ,其中 是分数的累积分布)。
- mini-GPT 上的玩具奖励模型(1h):
python code/w3d2_reward_model.py --mini。看测试准确率离贝叶斯上限多远,以及它给”重复的正面词”打多少分。这个奖励模型明天 PPO 要用,一定要跑。 - 真实的奖励模型(1.5h,GPU 更好):
--hf,在 UltraFeedback 的 2000 对上训练,记录测试准确率与”奖励 vs 长度”的相关系数。 - 读论文:InstructGPT 的第 3 节(方法)与图 1;Gao et al. 2022 的图 1。
参考实现:code/w3d2_reward_model.py。
§C 自测
动机与直觉层
- SFT 缺了哪三样东西?为什么”比较”比”写”更适合收集数据?
- 为什么 1.3B 的 InstructGPT 能胜过 175B 的 GPT-3?这说明了什么?
- 奖励模型为什么要从 SFT 模型初始化?为什么取最后一个 token?
- 奖励模型测试准确率只有 70%,你会得出什么结论?
- 什么是过度优化?为什么代理奖励一直涨,真实质量却会下降?
- KL 惩罚去掉会发生什么?reward hacking 长什么样?
推导与计算层
- 从”效用 + Gumbel 噪声”推出 Bradley-Terry 模型,写出损失和梯度。
- 为什么奖励只能确定到一个常数?这对 RL 有什么影响?
- 写出 RLHF 的 KL 约束目标和它的最优解形式,解释 和 两个极端。
- 个回答的完整排序能产生多少个比较对?为什么要把它们放在同一个 batch 元素里?
工程层
- 画出 RLHF 三个阶段的数据流:每一步训练哪个模型、用什么数据、哪些模型冻结。
- 用 HF 训练奖励模型时,
pad_token_id和补齐方向各有什么坑? - 列出至少四种对抗 reward hacking 的手段。
出关标准
- 能说清 SFT 的三个局限,以及偏好数据为什么更容易收集
- 能从 Gumbel 噪声推出 Bradley-Terry 模型,写出损失和梯度
- 能解释奖励模型准确率的天花板,以及它会学到标注者的偏见
- 能画出 RLHF 三阶段的数据流
- 能用 Goodhart 定律解释过度优化,说清 KL 惩罚的三个作用
- 跑通
--mini,得到 Day 3 要用的奖励模型