大模型从随机初始化到能用的 AI 助手,要经历四个阶段。每个阶段解决不同的问题,缺一不可。
产物:Base Model
产物:SFT Model
产物:Aligned Model
产物:v2/v3/v4…
| 阶段 | 解决的核心问题 | 类比 |
|---|---|---|
| ① 预训练 | 让模型有能力——理解和生成语言 | 让人读完几千本书,有了语言理解和知识储备 |
| ② SFT | 让模型听话——按指令格式回答,而不是随机续写 | 让读了很多书的人学会「被问→给出有用回答」的对话格式 |
| ③ RLHF | 让模型讨人喜欢——回答不只是格式对,还要质量高、无害 | 从「及格水平」提升到「真正优秀」,通过大量反馈不断打磨 |
| ④ 迭代 | 让模型持续进步——修复上线后发现的问题 | 产品上市后根据用户反馈持续改进 |
• 没有预训练直接 SFT:模型没有语言能力,学不了指令格式
• 没有 SFT 直接 RLHF:Base Model 输出太随机,奖励模型无法区分好坏,RL 无法收敛
• 只有预训练 + SFT 没有 RLHF:模型会「及格线」回答,但无法达到「优秀」,且可能输出有害内容
三个阶段各自必要,且顺序不可颠倒。
1.1 预训练在优化什么——「下一个词预测」
预训练的训练目标极其简单:给定前面的文字,预测下一个词(token)。这叫自回归语言模型(Autoregressive Language Model),对应损失函数是:
- $x_1, x_2, \ldots, x_T$:一段文本的 token 序列(如「今天天气很好」被分成 6 个 token)
- $P_\theta(x_t \mid \text{前文})$:模型对第 $t$ 个 token 的预测概率
- $\log P$:对数概率,概率越接近 1(预测越准),这一项越接近 0
- 整个损失:让模型尽量准确预测每个位置的下一个词
预训练就是让模型做无数遍"完形填空"——「今天天气___」→ 猜「很好」。数据量:互联网上所有文字,大约 10 万亿~100 万亿个词。
做了足够多的完形填空后,模型被迫学会:语法规则、事实知识(巴黎是法国首都)、逻辑推理(如果…那么…)、各种写作风格……因为这些知识都有助于更准确地预测下一个词。
1.2 预训练得到了什么——Base Model
预训练结束后,得到的叫 Base Model(基础模型)。它的特点:
✅ 会做的事
- 续写文本(给半截文章,补完后半段)
- 回答知识性问题(如果问题在训练数据的 pattern 里)
- 代码补全(训练数据包含大量代码)
- 翻译、摘要(但没有"指令跟随"格式)
❌ 不会做的事
- 按指令格式回答问题
- 拒绝有害请求
- 多轮对话(不知道自己是 AI)
- 一致性回答(同一个问题可能给出完全不同的答案)
你输入:「请帮我写一封请假邮件」
Base Model 可能输出(续写模式):
「请帮我写一封请假邮件。
——(续写)以下是几个常见请假邮件模板:
1. 病假邮件模板:
尊敬的 [上级姓名],我因身体不适……
2. 事假邮件模板:……」
注意:Base Model 把你的"指令"当成了文章的开头,继续续写,给了你一个"模板列表"而不是一封具体的邮件。它不理解你要的是什么。
SFT 后的 Chat Model 输出:
「当然!这是一封请假邮件:
主题:请假申请
尊敬的 [上级姓名],
您好!我因 [原因] 需要请假 [天数],时间为 [具体日期]。在此期间,我已安排 [同事姓名] 代为处理紧急事务……」
1.3 预训练的局限——为什么还需要 SFT
互联网上的文本包含大量:错误信息、有害内容、不尊重的语言、广告垃圾……Base Model 学了这些也会生成这些。
更重要的是:即使 Base Model 有能力回答问题,它不知道自己「应该」以对话形式、有用地回答——它只知道"续写"。SFT 就是把「续写」能力引导到「有用的对话」上。
2.1 SFT 到底在做什么——从数据到参数更新的完整过程
SFT(Supervised Fine-Tuning)本质上是一个普通的监督学习,但有几个关键细节决定了它的效果。我们从头到尾完整走一遍:
(prompt, response) 对
ChatML 格式化
只对回答计算 loss
计算 cross-entropy
更新参数(全量或 LoRA)
2.2 Step 1&2:数据格式——ChatML 模板详解
SFT 的原始数据是一批 (prompt, response) 对,比如:
喂给模型之前,必须先套上对话模板(Chat Template),把数据整理成模型能理解的格式。以 ChatML 格式(OpenAI、Qwen 等使用)为例:
# ───────────────────────────────────────────────────────────── # 送入模型的完整 token 序列(已经过 tokenizer 编码) # 每一行对应一段原始文本,右边注释说明 loss_mask 状态 # ───────────────────────────────────────────────────────────── <|im_start|>system\n 你是一个有帮助的 AI 助手。<|im_end|>\n ← loss_mask = 0,不计算 loss <|im_start|>user\n 请用简单语言解释什么是递归<|im_end|>\n ← loss_mask = 0,不计算 loss <|im_start|>assistant\n 递归就像一面镜子对着另一面镜子,\n ← loss_mask = 1,计算 loss ✓ 你能看到无限层叠的反射。...<|im_end|>\n ← loss_mask = 1,计算 loss ✓
因为 SFT 的目标是教模型「如何回答」,不是教它「如何提问」。如果对 prompt 也计算 loss,模型会浪费容量去记忆 prompt 的写法,反而影响对 response 的学习质量。
具体实现:在构造训练 batch 时,把 prompt token 对应的 label 全部设为
-100(PyTorch CrossEntropyLoss 的 ignore_index),这样这些位置不会产生梯度。
2.3 Step 3&4:Loss 函数——只计算 response 的 Cross-Entropy
设 response 部分共有 $T$ 个 token,SFT 的 loss 就是对这 $T$ 个位置做 next-token prediction 的交叉熵:
- $x$:prompt(system + user 部分),只提供上下文,不产生 loss
- $y_t$:response 中第 $t$ 个 token 的真实标签
- $P_\theta(y_t \mid \cdot)$:模型预测第 $t$ 个位置是 $y_t$ 的概率
- 整体目标:让模型在看到 prompt 和前面已生成 token 的条件下,尽量准确预测 response 的每一个后续 token
- 和预训练 loss 唯一的区别:加了 loss mask,排除了 prompt 的贡献
假设 response 是「好的」(被 tokenizer 分成 3 个 token:「好」「的」「<|im_end|>」):
t=2:模型看到 prompt + 「好」,预测 → P(「的」) = 0.88 → loss₂ = -log(0.88) = 0.128
t=3:模型看到前两步,预测结束符 → P(「<im_end>」) = 0.91 → loss₃ = -log(0.91) = 0.094
total loss = (0.329 + 0.128 + 0.094) / 3 = 0.184
反向传播:梯度只来自这 3 个位置,prompt 部分对梯度无贡献。
2.4 Step 5:参数更新——全量微调 vs LoRA
Base Model 通常有 7B~405B 参数。SFT 时更新参数有两种策略:
全量微调(Full Fine-tuning)
更新模型的所有参数。一个 7B 模型用 bf16 存储需要 14GB,加上梯度和优化器状态(Adam 需要 3x 参数量),训练时需要约 7×4 = 56GB 显存。
⚠️ 对于 70B 以上的模型,单机根本跑不了,需要模型并行(ZeRO-3)。
适用场景:你有充足 GPU,想要最好的效果。
LoRA(参数高效微调)
LoRA 的核心思想:大模型的参数更新矩阵 $\Delta W$ 可以用两个低秩矩阵近似:
只训练 $A$ 和 $B$,冻结原始权重 $W_0$。前向传播时:$h = W_0 x + \Delta W x = W_0 x + BAx$
如果 $r=16$,$d=k=4096$(LLaMA-7B 的 attention 维度),则 $\Delta W$ 有 $4096^2 \approx 16M$ 参数,但 $A+B$ 只有 $2 \times 4096 \times 16 = 131K$ 参数,压缩约 120 倍。
✅ 7B 模型用 LoRA 只需 10~16GB 显存,一张 4090 就能跑。
通常挂在 Transformer 的 Self-Attention 层,具体是 Q、K、V 三个投影矩阵(有时也加 O 矩阵和 FFN 层)。Embedding 和 LayerNorm 一般不加 LoRA。
关键超参:rank(秩 $r$,常用 8/16/32/64)和 lora_alpha(缩放系数,通常 = rank 或 2×rank),以及 target_modules(指定挂哪些层)。
2.5 多轮对话的 SFT 处理
实际场景中 SFT 数据往往是多轮对话。多轮对话的 loss mask 有两种策略:
| 策略 | loss mask 设置 | 优缺点 |
|---|---|---|
| 只算最后一轮 | 只对最后一个 assistant 回合计算 loss,前面的 user/assistant 均 mask 掉 | 实现简单,但浪费了前几轮对话中 assistant 的监督信号 |
| 所有 assistant 轮都算(推荐) | 每个 assistant 回合的 token 都计算 loss,user/system 的 token 全部 mask | 充分利用标注数据,训练更高效。目前大多数开源方案默认这种 |
# 多轮对话的完整 token 序列示例(assistant 回合都计算 loss) <|im_start|>system\n你是助手。<|im_end|>\n ← mask <|im_start|>user\n你好<|im_end|>\n ← mask <|im_start|>assistant\n你好!有什么可以帮你?<|im_end|>\n ← loss ✓ <|im_start|>user\n解释递归<|im_end|>\n ← mask <|im_start|>assistant\n递归是...<|im_end|>\n ← loss ✓
2.6 SFT 数据质量——「垃圾进,垃圾出」的典型案例
SFT 的学习信号完全来自人类标注,标注质量决定了模型上限。
InstructGPT 的著名结论:13,000 条高质量数据训练的 1.3B 模型,人类偏好胜过没有 SFT 的 175B GPT-3。数据质量 >> 数据量 >> 模型大小。
🎯 格式一致性
所有数据必须使用同一套 Chat Template,system prompt 风格、回答结构、拒绝格式保持统一。格式混乱会让模型在两种格式之间反复摇摆,两边都学不好。
📏 回答质量决定天花板
模型只能学到标注员能写出来的水平。如果 50% 的标注是平庸的,模型就会学到平庸。要求标注员按「专家水平」而非「及格水平」来写示例。
🌐 任务覆盖多样性
数据需要覆盖:写作/代码/数学/知识问答/对话/分析/角色扮演/安全拒绝……如果某类任务数据很少,模型在这类任务上就会表现差(分布外问题)。
2.7 SFT 的根本局限——为什么还需要 RLHF
-
只会「模仿」,不理解「好坏」:损失函数告诉模型「你要输出 token A」,但从不告诉模型「为什么 A 比 B 好」。模型没有形成对质量的判断能力,只是在记忆和泛化标注员的写作风格。
后果:遇到没见过的 prompt,模型不知道什么样的回答才算好,只能「随机漫步」。 - 标注示例质量有上限:人类标注员无法对每一种可能的 prompt 都写出「最优」回答,只能写出「还不错」的示例。SFT 把模型训练到「接近标注员水平」,但无法超越。
RLHF 的解法:不再问「模型应该输出什么」,而是问「模型的输出够不够好」——通过大量尝试 + 奖励模型评分 + 强化学习更新,让模型自己摸索出超越标注员示例的策略。
3.1 为什么需要奖励模型——人类实时打分太贵
有了 SFT 模型后,我们知道要用强化学习让模型继续提升。RL 需要一个「奖励信号」——模型生成了一段回答,需要有人或系统告诉它这个回答好不好、好多少分。
最理想的做法是:模型每次生成回答,立刻让人类给个分数。但这有根本性的问题:
- 一次 PPO 训练可能要生成 几百万条回答,人类根本评不过来
- 人类标注有延迟(至少几小时),RL 需要实时反馈
- 成本极高:请人打分的费用会是训练费用的 100 倍以上
解决方案:不让人类实时打分,而是先让人类标注一批偏好数据,用这些数据训练一个模型来代替人类打分——这就是奖励模型(Reward Model)。
3.2 奖励模型如何训练——从「排序」中学打分
关键洞察:让人类说「哪个回答更好」比让人类说「这个回答打几分」要容易得多、一致性更高。
其中 $r_\theta(x, y)$ 是奖励模型对 (prompt $x$, 回答 $y$) 的打分,$\sigma$ 是 sigmoid 函数。这个损失让 $r(x, y_w) > r(x, y_l)$,即好回答的分数更高。
① 原始 Bradley-Terry 假设
Bradley-Terry(1952)是一个用于成对比较(Pairwise Comparison)的概率模型。它假设每个「选手」有一个潜在能力值(strength)$s_i$,两者之间的胜负概率为:
对应到奖励模型,我们把打分 $r_\theta(x, y)$ 当作「能力值」,令 $s = e^{r}$(保证正数),则:
最后一步用了 sigmoid 的等价变形:$\frac{e^a}{e^a+e^b} = \frac{1}{1+e^{b-a}} = \sigma(a-b)$。
直觉:$\sigma(r_w - r_l)$ 就是「奖励模型认为 $y_w$ 更好的概率」。当 $r_w \gg r_l$ 时这个概率接近 1;当两者相等时为 0.5;当 $r_w \ll r_l$ 时接近 0。
② 从最大似然到损失函数
我们希望奖励模型「尽量正确地复现人类的偏好判断」,即最大化人类标注数据上的对数似然:
取负号变成最小化损失,就得到我们看到的 $\mathcal{L}_{RM}$:
本质:这和二分类的交叉熵完全等价——把「$y_w$ 比 $y_l$ 好」当作正例(label=1),用 sigmoid 输出预测概率,做 Binary Cross-Entropy。唯一特别之处是输入不是单个样本的 logit,而是两个得分的差值。
③ 梯度方向——参数怎么更新
对 $r_\theta(x, y_w)$ 求偏导(令 $\Delta = r_w - r_l$):
梯度为负 → 梯度下降时 $r_w$ 增大(好回答分数涨)。
对 $r_l$ 求导同理得 $\sigma(\Delta) \geq 0$,即 $r_l$ 减小(差回答分数跌)。
| 当前情况 | $\sigma(\Delta)$ | 梯度幅度 | 更新力度 |
|---|---|---|---|
| $r_w \gg r_l$(模型已判断正确) | ≈ 1 | ≈ 0 | 几乎不更新(已经很确信) |
| $r_w \approx r_l$(模型举棋不定) | ≈ 0.5 | ≈ 0.5 | 较大更新(最需要学习的地方) |
| $r_w \ll r_l$(判断错了) | ≈ 0 | ≈ 1 | 最大更新(强力纠错) |
这个自适应的更新幅度是 BT 损失的优雅之处——越难的样本(模型判断不确定或判断错误)得到越大的学习信号,和人类考试时「错题重点复习」的直觉完全一致。
④ 具体数值示例
假设当前奖励模型对上文「天空为什么是蓝色」例子的打分是:$r(B)=2.1,\; r(D)=1.3,\; r(A)=0.8,\; r(C)=-0.5$
对训练对 (B, D)(B 应该比 D 好):
loss = $-\log(0.69) \approx 0.37$,梯度幅度 = $1 - 0.69 = 0.31$,模型需要进一步拉大 B 和 D 的差距。
对训练对 (B, C)(B 应该比 C 好):
loss = $-\log(0.93) \approx 0.07$,梯度幅度 = $1 - 0.93 = 0.07$,模型已经很确信 B > C,几乎不更新——把宝贵的学习机会留给更难区分的对。
Prompt:「解释为什么天空是蓝色的,用小学生能懂的语言」
SFT 模型生成的 4 个候选回答:
| 回答 | 内容 | 人类排名 |
|---|---|---|
| 回答 A | 「天空是蓝色的,因为大气散射了太阳光中蓝色波长的光,这种现象叫瑞利散射……(专业物理术语一堆)」 | 3(太专业,小学生看不懂) |
| 回答 B | 「想象太阳光是一包彩色糖豆,里面有红的、橙的、黄的、绿的、蓝的……当阳光进入大气层,蓝色糖豆(光)特别爱乱跑,到处弹来弹去,所以你看哪里天空都是蓝色的!」 | 1(最好!有类比,小学生能懂) |
| 回答 C | 「因为天空本来就是蓝色的。」 | 4(太简单,没有解释) |
| 回答 D | 「太阳光包含很多颜色。蓝色光比红色光更容易被空气中的小颗粒弹散,所以天空看起来是蓝色的。」 | 2(还不错,但没有类比) |
生成的训练对(从排名 B>D>A>C 提取):(B,D)、(B,A)、(B,C)、(D,A)、(D,C)、(A,C) 共 6 对
奖励模型训练目标:对每一对,让 $r(B) > r(D)$,$r(D) > r(A)$,$r(A) > r(C)$……最终奖励模型学会给「通俗易懂、有类比、能让小学生理解」的回答更高的分。
3.3 奖励模型的结构细节
🏗️ 模型结构
通常是 SFT 模型(或同规模模型)+ 线性打分头:
class RewardModel(nn.Module):
def __init__(self, lm_backbone):
self.lm = lm_backbone # SFT 模型
self.score_head = nn.Linear(
lm.hidden_size, 1) # 打分头
def forward(self, input_ids):
hidden = self.lm(input_ids)
# 取最后一个 token 的隐状态
last = hidden[:, -1, :]
return self.score_head(last) # 输出一个分数
⚠️ 奖励模型的关键风险
Reward Hacking(奖励函数被钻空子):
- 模型可能发现「长回答得分更高」,开始无意义地堆字数
- 可能发现「加很多鼓励语气的词」得分高,开始过度讨好
- 奖励模型本身是不完美的,训练足够多 RL 步后模型会发现其漏洞
这就是为什么需要 KL 惩罚(见 §4.3),防止模型因为 reward hacking 而退化。
4.1 为什么需要 RL——SFT 无法做的事
有了奖励模型后,我们有了一个「自动打分机」。现在核心问题是:如何用这个分数来提升语言模型?
SFT 阶段:教练(标注员)给运动员展示几千个标准动作视频,让运动员模仿。运动员学会了基本姿势,能完成基本任务。
RLHF 阶段:运动员开始实战——每次完成一个动作后,专业裁判(奖励模型)打分。运动员的目标是最大化自己的得分,不断调整动作,超越标注员示例的水平。
关键区别:SFT 限于"见过的示例水平",RL 可以通过大量尝试-反馈-调整,找到超越任何单一示例的策略。
从数学角度,RLHF 的目标是:
- $\pi_\theta$:当前策略(语言模型),参数为 $\theta$
- $r(x, y)$:奖励模型对 prompt $x$、回答 $y$ 的打分(越高越好)
- $\text{KL}[\pi_\theta \| \pi_\text{ref}]$:当前策略与参考模型(SFT 模型)的 KL 散度(衡量"偏离了多少")
- $\beta$:KL 惩罚系数,控制允许偏离多少
- 整体目标:最大化奖励,同时不要偏离 SFT 模型太远
4.2 PPO 完整训练流程——一次迭代发生了什么
PPO(Proximal Policy Optimization)是目前 RLHF 中最常用的 RL 算法,核心思想是:每次更新幅度不能太大,防止训练崩溃。
一次 PPO 迭代(对一个 prompt $x$)的完整步骤:
同时记录生成过程中每个 token 的 log 概率 $\log \pi_\theta(y_t \mid x, y_{<t})$(后面计算 ratio 要用)。
同时计算 KL 惩罚:$\text{KL}_t = \log \pi_\theta(y_t) - \log \pi_\text{ref}(y_t)$(逐 token 计算)。
最终每个 token 的奖励:$\hat{r}_t = r(x,y) \cdot \mathbf{1}[t=T] - \beta \cdot \text{KL}_t$(只在最后一个 token 加奖励模型的分数)
$A_t > 0$:这个 token 的选择比预期要好,应该增大其概率。
$A_t < 0$:比预期差,应该减小其概率。
$\rho_t = \frac{\pi_\theta(y_t)}{\pi_{\theta_\text{old}}(y_t)}$ 是新旧策略的概率比,$\varepsilon=0.2$。核心:比率被限制在 $[0.8, 1.2]$,每次更新不超过 20%。
Prompt:「给我推荐一本好书」
当前策略生成:「我推荐《百年孤独》,这是加西亚·马尔克斯的魔幻现实主义杰作,讲述了布恩迪亚家族七代人的故事……(高质量推荐,有理由)」
奖励模型打分:8.5 / 10(具体推荐,有书名有理由,很有帮助)
旧策略(SFT 模型)对同一生成的 log 概率:相对较低(因为 SFT 模型给这种详细推荐的概率不高)
优势 $A_t > 0$:这个回答比 Critic 预期的好,所以每个 token 的概率都应该增大
PPO 更新后:模型对这种「给出具体书名 + 详细理由」格式的回答概率增大了,但由于裁剪,最多增大到 $1.2$ 倍,不会一下子跳得太远。
4.3 KL 惩罚——系在 SFT 模型上的「安全绳」
KL 散度惩罚是 RLHF 中最重要的稳定性保障机制。
想象 SFT 模型是一个「原点」,用强化学习让模型向高分方向移动,但系了一根弹性绳——离原点越远,绳子拉力越大。
没有绳子(无 KL 惩罚):模型会发现奖励模型的漏洞,不断钻空子,最终输出奇怪的文本(Reward Hacking)。比如发现「在回答里加很多感叹号」可以得高分,然后所有回答都加满感叹号。
有了绳子:即使发现了漏洞,因为离 SFT 模型太远会受到惩罚,模型被迫在「钻空子」和「保持正常」之间取平衡,训练更稳定。
| $\beta$(KL系数) | 效果 | 风险 |
|---|---|---|
| 太大(如 0.5) | 模型几乎不偏离 SFT,训练稳定 | RL 改进空间太小,提升有限 |
| 太小(如 0.001) | RL 可以大幅改变模型 | 容易 reward hacking,训练不稳定 |
| 合适(如 0.02~0.1) | 在改进空间和稳定性之间取得平衡 | 需要反复调参 |
4.4 RLHF-PPO 需要四个模型同时运行
这是 PPO 最大的工程挑战——需要同时维护和运行四个模型:
需要更新梯度
冻结,不更新
冻结,不更新
需要更新梯度
- SFT 数据:约 13,000 条 prompt-response 对,由标注员撰写
- RM 训练数据:约 33,000 条 prompt,每条有 4–9 个候选回答,标注员排序
- PPO 训练:约 31,000 条 prompt,反复采样和更新
- 结果:经过 RLHF 的 1.3B 模型,人类评估胜率高于未经 RLHF 的 175B GPT-3(大了 100 多倍的模型)
- 结论:对齐质量比参数量更重要
DPO(Direct Preference Optimization,直接偏好优化,Stanford 2023)的出发点很简单:能不能不用 RL,直接从偏好数据训练语言模型?
DPO 的核心数学洞察
数学上可以证明:在 KL 约束下的最优策略和奖励函数之间存在一个直接的解析关系:
这意味着奖励函数可以直接用策略的对数概率比来表示。把这个关系代入偏好模型,化简后得到 DPO 损失:
直觉:让好回答 $y_w$ 相对于参考模型的概率提升,比坏回答 $y_l$ 的提升更大。
DPO vs RLHF-PPO 对比
| 维度 | RLHF-PPO | DPO |
|---|---|---|
| 所需模型数量 | 4 个(Actor / Reference / RM / Critic) | 2 个(Actor + Reference) |
| 是否需要在线采样 | ✅ 每次都要采样新回答 | ❌ 用离线偏好数据集 |
| 训练复杂度 | 极高(需要调 PPO 超参) | 类似 SFT(简单) |
| 计算成本 | 极高(4 个大模型同时占显存) | 低(约为 RLHF 的 1/4) |
| 效果 | 上限更高(能持续在线改进) | 依赖数据集质量,上限稍低 |
| 适用场景 | 有大量实时标注资源、追求极致质量 | 资源有限、有现成偏好数据集 |
| 典型应用 | GPT-4、Claude(早期) | LLaMA-2-Chat、Zephyr、Mistral |
对于没有大量 GPU 资源和实时标注团队的开源项目,DPO 只需要:① 一个现成的 SFT 模型,② 一个公开的偏好数据集(如 Anthropic HH、OpenHermes),③ 相当于 SFT 的计算量。
这让个人或小团队也能做 RLHF 对齐,大大降低了门槛。
GRPO(Group Relative Policy Optimization,组相对策略优化)是 DeepSeek 团队提出的算法,是 DeepSeek-R1 在数学推理上超越 GPT-4o 的核心技术。
GRPO 解决了 PPO 的什么问题
PPO 需要 Critic(价值网络)来估计每一步的期望回报。Critic 和 Actor 一样大(也是一个 7B/70B 模型),成本极高,而且估计误差会导致训练不稳定。
GRPO 的观察:对于数学题、代码等有客观答案的任务,可以不用 Critic!让模型对同一道题生成多个答案,用组内的相对好坏来计算优势:
PPO 方式(需要 Critic):老师对学生的每一步解题过程都给详细打分(需要老师投入大量精力)。
GRPO 方式(组内对比):学生对同一道题写 8 种解法,只告诉他哪几种答案对、哪几种错。学生通过对比找出规律——无需老师逐步点评,靠自己大量练习归纳。
为什么数学/代码特别适合:答案有客观对错(不需要主观判断),可以自动验证(答案是否正确),多解法自然存在(同一道题有多种做法)。
- AIME 2024 准确率:GRPO 训练前 15.6% → 训练后 79.8%
- 最惊人的发现:模型自发涌现出了「思考-验证-回溯」的 CoT 推理行为,没有人教它这么做,是 RL 探索的结果
- 训练后模型会在回答前写大量的「等等,让我重新想想」「这里有个错误……」——完全是自发行为
SFT 阶段的工程细节
📦 数据格式问题
- prompt 部分的 loss mask 要设为 0(不计入 loss),否则模型会同时学习"怎么提问"
- 多轮对话:只对最后一轮的回答计算 loss,或者对所有 assistant 轮都计算 loss(后者效果通常更好)
- packing(把多条短数据拼接成一条长数据)提升训练效率,但要注意 attention mask 防止跨样本的注意力
⚡ 训练稳定性
- 学习率:通常比预训练低 10–100x(如 1e-5 ~ 5e-5)
- Warmup:少量 warmup step 防止初始梯度爆炸
- Gradient clipping:clip_grad_norm=1.0 是标配
- LoRA rank:太小(r=4)效果差,太大(r=128)训练慢,通常 r=16 或 r=32
RLHF 阶段的工程细节
🎛️ 关键超参
- $\beta$(KL系数):0.02~0.1,需要根据任务调整
- $\varepsilon$(PPO 裁剪):通常 0.2
- mini-batch size:通常 8~64 个 prompt
- PPO epochs:每批数据更新几次(通常 1~4 次)
- 生成长度:限制最大生成 token 数,防止模型生成超长废话
🚨 常见训练崩溃原因
- Reward hacking:RM 分数一直涨但 KL 也快速上升,模型开始生成乱码
- Entropy collapse:模型变得非常确定,输出多样性极低(生成总是重复同样的句子)
- Value network不稳定:Critic 的估计误差太大,导致 Advantage 信号噪声大
- 处理方案:加大 $\beta$,加 entropy bonus,降低学习率
- RM 分数缓慢稳定上升:健康。如果上升太快说明可能 reward hacking
- KL 散度在合理范围内:通常 < 5 是安全的,超过 10 要警惕
- 生成多样性保持:不同 prompt 应该得到不同风格的回答
- Benchmark 分数同步提升:RM 分数提升但 benchmark 不动甚至下降,说明 reward hacking
四阶段的必要性——每一步都不可少
| 阶段 | 本质 | 解决的问题 | 为什么这一步不能省 |
|---|---|---|---|
| 预训练 | 自监督学习(无需标注) | 获得语言理解和生成能力,积累世界知识 | 没有这步,模型什么都不会,后续步骤无从谈起 |
| SFT | 监督学习(需要标注) | 让模型学会「对话格式」,从「续写」转向「回答」 | 没有 SFT,RL 无法收敛——奖励模型无法区分毫无格式的随机续写的好坏 |
| RLHF | 强化学习(需要偏好数据) | 让模型超越示例水平,学会「什么是好回答」而非「模仿某个示例」 | 没有 RLHF,模型只能达到标注员水平,无法继续提升,也无法学会更复杂的偏好判断 |
| 持续迭代 | 在线学习(用户数据) | 修复上线后发现的长尾问题,扩展能力边界 | 没有这步,模型会随着用户需求变化而逐渐落伍 |
RLHF 和 DPO/GRPO 的关系一图总结
三条路径都是「用人类(或规则)的偏好信号来改进模型」,只是在信号获取方式和优化算法上做了不同的权衡。
大模型训练流水线的每一步,本质上都在回答同一个问题:「如何给模型一个学习信号,让它朝着我们想要的方向移动,同时不破坏它已有的能力?」
预训练用「预测下一个词」作为信号;SFT 用「标注员的示例」;RLHF 用「人类对好坏的判断」;GRPO 用「答案是否正确的规则」。信号的来源越精准、越可扩展,模型就能进化得越快、越好。
学习路线建议
📚 推荐阅读顺序
- InstructGPT 论文(2022):RLHF 的原始论文
- Constitutional AI(2022):RLAIF 方向
- DPO 论文(2023,Rafailov et al.)
- DeepSeek-Math(2024):GRPO 原始论文
- DeepSeek-R1(2025):GRPO 工业应用
🔧 动手实践
- 用 LLaMA-Factory 跑一个 7B 模型的 SFT(几行配置搞定)
- 用 TRL 库的 DPOTrainer 在 Anthropic HH 数据集上做偏好对齐
- 跑 GRPO 在 GSM8K 上做数学对齐(TRL 有完整示例)
🔗 其他学习笔记
- LLM 中的 RL 方法全解析(PPO/DPO/GRPO 公式推导)
- 大厂大模型部门技术栈(工程实践视角)
- LLM Tokenizer 详解