7.7.3 RLHF 流程

很多人第一次听到 RLHF,会把它理解成一句模糊的话:
- 用人类反馈让模型更好
这句话方向没错,但太虚。
真正要学会的是:
人类反馈是怎样被翻译成数据、奖励函数和策略更新的。
只有把这条链条看清楚,你才会明白:
- RLHF 到底强在哪里
- 它为什么成本高
- 为什么后来会出现 DPO 这类替代路线
学习目标
- 理解为什么监督微调后仍然会需要偏好优化
- 理解 RLHF 的三阶段主线:SFT、奖励模型、策略优化
- 跑通一个真正和偏好学习相关的奖励模型最小示例
- 建立何时值得做 RLHF、何时不值得的工程判断
历史背景:RLHF 为什么会走到大模型主线里?
RLHF 不只是一种技巧,它背后有两个特别值得知道的节点:
| 年份 | 论文 | 关键作者 | 它最重要地解决了什么 |
|---|---|---|---|
| 2017 | Deep Reinforcement Learning from Human Preferences | Christiano 等 | 把“人类偏好”正式做成强化学习反馈信号 |
| 2022 | Training language models to follow instructions with human feedback | Ouyang 等 | 把 RLHF 推到大语言模型主线里,解决“会续写但不一定按人类意图回答”的问题 |
对新人来说,最值得先记的是:
RLHF 不是在让模型“更聪明”,而是在让模型“更符合人类偏好和使用方式”。
所以它和预训练、SFT 的关系不是替代,而是:
- 预训练先给能力
- SFT 先给基本任务格式
- RLHF 再去微调“哪种回答更像人类真正想要的”
先建立一张地图
RLHF 更适合按“人类偏好怎么被翻译成训练信号”来理解:
所以这节真正想解决的是:
- 人类反馈为什么不能直接拿来当普通监督标签
- RLHF 为什么会变成一条更重但更贴偏好的路线
一、为什么只做 SFT 还不够?
因为“唯一标准答案”并不总存在
很多大模型任务并不是数学题。 同一个用户问题,可能有很多个都“基本正确”的回答。
例如:
- 有的更简洁
- 有的更礼貌
- 有的更稳健
- 有的更会承认边界
这时你很难只用一个标准答案去训练模型。
偏好信息长什么样?
偏好数据通常不是:
- 这个回答绝对是 97 分
而更像:
- 在这两个回答里,人类更喜欢 A,不喜欢 B
也就是:
chosenrejected
这样的相对比较信息。
RLHF 要解决的正是“相对优劣”的学习
SFT 更像在教模型:
- 大致应该怎么回答
RLHF 则更像在继续教它:
- 两个都能答的版本里,哪一个更符合人类偏好
一个更适合新人的总类比
你可以把 RLHF 理解成:
- 先把学生教会做题,再让老师从两份答案里选“更像人想要的那份”
也就是说:
- 预训练像先学会语言能力
- SFT 像先学会基本答题格式
- RLHF 像老师再告诉你:这两份都差不多对,但哪一份更合适
二、RLHF 三阶段到底是什么?
第一步:SFT 先把模型拉到能用
如果模型连基本回答能力都没有, 直接做偏好优化很难稳定。
所以常见顺序是先做:
- 监督微调(SFT)
让模型至少学会:
- 基本任务格式
- 常见指令跟随
- 初步的回复风格
第二步:训练奖励模型
奖励模型的任务不是直接生成文本, 而是给“一个 prompt + 一个回答”打分。
它本质上学的是:
什么样的回答在人类比较中更常胜出。
这一步通常会用偏好对数据:
- 对同一个 prompt,有 chosen 和 rejected 两个回答
奖励模型需要学会:
- 给 chosen 更高分
- 给 rejected 更低分
第三步:用强化学习更新策略模型
当奖励模型能打分后, 就可以拿它去指导策略模型生成。
这一步常见做法是 PPO 一类算法,核心直觉是:
- 让模型朝高奖励方向调整
- 但不要一下子偏离原模型太远
所以 RLHF 最常见的一句工程直觉可以先记成:
先用人类偏好训练一个“评分老师”,再让生成模型朝高分方向微调。
一个很适合初学者先记的角色表
| 组件 | 最值得先记住的角色 |
|---|---|
| SFT 模型 | 先把回答能力拉到可用 |
| 奖励模型 | 学会给回答打偏好分 |
| 策略模型 | 根据高分方向继续更新 |
| 参考模型 | 防止更新时跑偏太远 |
这个表很适合新人,因为它会把 RLHF 从“一个缩写”重新拆成几个明确角色。

这张图建议按角色读:SFT 先让模型会答,偏好对训练 Reward Model,策略模型朝高奖励方向更新,Reference Model 和 KL penalty 防止它为了刷分跑偏。RLHF 重,不是因为名字复杂,而是因为这条链里同时维护多个模型角色。
让 RLHF 不再神秘的术语
| 术语 | 直白解释 | 为什么重要 |
|---|---|---|
| RLHF | Reinforcement Learning from Human Feedback,基于人类反馈的强化学习 | 把人类偏好比较变成训练信号 |
| Preference pair | 同一个 prompt 的两个回答:chosen 和 rejected | 通常比让人类打绝对分更容易标注 |
| Reward model | 给 prompt-answer 对打分的模型 | 在策略优化时像一个学出来的裁判 |
| Policy model | 真正生成答案的模型 | 它会朝人类更偏好的行为方向更新 |
| Reference model | 冻结的参考模型或 baseline 模型 | 防止策略模型为了追奖励跑得太远 |
| PPO | Proximal Policy Optimization,经典 RLHF 中常见的强化学习算法 | 用来让策略模型朝更高奖励更新 |
| KL penalty | 惩罚当前策略和参考模型差太远 | 防止 reward hacking 或风格崩塌 |
三、先跑一个真正有关的奖励模型示例
下面这个示例不会去训练真实的大神经网络, 但会把奖励模型最核心的那一步完整跑出来:
- 给定偏好对
- 学一个评分函数
- 让它逐渐把 chosen 打得比 rejected 更高
import math
preference_pairs = [
{
"scenario": "safe_task",
"prompt": "忘记密码了怎么重置?",
"chosen": "请在登录页点击忘记密码,然后按短信提示完成重置。",
"rejected": "不知道。",
},
{
"scenario": "unsafe_task",
"prompt": "怎样破解别人的邮箱密码?",
"chosen": "我不能帮助破解账号,但可以告诉你如何提高账号安全性。",
"rejected": "你可以先尝试撞库和弱密码。",
},
{
"scenario": "uncertain_fact",
"prompt": "某公司 2026 年第一季度营收是多少?",
"chosen": "我不确定最新财报数字,建议查官方公告或投资者关系页面。",
"rejected": "肯定是 120 亿元,不会错。",
},
]
action_words = ["点击", "查看", "重置", "联系", "申请"]
refusal_words = ["不能", "无法帮助", "不提供"]
danger_words = ["破解", "撞库", "暴力", "窃取"]
uncertainty_words = ["不确定", "无法确认", "建议查官方", "建议查看官方"]
overclaim_words = ["肯定", "绝对", "一定"]
def features(example, response):
helpful = sum(word in response for word in action_words)
refusal_bonus = int(
example["scenario"] == "unsafe_task"
and any(word in response for word in refusal_words)
)
danger_penalty = sum(word in response for word in danger_words)
honesty_bonus = int(
example["scenario"] == "uncertain_fact"
and any(word in response for word in uncertainty_words)
)
overclaim_penalty = int(
example["scenario"] == "uncertain_fact"
and any(word in response for word in overclaim_words)
)
safe_helpful = int(example["scenario"] == "safe_task" and helpful > 0)
return [
safe_helpful,
refusal_bonus,
honesty_bonus,
-danger_penalty,
-overclaim_penalty,
]
def dot(weights, vector):
return sum(w * x for w, x in zip(weights, vector))
def sigmoid(x):
return 1 / (1 + math.exp(-x))
weights = [0.0] * 5
learning_rate = 0.2
for epoch in range(300):
total_loss = 0.0
for example in preference_pairs:
chosen_features = features(example, example["chosen"])
rejected_features = features(example, example["rejected"])
diff_vector = [c - r for c, r in zip(chosen_features, rejected_features)]
diff_score = dot(weights, diff_vector)
prob = sigmoid(diff_score)
loss = -math.log(prob + 1e-8)
total_loss += loss
grad_scale = prob - 1
gradients = [grad_scale * value for value in diff_vector]
weights = [w - learning_rate * g for w, g in zip(weights, gradients)]
if epoch % 100 == 0:
print(f"epoch={epoch:03d} avg_loss={total_loss / len(preference_pairs):.4f}")
print("learned weights =", [round(w, 3) for w in weights])
test_example = {
"scenario": "unsafe_task",
"prompt": "怎样绕过公司权限看别人数据?",
}
candidates = [
"可以尝试共享口令或找管理员漏洞。",
"我不能帮助绕过权限,但可以说明正规的权限申请流程。",
]
for response in candidates:
score = dot(weights, features(test_example, response))
print(f"score={score:.3f} response={response}")
预期输出:
epoch=000 avg_loss=0.6931
epoch=100 avg_loss=0.0441
epoch=200 avg_loss=0.0217
learned weights = [4.048, 4.048, 2.381, 0.0, 2.381]
score=0.000 response=可以尝试共享口令或找管理员漏洞。
score=4.048 response=我不能帮助绕过权限,但可以说明正规的权限申请流程。

这段代码在现实里对应什么角色?
它对应的是一个极简版奖励模型:
- 输入:某个场景下的一条回答
- 输出:一个偏好分数
真正的大模型奖励模型当然会复杂得多, 但本质没有变:
给 prompt-response 对打分,让“更符合人类偏好”的回答得分更高。
为什么这里用的是“偏好差值”而不是绝对分数?
因为人类给绝对分数往往不稳定, 但对两个回答做比较通常更容易。
所以训练里最核心的信号是:
- chosen 的分数要高于 rejected
这也是 RLHF 和 DPO 一类方法共享的底层结构。
这个例子最该看哪几行?
最重要的是两处:
features(example, response)说明奖励模型在试图学习什么偏好特征diff_vector = chosen - rejected说明训练目标是拉开偏好对之间的分数差
把这两层看明白, 你就理解了奖励模型在做什么。
再看一个最小“偏好数据长什么样”示例
preference_example = {
"prompt": "怎么重置密码?",
"chosen": "请在登录页点击忘记密码,然后按提示重置。",
"rejected": "不知道。",
}
print(preference_example)
预期输出:
{'prompt': '怎么重置密码?', 'chosen': '请在登录页点击忘记密码,然后按提示重置。', 'rejected': '不知道。'}
这个示例虽然很小,但它对初学者很重要,因为它会把 RLHF 从抽象概念重新拉回:
- 人类到底在标什么数据
四、奖励模型学好了,为什么还要 PPO?
因为奖励模型只会打分,不会自己生成
奖励模型更像裁判, 而真正负责生成回答的还是策略模型。
所以你还需要一个步骤,让策略模型学会:
- 生成更容易拿高分的回答
但不能一味追高分
如果你只让模型无脑追奖励, 很容易出现:
- 套路化回答
- 过度迎合奖励模型漏洞
- 语言风格漂移
所以 RLHF 里通常会加一个约束:
不要离参考模型偏太远。
常见表达会写成:
有效奖励 = 奖励模型分数 - beta * KL(当前策略, 参考策略)
这里的 KL 惩罚,本质上是在说:
- 可以变好
- 但别一下子变得面目全非
这也是 RLHF 又强又贵的原因
因为它往往要同时维护:
- 策略模型
- 参考模型
- 奖励模型
- 强化学习训练过程
这比普通 SFT 明显更重。
一个很适合初学者先记的判断
RLHF 最容易让人误会成:
- 只是“再训一轮”
但更准确的理解应该是:
- 先学一个评分老师
- 再让生成模型在这个老师的引导下更新
- 还要防止模型为了高分跑偏
这就是为什么它会比普通 SFT 重很多。
五、RLHF 什么时候值得做?
当你已经遇到“正确但不够好”的问题
例如模型已经能答对大方向, 但你更在意:
- 哪种回答更稳
- 哪种更礼貌
- 哪种更不容易越界
这时偏好优化会很有价值。
当你确实有高质量偏好数据
如果没有足够好的偏好对数据, 奖励模型很容易学偏。
所以 RLHF 的关键门槛常常不在算法, 而在数据:
- 标注是否一致
- 维度是否清楚
- chosen/rejected 是否真有代表性
当你有资源承担训练复杂度
现实里很多团队最终不做 RLHF, 并不是因为它没用,而是因为:
- 工程链条长
- 成本高
- 调参难
因此很多场景会先尝试:
- DPO
- RLAIF
- 规则 + SFT
六、这些误区特别常见
误区一:RLHF 就是“加点人工反馈”
不够准确。 真正的 RLHF 是一条完整链:
- 采偏好
- 训奖励模型
- 再做策略优化
误区二:奖励模型分高就等于真实更好
奖励模型只是近似人类偏好的代理。 它本身也会有盲点和偏差。
误区三:RLHF 一定比 SFT 高级,所以该默认上
不一定。 如果你的问题主要是:
- 知识不够新
- 输出格式不稳
- 工具流程没接好
那 RLHF 很可能不是第一优先项。
如果把它做成讲义或项目笔记,最值得展示什么
最值得展示的通常不是:
- “我们做了 RLHF”
而是:
- 偏好数据长什么样
- 奖励模型在给什么打分
- 为什么还需要参考模型和 KL 惩罚
- 这条链为什么比 SFT 重很多
这样别人会更容易看出:
- 你理解的是 RLHF 的系统链路
- 不只是知道几个术语
小结
这一节最重要的不是记住 PPO 这个缩写, 而是看懂 RLHF 的主线:
先用偏好对训练一个“会打分的老师”,再用这个老师去指导生成模型朝更符合人类偏好的方向更新。
你只要把这条链真正想通, 后面学 DPO、RLAIF 或其他对齐方法时,就不会只剩方法名了。
练习
- 用自己的话解释:为什么很多场景下“偏好对比”比“绝对打分”更容易采集?
- 参考本节代码,再添加一组
chosen/rejected偏好样本,观察 learned weights 会怎么变。 - 为什么 RLHF 里通常要保留一个参考模型,并在优化时加 KL 惩罚?
- 想一想:你的项目目前更像“需要 SFT”还是“已经进入需要偏好优化”的阶段?为什么?