全书导航
大模型之路:从图灵、感知机到 ChatGPT · 卷 4

第 30 章:RLHF:当人类开始给 AI 打分

本章问题:当指令微调不够——如何让模型的回答不止"正确",而且"有用、安全、有判断力"?


30.1 标注员分歧问题

上一章留了一个尾巴:指令微调的根本局限在于它依赖"理想回答"的标注。但标注员是人。两个不同的人面对同样的提示词,可能写出完全不同的"理想回答"——一个人觉得回答要简洁,另一个人觉得要详尽;一个人觉得要正式,另一个人觉得要亲切。

更麻烦的问题是:很多问题根本没有唯一的理想回答。 "如何做一个好父母?""人生的意义是什么?""这只股票该买吗?"——这些问题本身不存在唯一的标准答案。

指令微调通过"让标注员手写理想回答并训练模型去模仿"来处理这个问题,但核心矛盾未解——你只能模仿一个标注员的特定风格和特定选择,无法从多个标注员的比较中学习"什么样的回答普遍更好"。

这就引出了 RLHF(基于人类反馈的强化学习,Reinforcement Learning from Human Feedback)的核心动机:

不要教模型一个"正确答案"。教模型判别"两个回答中哪一个更好"。让模型自己去找出能产生更好回答的策略。


30.2 从"模仿正确答案"到"学什么是好"

RLHF 的基础想法可以追溯到 2017 年 OpenAI 的 Christiano 等人的论文——《从人类偏好中进行深度强化学习》。原本的问题是:如何让强化学习 agent 在奖励函数难以精确定义的复杂任务(如后空翻机器人控制)中学到好策略?

传统 RL 需要一个精确定义好的奖励函数——你到达目标位置加 1 分,你摔倒扣 5 分。但在复杂任务中——比如"让模拟机器人做一个漂亮的后空翻"——写好奖励函数极其困难。你无法用代码写出什么算"漂亮"。

Christiano 等人的方案是:用人类对行为轨迹的偏好比较来训练一个奖励预测器——然后用这个学到的奖励函数去训练 RL agent。 人类需要做的是:看两段 agent 执行任务的视频,说"B 比 A 更好"——比起为每个动作写奖励函数,这直观得多。

关键的新洞见是:比较(A vs B)比评分(给 A 打分 1-10)更可靠、一致性更高。人很难对单一段落给出精确的 7/10 分——但人能相对一致地判断"回答 B 比回答 A 更有帮助"。标注者间的一致性在偏好比较(preference comparison)任务上显著高于绝对评分(absolute rating)任务,OpenAI 和 Anthropic 的团队都报告了这一发现。

2022 年,OpenAI 把同一套逻辑搬到了语言模型上——结果就是 InstructGPT 和后来的 ChatGPT。


30.3 三步训练法——RLHF 最核心的精要

RLHF 的训练可以拆成三个明确的步骤。以下是每一步的细节和你需要知道的工程直觉。

步骤一:监督微调(SFT, Supervised Fine-Tuning)

先取一个预训练好的基础模型(如 GPT-3)。雇佣人类标注员——给他们提示词,让他们手写高质量回答。用这些 (提示词, 高质量回答) 对去有监督地微调模型。

这一步本质上就是指令微调——目的是让模型在 RL 开始之前已经有了"基本遵循指令"的能力。如果 RL 在一张白纸上开始——模型会在前期随机生成各种垃圾,人类需要看无穷无尽的垃圾才能给出偏好判断——效率极低。SFT 模型提供了一个"已经大致合理"的起点——RL 在"尚可"的基础上追求"更好"。

步骤二:训练奖励模型(RM, Reward Model)

取 SFT 模型,去掉最后输出层的 lm_head(语言模型头,把隐藏状态投影到词汇表),替换为一个奖励值输出头(把隐藏状态投影为一个标量分数)。

对同一个提示词——让 SFT 模型生成 K 个不同的回答(K 通常为 4-9)。人类标注员对这 K 个回答进行排序——从最好到最差。

排序数据被转换成偏好对(preference pairs)——对任意两个回答 (A, B),标注员的排序告诉你哪个更好。然后用这些偏好对训练奖励模型——训练目标是让奖励模型对"更好"的回答打出"更高的"分。

具体损失函数是:

loss = -E[log(σ(r_better - r_worse))]

其中 σ 是 sigmoid 函数——把两个得分的差值映射到 (0,1) 区间。这个损失函数的直觉是:使奖励模型能在两种回答之间做出偏好的正确预测——类似于学习一个二分类器,每次给它一对回答,它判断哪个更好;交叉熵惩罚鼓励它在训练中正确区分每一对已知偏好的回答。

步骤三:用 PPO 进行强化学习

拿步骤一中训练好的 SFT 模型作为初始策略 π₀,步骤二中训练好的 RM 作为奖励函数。用近端策略优化(PPO,Proximal Policy Optimization,Schulman et al., 2017)算法更新模型参数——让模型最大化:

奖励 = RM 评分 - β × KL(π || π₀)

= 生成高奖励(在 RM 看来好)的回答,同时不要偏离 SFT 模型太远。

β(beta)是一个关键的超参数——如果 β 太小,模型会过度优化 RM,找到 RM 评分高但实际质量差或奇怪的"奖励游戏"策略(例如生成讨喜但空洞的客套话堆砌、大量重复礼貌用词来"刷分")。如果 β 太大,模型会拒绝偏离 SFT 策略——不会变得更好。

KL 散度(Kullback-Leibler Divergence)衡量两个概率分布的差异程度。在这里——它计算新策略 π 生成的 token 分布和初始策略 π₀ 的分布在统计上有多大差异。KL 惩罚的作用是让模型记住自己的基本语言能力——不要因为追求高奖励分而产生语法错乱或丧失连贯性。

这就是 RLHF。

三步总结:

基础模型 → SFT(学"按要求回答")→ RM(学"判断什么回答好")→ PPO(用 RM 做奖励裁判,同时保持不要分太远)

30.4 为什么必须是 RL——不能直接用人类偏好排序训练吗

一个自然的问题:既然你有偏好排序数据——为什么不能直接把这些数据拿去做有监督训练?

你可以。这叫做 DPO(Direct Preference Optimization)——直接偏好优化——2023 年由 Rafailov 等人提出,直接把偏好数据转化为一个可优化的分类损失函数,跳过了"训练奖励模型→强化学习"这两个步骤。DPO 的数学性质比 RLHF 更简单——它证明了在特定条件下,最优策略可以通过一个二分类式损失直接解出,不需要显式的 RM 或 PPO 对齐。

但 RLHF 让研究者能先观察 RM 在和人类偏好数据的拟合程度——如果 RM 的准确率不够(如在一些微妙的安全判断上只有 65%),你可以调整数据或重新标注来做第三轮、第四轮——在正式 RL 之前反复改进 RM。在 OpenAI 的 RLHF 实践中,RM 准确率通常在 70-75% 左右——这个分数看起来不高,主要是因为很多偏好对的内在差异非常细微(两个回答都合理,标注员意见也不统一)。实际中,RLHF 的效果往往比这个数字能暗示的要好——因为 RM 不需要完美,只需要在正确的方向上给出趋势性的分数信号。

DPO 的优势是简单——不需要训练单独的 RM 或运行 RL,训练更稳定、更省算力。RLHF 的优势是灵活——你可以反复改进 RM 而无需回到每一次修改都重新跑完整 SFT→RM→PPO 的完整管路。两者的选择至今仍是活跃的研究和工程话题。


30.5 最小代码:一个玩具奖励模型的训练

以下代码在极小规模上演示 RM 训练的核心逻辑——偏好对的二分类损失:

python
import torchimport torch.nn as nnclass ToyRewardModel(nn.Module):    """极简奖励模型骨架:输入 = 一个回答的向量表示,       输出 = 一个标量奖励分。"""    def __init__(self, hidden_dim=128):        super().__init__()        self.score = nn.Sequential(            nn.Linear(hidden_dim, 64),            nn.ReLU(),            nn.Linear(64, 1),        )    def forward(self, response_vec):        return self.score(response_vec).squeeze(-1)# ---- 模拟偏好对数据 ----# 每个样本:同一 prompt 的两个回答向量(rA, rB),标注员认为 A > Bprompt_vec = torch.randn(4, 128)  # batch=4, 上下文向量rA = prompt_vec + torch.randn(4, 128) * 0.1  # "更好"的回答rB = prompt_vec + torch.randn(4, 128) * 0.3  # "更差"的回答(更多噪声)rm = ToyRewardModel()opt = torch.optim.Adam(rm.parameters(), lr=1e-3)for step in range(200):    score_A = rm(rA)    score_B = rm(rB)    # 损失:鼓励 score_A > score_B    loss = -torch.log(torch.sigmoid(score_A - score_B) + 1e-8).mean()    opt.zero_grad()    loss.backward()    opt.step()    if step % 40 == 0:        acc = (score_A > score_B).float().mean().item()        print(f"step {step:>3d}  loss={loss.item():.4f}  "              f"偏好准确率={acc:.2f}")

运行输出会显示 loss 下降,偏好准确率从约 0.5 逐步升到接近 1.0——奖励模型学会了区分两种回答。

完整的 RLHF 还需要在这个 RM 训练完成后加一层 PPO 训练循环——用 RM 打分、加 KL 惩罚、更新语言模型参数。但核心概念在这段代码中已经可理解:RM 的训练目标是判断两个回答哪个更好,奖励分差越大→模型越自信。


30.6 本章小实验:你是人类标注员

找 3 个对话式 AI(ChatGPT、Claude、Kimi、文心一言、通义千问等)。对每一个,输入同样的提示词:

"我的朋友最近失业了,心情很低落。我应该怎么帮助他?"

你会得到 3 个回答。现在扮演标注员——把这三个回答从"最有帮助"到"最没帮助"排序。

仔细想想你排序时用的标准是什么:

  • 是否表达了共情?
  • 是否给出了具体可操作的建议?
  • 是否同时考虑了情感支持和实际行动?
  • 是否注意了说话的安全边界(比如没有建议"帮他找工作"而忽略了情绪)?

你没有给这些回答各自打一个绝对分——你直接比较了"谁比谁更好"。而且你做到了。这就是 RLHF 中标注员做的事——你在这个小实验里直接体验了训练 RM 所需要的核心数据来源。


30.7 本章地图

text
问题:当指令微调不够——如何让模型的回答不止"正确",而且"有用、安全、有判断力"?核心思路:不要教模型"正确答案"——教模型判别"两个回答中哪一个更好",让模型自己去找出好策略。三步训练:  SFT(用人工写的理想回答做监督微调)→   RM(在同一 prompt 的多个回答上做偏好比较,训练奖励模型学会"打分")→   PPO(用 RM 作为奖励裁判,加 KL 惩罚防止偏离太远,用强化学习优化模型)。局限:RM 只能学到人类标注员的偏好——标注员有偏见,RM 就有偏见;标注员对复杂问题不一致,RM 就学会"选择多数"。偏好数据本身的文化依赖性也有待持续改善。替代:DPO(直接偏好优化)——跳过 RM 和 RL,直接把偏好数据变成分类损失——更简单、更稳定,但不能反复调试 RM。今天:RLHF 是 ChatGPT、Claude、Gemini 等主流对话模型的核心对齐技术——它标志着 AI 训练从"让模型猜得更准"转到了"让模型的输出对人类更有价值"。

30.8 本章结语:从猜词到答问——训练目标的两次脱胎换骨

卷三的训练目标是"猜下一个词"——这是语言建模的唯一目标:在海量文本上,模型奋力降低预测下一个词的误差。

卷四的前三章,你看到的这个唯一的训练目标被连续地叠加了三层新目标:

  • 指令微调:不仅要预测下一个词——还要让生成的内容匹配指令的类型。
  • 人类偏好比较:不仅要匹配指令——还要和人类觉得"有用/安全"的期望对齐。
  • 强化学习:不仅要做一次对的事——还要在保持自身语言能力的前提下,策略性地最大化人类的满意度。

RLHF 是这三层中最复杂的一层——但也是最关键的一层。因为它标志着 AI 训练中"目标函数定义权"的转移——从"损失函数只看语言建模的交叉熵"变成了"损失函数里嵌入了人类价值判断"。尽管这种嵌入还是很粗糙的——用几百个标注员的偏好比较数据来近似整个人类的价值光谱——但这是训练从纯数据驱动的自动统计回归朝向"负责任对齐"所能迈出的必要的第一步。

但即使是最好的对齐——最终也是在一个巨大的基础模型上跑几小时到几天的微调。基础模型本身的训练如何做?从头训练一个大语言模型需要什么?

从下一章开始——卷四进入实操部分。你将从头训练你自己的 tiny GPT——从分词器到推理,完整一盘。

下一章:从零开始训练一个小型中文 GPT——完整实操。

SECTION §02 · ENGAGE

Discussion

留言区 · GitHub-powered comments via Giscus