主题
RLHF 与大模型中的强化学习(PPO / DPO / GRPO)
基于人类反馈的强化学习(Reinforcement Learning from Human Feedback,RLHF)是让大语言模型的输出与人类偏好对齐的关键技术:先用人类偏好数据训练奖励模型,再用 PPO 等算法优化语言模型,并通过 KL 惩罚防止其偏离参考模型过远,InstructGPT(Ouyang et al., 2022)是代表性工作。DPO(Rafailov et al., 2023)把问题转化为直接在偏好数据上做分类式优化,无需显式奖励模型和在线采样。GRPO(Shao et al., 2024,DeepSeekMath)去掉了价值网络,用同一问题的一组采样结果的相对奖励来估计优势,配合可验证奖励(如数学题答案是否正确),在提升模型数学与推理能力方面被广泛采用(DeepSeek-R1 即基于 GRPO 训练)。
把语言生成看作强化学习
在 RLHF 中,强化学习的各个要素对应如下:
| RL 概念 | 语言模型中的对应 |
|---|---|
| 策略 | 语言模型本身 |
| 状态 | 提示词 |
| 动作 | 下一个 token |
| 回合 | 生成一个完整回复 |
| 奖励 | 通常只在回复结束时给出:奖励模型打分 |
| 转移 | 确定性:把新 token 拼接到序列后面 |
整个回复的对数概率是各 token 对数概率之和:
RLHF 流程
InstructGPT 的经典三阶段流程(Ouyang et al., 2022;更早的工作见 Christiano et al., 2017;Stiennon et al., 2020):
flowchart LR
A[预训练模型] --> B[1. 监督微调 SFT<br/>人类示范]
B --> C[2. 训练奖励模型<br/>人类偏好比较]
B --> D[3. PPO 微调<br/>最大化奖励 − KL]
C --> D
监督微调(SFT)
用人工编写的高质量“提示—回复”对,对预训练模型做监督微调。这一步本质上是 行为克隆,得到的模型
奖励模型训练
对同一个提示,让模型生成多个回复,请标注员比较哪个更好,得到偏好数据
奖励模型
PPO 微调与 KL 惩罚
用 PPO 优化如下目标:
KL 惩罚有两个作用:防止模型为了刷高奖励模型的分数而生成奇怪的文本(奖励欺骗,奖励模型只在 SFT 分布附近可靠);保持语言的流畅性和多样性。实现中常把 KL 作为逐 token 的惩罚并入奖励:
RLHF 中的 PPO 需要同时维护四个模型:策略(Actor)、价值网络(Critic,通常与策略同等规模)、奖励模型、参考模型。显存和工程复杂度都很高,这是 DPO 和 GRPO 出现的重要动机。
DPO:直接偏好优化
从 RLHF 目标推导 DPO
上面的 KL 约束目标有闭式最优解:
反过来,奖励可以用最优策略表示:
把它代入 Bradley-Terry 模型,难以计算的配分函数
直观上:提高被偏好回复相对于参考模型的概率,降低被拒绝回复的概率;
优缺点
- 优点:不需要训练奖励模型、不需要在线采样、不需要价值网络,像监督学习一样稳定、简单,只需策略和参考两个模型。
- 缺点:是离线方法,只能利用已有的偏好数据,无法像在线 RL 那样探索并从自己的新样本中学习;可能过拟合偏好数据;被拒绝回复的概率可能被压得过低,有时连被偏好回复的绝对概率也会下降。后续有 IPO、KTO、SimPO 等大量变体,以及在线/迭代式 DPO。
GRPO:组相对策略优化
组内相对优势
GRPO(Group Relative Policy Optimization)对每个问题
目标函数沿用 PPO 的裁剪形式,并把 KL 惩罚直接加在损失里(而不是并入奖励):
其中
去掉价值网络的好处
- 省显存:不需要与策略同等规模的 Critic;
- 更契合奖励结构:LLM 的奖励通常只在回复结束时给出一次,训练一个对每个 token 都准确的价值函数很困难;
- 组内比较天然契合奖励模型“比较式”训练的特点。
代价是每个问题需要采样多个回复(
基于可验证奖励的 RL(RLVR)
对数学、编程等有明确对错的任务,可以不用奖励模型,而是用规则直接判定:答案与标准答案一致得 1 分,否则 0 分;代码通过单元测试得分。这种可验证奖励几乎无法被“欺骗”,避免了奖励模型带来的奖励欺骗问题。DeepSeek-R1(DeepSeek-AI, 2025)展示了:在基础模型上直接用 GRPO + 规则奖励(正确性奖励 + 格式奖励)进行大规模强化学习(DeepSeek-R1-Zero),模型会自发地学会更长的思维链、自我验证和反思等推理行为。Tülu 3(Lambert et al., 2024)中也系统地使用了 RLVR。
玩具演示:GRPO 与 DPO 的损失函数
在真实的大模型上做 RLHF 需要大量 GPU,本站没有进行这样的训练。为了把公式落到代码上,我们构造了一个极小的玩具问题:20 个“问题”,每个问题有 8 个候选“回答”(可以理解为只生成 1 个 token、词表大小为 8),其中恰有 1 个正确;策略是一张
- GRPO:每个问题采样
个回答,可验证奖励为“回答正确得 1 分”,用组内标准化优势、PPO 裁剪和 k3 形式的 KL 惩罚更新; - DPO:偏好数据为(正确回答 ≻ 一个随机错误回答),直接最小化 DPO 损失。
py
"""大模型 RL 的玩具演示:用一个极小的“策略模型”演示 GRPO 与 DPO 的损失函数。
设定:共 20 个“问题”,每个问题有 8 个候选“回答”(相当于 8 个 token),其中恰有 1 个正确。
可验证奖励:回答正确得 1,否则得 0。"""
import torch
import torch.nn.functional as F
torch.manual_seed(0)
N_PROMPTS, N_ANS = 20, 8
correct = torch.randint(0, N_ANS, (N_PROMPTS,))
def new_policy():
# 每个问题一组 logits,相当于一个查表式的“语言模型”;初始化带少量随机性
return (0.1 * torch.randn(N_PROMPTS, N_ANS)).requires_grad_()
def accuracy(logits):
return F.softmax(logits, -1)[torch.arange(N_PROMPTS), correct].mean().item()
# ======================= GRPO =======================
G, CLIP, BETA, LR, STEPS = 8, 0.2, 0.04, 0.5, 60
logits = new_policy()
ref = logits.detach().clone() # 参考模型(冻结)
opt = torch.optim.Adam([logits], lr=LR)
print(f"[GRPO] 初始:正确回答的平均概率 {accuracy(logits):.3f}")
for step in range(STEPS):
with torch.no_grad():
old_logp_all = F.log_softmax(logits, -1)
samples = torch.multinomial(old_logp_all.exp(), G, replacement=True) # 每个问题采样 G 个回答
rewards = (samples == correct[:, None]).float()
# 组内相对优势:A_i = (r_i - mean(r)) / (std(r) + eps),不需要价值网络
adv = (rewards - rewards.mean(1, keepdim=True)) / (rewards.std(1, keepdim=True) + 1e-4)
old_logp = old_logp_all.gather(1, samples)
for _ in range(2): # 同一批样本上做少量优化步
logp_all = F.log_softmax(logits, -1)
logp = logp_all.gather(1, samples)
ratio = (logp - old_logp).exp()
pg = torch.min(ratio * adv, ratio.clamp(1 - CLIP, 1 + CLIP) * adv)
# KL(π || π_ref) 的无偏估计(k3):exp(ref - logp) - (ref - logp) - 1
ref_logp = F.log_softmax(ref, -1).gather(1, samples)
kl = (ref_logp - logp).exp() - (ref_logp - logp) - 1
loss = -(pg - BETA * kl).mean()
opt.zero_grad(); loss.backward(); opt.step()
if (step + 1) % 20 == 0:
print(f"[GRPO] step {step + 1:3d}:正确回答的平均概率 {accuracy(logits):.3f},本批平均奖励 {rewards.mean():.3f}")
# ======================= DPO =======================
BETA_DPO, LR, STEPS = 0.1, 0.5, 200
logits = new_policy()
ref = logits.detach().clone()
opt = torch.optim.Adam([logits], lr=LR)
# 偏好数据:对每个问题,(正确回答 y_w) ≻ (随机错误回答 y_l)
idx = torch.arange(N_PROMPTS)
wrong = (correct + torch.randint(1, N_ANS, (N_PROMPTS,))) % N_ANS
print(f"[DPO ] 初始:正确回答的平均概率 {accuracy(logits):.3f}")
for step in range(STEPS):
logp, ref_logp = F.log_softmax(logits, -1), F.log_softmax(ref, -1)
# 隐式奖励差:β[(log π(y_w) - log π_ref(y_w)) - (log π(y_l) - log π_ref(y_l))]
margin = BETA_DPO * ((logp[idx, correct] - ref_logp[idx, correct]) - (logp[idx, wrong] - ref_logp[idx, wrong]))
loss = -F.logsigmoid(margin).mean()
opt.zero_grad(); loss.backward(); opt.step()
if (step + 1) % 50 == 0:
p = F.softmax(logits, -1)
print(f"[DPO ] step {step + 1:3d}:loss {loss.item():.4f},正确回答平均概率 {accuracy(logits):.3f},"
f"被拒绝回答平均概率 {p[idx, wrong].mean():.3f}")1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
实际运行输出:
text
[GRPO] 初始:正确回答的平均概率 0.124
[GRPO] step 20:正确回答的平均概率 0.994,本批平均奖励 0.988
[GRPO] step 40:正确回答的平均概率 0.997,本批平均奖励 0.994
[GRPO] step 60:正确回答的平均概率 0.998,本批平均奖励 1.000
[DPO ] 初始:正确回答的平均概率 0.119
[DPO ] step 50:loss 0.0392,正确回答平均概率 1.000,被拒绝回答平均概率 0.000
[DPO ] step 100:loss 0.0160,正确回答平均概率 1.000,被拒绝回答平均概率 0.000
[DPO ] step 150:loss 0.0096,正确回答平均概率 1.000,被拒绝回答平均概率 0.000
[DPO ] step 200:loss 0.0065,正确回答平均概率 1.000,被拒绝回答平均概率 0.0001
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
两种方法都把正确回答的概率从约 1/8 提升到接近 1。需要强调:这只是损失函数的正确性演示,问题极其简单(查表式策略、没有泛化、没有长序列),不代表这些方法在真实大模型上的效果。从代码中可以看到两者的本质区别:GRPO 需要在线采样并用奖励打分;DPO 只用固定的偏好对,不需要采样也不需要奖励函数。
奖励欺骗与对齐风险
- 奖励欺骗(reward hacking):策略找到奖励模型的漏洞,例如生成更长的回复、讨好用户、重复某些“高分”措辞,而不是真正变好。Gao 等人(2023)系统研究了过度优化代理奖励时真实质量先升后降的规律。
- 对策:KL 约束、奖励模型集成、定期用新数据重新训练奖励模型、使用可验证奖励、人工抽查。
- 更广泛的对齐问题:人类偏好本身可能不一致或带有偏见;模型可能学会“看起来正确”而非“真的正确”。Constitutional AI / RLAIF(Bai et al., 2022)尝试用 AI 反馈替代部分人类反馈。
开源工具:TRL、OpenRLHF、veRL
| 工具 | 维护者 | 特点 |
|---|---|---|
| TRL | Hugging Face | SFT、奖励建模、PPO、DPO、GRPO 等,与 Transformers 生态集成,上手最容易 |
| OpenRLHF | 开源社区 | 基于 Ray + vLLM,面向大规模分布式 RLHF |
| veRL | 字节跳动火山引擎 | HybridFlow 架构,支持 PPO、GRPO 等,面向大规模 LLM 强化学习 |
实践建议与常见误区
- 先做好 SFT:RL 阶段很难弥补 SFT 阶段的不足。
- 监控 KL 和回复长度:KL 飙升或回复越来越长,通常意味着奖励欺骗。
- GRPO 的数据难度要适中:全对或全错的问题不提供梯度。
- 误区:DPO 完全等价于 RLHF:两者的最优解在理想条件下相同,但 DPO 是离线的,实际表现取决于偏好数据的覆盖。
- 误区:奖励越高越好:奖励模型只是人类偏好的不完美代理。
小结
- RLHF = SFT + 奖励模型(Bradley-Terry)+ 带 KL 约束的 PPO。
- DPO 利用 KL 约束问题的闭式解,把 RLHF 变成偏好数据上的分类损失,无需奖励模型和在线采样。
- GRPO 用组内相对奖励代替价值网络,配合可验证奖励成为训练推理模型的主流方法。
- 奖励欺骗是 RLHF 的核心风险,需要 KL 约束、可验证奖励等手段控制。
练习题
- 从 KL 约束目标出发,推导最优策略
。 - 证明 DPO 损失中配分函数
会相互抵消。 - 在玩具代码中把 GRPO 的组大小
改为 2,学习速度如何变化?为什么? - 在玩具代码中把 GRPO 的 KL 系数
调大到 1.0,最终正确回答的概率会如何变化? - 证明 k3 估计
在 下是 的无偏估计,且总是非负。
参考文献
- Christiano, P. et al. (2017). Deep Reinforcement Learning from Human Preferences. NeurIPS. arXiv:1706.03741
- Stiennon, N. et al. (2020). Learning to summarize from human feedback. NeurIPS. arXiv:2009.01325
- Ouyang, L. et al. (2022). Training language models to follow instructions with human feedback. NeurIPS. arXiv:2203.02155
- Bai, Y. et al. (2022). Constitutional AI: Harmlessness from AI Feedback. arXiv:2212.08073
- Rafailov, R. et al. (2023). Direct Preference Optimization: Your Language Model is Secretly a Reward Model. NeurIPS. arXiv:2305.18290
- Gao, L., Schulman, J., & Hilton, J. (2023). Scaling Laws for Reward Model Overoptimization. ICML. arXiv:2210.10760
- Shao, Z. et al. (2024). DeepSeekMath: Pushing the Limits of Mathematical Reasoning in Open Language Models. arXiv:2402.03300
- Lambert, N. et al. (2024). Tülu 3: Pushing Frontiers in Open Language Model Post-Training. arXiv:2411.15124
- DeepSeek-AI (2025). DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning. arXiv:2501.12948