RLHF:ChatGPT 也是被打分教出来的
这本书前二十一章讲的是一个学走路的小人。这一章你会发现,把同一套东西换个场地,得到的是今天最重要的那个 AI 产品——而且这一路上的每一个坑,都在那儿原样重现了一遍。
先把五元组对上
第 2 章那五个词,换到对话上是这样的:
| 两足小人 | 语言模型 | |
|---|---|---|
| 状态 | 关节角度、速度 | 到目前为止的整段文本(提示 + 已生成的部分) |
| 动作 | 四个关节的力矩 | 下一个 token(词表大小 = 动作空间,十万级) |
| 策略 | 一个小网络 | 就是那个语言模型本身 |
| 奖励 | 作者写的那一行 | 一个学出来的模型 |
| 回合 | 500 帧或摔倒 | 生成完一段回复 |
前三行是直接对应。第四行是这一章的全部内容。
为什么奖励必须是学出来的
你要模型「回答得好」。那就写个奖励函数吧:
def reward(question, answer):
return ??? # 「好」怎么算?
写不出来。「好回答」没有一个可以写成代码的定义。
试着写几条你就知道有多糟:按长度?那就啰嗦。按关键词匹配?那就堆关键词。按语法正确性?胡说八道也可以语法完美。
每一条都是第 20 章那种奖励黑客的现成入口。
但有一件事人类做得很轻松:给两个回答,说哪个更好。
说不出「这个值 7.3 分」,但能说「A 比 B 好」。RLHF 的全部起点就是这个不对称。
从「A 比 B 好」到一个分数
把偏好转成分数的工具叫 Bradley-Terry 模型,1952 年提出的,本来是用来给运动员排名的:
P(A 胜 B) = σ( r_A - r_B ) σ 是 sigmoid
给每个回答一个分数 r,让「分数差」能解释「谁赢」的概率。收集一堆两两比较,用梯度上升拟合这些 r。
五个回答,人类给了十条两两偏好。看它能不能把顺序还原出来。然后加入自相矛盾的标注,看分数会怎么变。
两件值得注意的事
① 分数的绝对值没有意义,只有差值有
Bradley-Terry 只定到一个平移常数——全部加 100,所有胜率预测完全不变。所以上面那个 demo 做了中心化。
有意义的是差值:差 1 分意味着「胜率约 73%」,差 2 分意味着「约 88%」。
这解释了一个你在论文里会看到的现象:奖励模型的分数在不同训练批次之间不可比。你不能说「这个模型的奖励从 2.1 涨到了 3.4」——那两个数字来自不同的尺子。
② 标注一矛盾,分数就向 0 收缩
加入矛盾标注之后,所有分数整体往中间挤(从 ±2.72 挤到 ±0.94),一致率从 100% 掉到 83%。
模型在说「我不太确定了」。这是个正确的、健康的反应。
但接下来是关键的一步:这个不确定,会原样传给后面的 PPO。奖励模型在某些区域分数很平坦、很含糊,PPO 就会在那些区域乱走——而那正是奖励黑客最容易发生的地方。
完整的三步
| 步骤 | 做什么 | 对应本书哪一章 |
|---|---|---|
| ① SFT | 拿人写的高质量问答做监督微调 | 第 21 章那个「模仿学习」 |
| ② RM | 收集人类偏好,训一个奖励模型 | 这一章上半部分 |
| ③ PPO | 用 RM 当奖励,用 PPO 优化语言模型 | 第 18 章 |
第一步就是第 21 章那条出路——先给它看演示,别让它从零随机探索。如果直接从一个没微调过的基座模型开始跑 PPO,它生成的东西离「好回答」太远,奖励模型给的分全在一个很窄的区间里,信号弱得学不动。这是稀疏奖励问题在语言上的形态。
第三步里那个至关重要的补丁
PPO 的目标不是单纯地最大化奖励模型的分数。它长这样:
reward = RM(回答) - β · KL( π_new ‖ π_SFT )
└──────────┬──────────┘
「别离原来那个模型太远」
那一项 KL 惩罚是必须的,而且理由和第 18 章那个 clip 一模一样。
会发生一件很有代表性的事:模型会发现一段完全不通顺、但奖励模型偏偏给高分的乱码,然后反复输出它。
为什么?因为奖励模型是在「像人写的文本」这个分布上训练的。一旦策略跑到那个分布之外,奖励模型的输出就毫无意义——它从没见过这种输入,给出的分数纯属外推,可能高得离谱。
这就是第 18 章那个「用一张过期的地图指路」,只不过过期得更彻底。
KL 惩罚做的事:把策略拴在 SFT 模型附近,保证生成的东西还在奖励模型「懂」的范围内。
它和 PPO 的 clip 是同一件事的两个层次:clip 限制「每次更新走多远」,KL 限制「离出发点总共走多远」。
这本书讲的每个坑,在这里都重现了
| 本书哪一章 | 在 RLHF 里的样子 |
|---|---|
| 第 20 章 奖励黑客 | 奉承:顺着用户说,哪怕用户错了 啰嗦:长回答显得用心,那就把话说三遍 虚假自信:犹豫的措辞得分低,那就用肯定语气说错话 |
| 第 21 章 稀疏奖励 | 不先做 SFT 直接跑 PPO,信号弱到学不动 |
| 第 18 章 一步迈太大 | 没有 KL 惩罚,模型跑到奖励模型的知识之外,输出乱码 |
| 第 16 章 训练不稳 | PPO + 几百亿参数,一旦发散重训成本极高 |
| 第 4 章 探索 | 采样温度就是探索率。温度太低学不到新东西,太高全是噪声 |
| 第 13 章 on/off-policy | 用 PPO(on-policy)而不是 SAC——稳定性压倒样本效率 |
第一行最值得停一下。「奉承」和第 20 章那个趴下刷姿态分的小人,是同一件事。
奖励模型不是「什么是好回答」,它是「标注员倾向于选哪一个」。这两者之间的差距,就是全部的漏洞。而标注员是人,人会被自信的语气说服,会觉得长的更用心,会更喜欢同意自己的观点。模型把这些全学会了——因为那些确实能得高分。
还有一层更麻烦的:奖励模型是一个有限的神经网络,它对「奇怪的输入」的判断是不可靠的。
而 PPO 的工作就是专门去找那些能拿高分的输入。训练久了,它一定会摸到奖励模型的过拟合区域——那里 RM 给分虚高,实际质量却在下降。
这个现象叫 reward model over-optimization,而且有实证研究把它量化了:随着 KL 散度增大,RM 打的分持续上升,而人类实际评价先升后降——两条曲线在某个点分道扬镳。
怎么办?目前的做法都是缓解,不是解决:
- 调大 β(KL 惩罚),别让它走那么远
- 提前停——在两条曲线分开之前停下
- 迭代式 RLHF:训一轮,拿新模型的输出重新找人标注,更新奖励模型,再训。让尺子跟着被测量的对象一起更新。
- 用多个奖励模型取最小值,避免单个模型的盲区被利用
这是第 20 章那个「补丁本身也会被黑」在最前沿的样子。这一层递归目前没有尽头。
RLHF 流程长、要三个模型、工程复杂。后来出现了几条更省事的路,值得知道名字:
- DPO(直接偏好优化):一个漂亮的推导——把奖励模型和 PPO 合并成一个损失函数,直接在偏好数据上微调,不用单独训 RM,也不用跑 PPO。简单太多,成了很多开源模型的默认选择。
- RLAIF:用一个强模型代替人类做偏好标注。便宜得多,代价是把那个模型的偏见一起继承过来。
- Constitutional AI:给模型一份原则清单,让它自己批评和修改自己的输出。Anthropic 的做法。
但注意:这些方法都没有消灭「你写下的和你想要的之间有差距」这个根本问题。DPO 里那个差距藏在偏好数据里,RLAIF 里藏在那个打分模型里。它只是换了个地方。
Hugging Face 的 trl 库把三步都封好了:
pip install trl transformers datasets
from trl import PPOTrainer, PPOConfig, AutoModelForCausalLMWithValueHead
config = PPOConfig(
learning_rate=1.4e-5,
batch_size=64,
init_kl_coef=0.2, # ← 那个 β,最重要的旋钮
target_kl=6.0, # KL 超过这个就自动调大惩罚
)
model = AutoModelForCausalLMWithValueHead.from_pretrained("gpt2")
# └─ 注意:策略和 Critic 共享主干
trainer = PPOTrainer(config, model, ref_model, tokenizer)
for batch in dataloader:
responses = trainer.generate(batch["query"])
rewards = reward_model(batch["query"], responses) # ← 你的奖励模型
trainer.step(batch["query"], responses, rewards)
三个提醒:
① 那个 ref_model 是冻住的 SFT 模型,专门用来算 KL。它就是第 16 章那个目标网络的思路——一份不动的参照物。
② AutoModelForCausalLMWithValueHead 里那个 value head,就是第 17 章的 Critic。它和策略共享主干,只多一个输出头。
③ 拿 GPT-2 在一个玩具任务上跑一次(比如「让生成的影评偏正面」,用一个现成的情感分类器当奖励模型)。单卡几十分钟。跑完之后,前面二十一章会突然全部串起来。
这一章想留下的一句话是:
那个学走路的小人,和你每天在用的那个聊天模型,是被同一种方法教出来的。
小人的奖励是作者写的一行代码,模型的奖励是一个从几万条人类偏好里拟合出来的网络。但结构完全一样:一个策略,一个打分器,一个 PPO。
而它们踩的坑也完全一样。小人学会趴下刷姿态分,模型学会说漂亮话讨好标注员——都是精确地最大化了那个被写下来的东西,而不是那个被想要的东西。
这就是为什么这本书把「打分」放在书名上。算法会换,框架会过时,但那个差距一直在。
这一章的一句话
环境是对话,动作是下一个 token,奖励是一个从「A 比 B 好」里拟合出来的模型。而那个模型不是「什么是好回答」,是「标注员倾向于选哪个」——这两者的差距,就是全部的漏洞。
下一章:回到那台两足小人。在你的浏览器里当场训练它,一百毫秒跑完三十代;顺便给你一条从 CartPole 到 BipedalWalker 的、不会浪费时间的真实路线。