← 返回笔记列表
📝 学习笔记 · LLM 基础

大模型完整训练流水线:预训练 → SFT → RLHF 深度解析

一篇讲清楚「大模型是怎么从随机初始化变成会聊天的 AI」的完整教程。重点深挖 SFT 和 RLHF 的本质、细节与局限。

核心问题
SFT 和 RLHF 到底在做什么、为什么这么做
覆盖阶段
预训练 → SFT → 奖励模型 → RLHF → DPO/GRPO
参考模型
InstructGPT · LLaMA · DeepSeek-R1 · Claude
适合读者
想真正理解大模型训练流程的工程师
🗺️
§0 四阶段全景图

大模型从随机初始化到能用的 AI 助手,要经历四个阶段。每个阶段解决不同的问题,缺一不可。

预训练
📚
在万亿 token 上学语言规律
产物:Base Model
SFT 监督微调
🎓
用标注示范教对话格式
产物:SFT Model
RLHF 偏好对齐
🏆
用人类偏好数据做强化学习
产物:Aligned Model
持续迭代
🔄
上线后持续修复和提升
产物:v2/v3/v4…
🔑 一句话理解每个阶段在解决什么
阶段解决的核心问题类比
① 预训练让模型有能力——理解和生成语言让人读完几千本书,有了语言理解和知识储备
② SFT让模型听话——按指令格式回答,而不是随机续写让读了很多书的人学会「被问→给出有用回答」的对话格式
③ RLHF让模型讨人喜欢——回答不只是格式对,还要质量高、无害从「及格水平」提升到「真正优秀」,通过大量反馈不断打磨
④ 迭代让模型持续进步——修复上线后发现的问题产品上市后根据用户反馈持续改进
⚠️ 为什么不能跳步骤?
• 没有预训练直接 SFT:模型没有语言能力,学不了指令格式
• 没有 SFT 直接 RLHF:Base Model 输出太随机,奖励模型无法区分好坏,RL 无法收敛
• 只有预训练 + SFT 没有 RLHF:模型会「及格线」回答,但无法达到「优秀」,且可能输出有害内容
三个阶段各自必要,且顺序不可颠倒。
📚
§1 预训练:获得「语言能力」

1.1 预训练在优化什么——「下一个词预测」

预训练的训练目标极其简单:给定前面的文字,预测下一个词(token)。这叫自回归语言模型(Autoregressive Language Model),对应损失函数是:

$$\mathcal{L}_{PT} = -\sum_{t=1}^{T} \log P_\theta(x_t \mid x_1, x_2, \ldots, x_{t-1})$$
符号解释
  • $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 如何回答「请帮我写一封请假邮件」

你输入:「请帮我写一封请假邮件」

Base Model 可能输出(续写模式):

「请帮我写一封请假邮件。
——(续写)以下是几个常见请假邮件模板:
1. 病假邮件模板:
尊敬的 [上级姓名],我因身体不适……
2. 事假邮件模板:……」

注意:Base Model 把你的"指令"当成了文章的开头,继续续写,给了你一个"模板列表"而不是一封具体的邮件。它不理解你要的是什么。

SFT 后的 Chat Model 输出:

「当然!这是一封请假邮件:

主题:请假申请
尊敬的 [上级姓名],
您好!我因 [原因] 需要请假 [天数],时间为 [具体日期]。在此期间,我已安排 [同事姓名] 代为处理紧急事务……」

1.3 预训练的局限——为什么还需要 SFT

核心问题:预训练目标(续写文本)≠ 我们的目标(有帮助地回答问题)

互联网上的文本包含大量:错误信息、有害内容、不尊重的语言、广告垃圾……Base Model 学了这些也会生成这些。

更重要的是:即使 Base Model 有能力回答问题,它不知道自己「应该」以对话形式、有用地回答——它只知道"续写"。SFT 就是把「续写」能力引导到「有用的对话」上。
🎓
§2 SFT 监督微调:完整训练流程深度解析

2.1 SFT 到底在做什么——从数据到参数更新的完整过程

SFT(Supervised Fine-Tuning)本质上是一个普通的监督学习,但有几个关键细节决定了它的效果。我们从头到尾完整走一遍:

🗺️ SFT 完整流程一览
① 准备数据
(prompt, response) 对
② 应用模板
ChatML 格式化
③ 设 Loss Mask
只对回答计算 loss
④ 前向传播
计算 cross-entropy
⑤ 反向传播
更新参数(全量或 LoRA)

2.2 Step 1&2:数据格式——ChatML 模板详解

SFT 的原始数据是一批 (prompt, response) 对,比如:

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 ✓
为什么 prompt 部分 loss_mask = 0?
因为 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 的交叉熵:

$$\mathcal{L}_{SFT} = -\frac{1}{T} \sum_{t=1}^{T} \log P_\theta\!\left(y_t \;\middle|\; \underbrace{x}_{\text{prompt}} ,\, y_1, \ldots, y_{t-1}\right)$$
逐项理解
  • $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 的贡献
💡 一条数据的 loss 计算过程(简化版)

假设 response 是「好的」(被 tokenizer 分成 3 个 token:「好」「的」「<|im_end|>」):

t=1:模型看到 prompt,预测下一个词 → P(「好」) = 0.72 → loss₁ = -log(0.72) = 0.329
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$ 可以用两个低秩矩阵近似:

$$\Delta W = B \cdot A, \quad B \in \mathbb{R}^{d \times r},\; A \in \mathbb{R}^{r \times k},\; r \ll \min(d, k)$$

只训练 $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 就能跑。

LoRA 挂载在哪些层?
通常挂在 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

SFT 存在的两大根本问题:
  1. 只会「模仿」,不理解「好坏」:损失函数告诉模型「你要输出 token A」,但从不告诉模型「为什么 A 比 B 好」。模型没有形成对质量的判断能力,只是在记忆和泛化标注员的写作风格。
    后果:遇到没见过的 prompt,模型不知道什么样的回答才算好,只能「随机漫步」。
  2. 标注示例质量有上限:人类标注员无法对每一种可能的 prompt 都写出「最优」回答,只能写出「还不错」的示例。SFT 把模型训练到「接近标注员水平」,但无法超越。

RLHF 的解法:不再问「模型应该输出什么」,而是问「模型的输出够不够好」——通过大量尝试 + 奖励模型评分 + 强化学习更新,让模型自己摸索出超越标注员示例的策略。

⚖️
§3 奖励模型:训练「自动评判者」

3.1 为什么需要奖励模型——人类实时打分太贵

有了 SFT 模型后,我们知道要用强化学习让模型继续提升。RL 需要一个「奖励信号」——模型生成了一段回答,需要有人或系统告诉它这个回答好不好、好多少分。

最理想的做法是:模型每次生成回答,立刻让人类给个分数。但这有根本性的问题:

为什么不能实时让人类打分?
  • 一次 PPO 训练可能要生成 几百万条回答,人类根本评不过来
  • 人类标注有延迟(至少几小时),RL 需要实时反馈
  • 成本极高:请人打分的费用会是训练费用的 100 倍以上

解决方案:不让人类实时打分,而是先让人类标注一批偏好数据,用这些数据训练一个模型来代替人类打分——这就是奖励模型(Reward Model)。

3.2 奖励模型如何训练——从「排序」中学打分

关键洞察:让人类说「哪个回答更好」比让人类说「这个回答打几分」要容易得多、一致性更高。

1
收集偏好数据:对同一个 prompt,让 SFT 模型生成 4~9 个不同的回答(采样时加入随机性),让人类标注员排序:「第 2 个 > 第 1 个 > 第 4 个 > 第 3 个」
2
转化成成对比较:从排序中提取出所有的两两对比:(第2个, 第1个)、(第2个, 第4个)、(第2个, 第3个)、(第1个, 第4个)… 每对都是一个「好回答 $y_w$ vs 差回答 $y_l$」的训练样本。
3
用 Bradley-Terry 模型训练:损失函数让奖励模型给好回答的打分高于差回答:
$$\mathcal{L}_{RM} = -\mathbb{E}_{(x, y_w, y_l)} \left[ \log \sigma\!\left( r_\theta(x, y_w) - r_\theta(x, y_l) \right) \right]$$

其中 $r_\theta(x, y)$ 是奖励模型对 (prompt $x$, 回答 $y$) 的打分,$\sigma$ 是 sigmoid 函数。这个损失让 $r(x, y_w) > r(x, y_l)$,即好回答的分数更高。

🔬 Bradley-Terry 模型详解——为什么这个损失函数有效?

① 原始 Bradley-Terry 假设

Bradley-Terry(1952)是一个用于成对比较(Pairwise Comparison)的概率模型。它假设每个「选手」有一个潜在能力值(strength)$s_i$,两者之间的胜负概率为:

$$P(i \text{ 胜过 } j) = \frac{s_i}{s_i + s_j}$$

对应到奖励模型,我们把打分 $r_\theta(x, y)$ 当作「能力值」,令 $s = e^{r}$(保证正数),则:

$$P(y_w \succ y_l \mid x) = \frac{e^{r_\theta(x,\,y_w)}}{e^{r_\theta(x,\,y_w)} + e^{r_\theta(x,\,y_l)}} = \sigma\!\left(r_\theta(x, y_w) - r_\theta(x, y_l)\right)$$

最后一步用了 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。

② 从最大似然到损失函数

我们希望奖励模型「尽量正确地复现人类的偏好判断」,即最大化人类标注数据上的对数似然:

$$\max_\theta \;\mathbb{E}_{(x,\,y_w,\,y_l)}\!\left[\log P_\theta(y_w \succ y_l \mid x)\right] = \max_\theta \;\mathbb{E}\!\left[\log \sigma\!\left(r_\theta(x,y_w) - r_\theta(x,y_l)\right)\right]$$

取负号变成最小化损失,就得到我们看到的 $\mathcal{L}_{RM}$:

$$\mathcal{L}_{RM} = -\mathbb{E}\!\left[\log \sigma\!\left(r_\theta(x,y_w) - r_\theta(x,y_l)\right)\right]$$

本质:这和二分类的交叉熵完全等价——把「$y_w$ 比 $y_l$ 好」当作正例(label=1),用 sigmoid 输出预测概率,做 Binary Cross-Entropy。唯一特别之处是输入不是单个样本的 logit,而是两个得分的差值

③ 梯度方向——参数怎么更新

对 $r_\theta(x, y_w)$ 求偏导(令 $\Delta = r_w - r_l$):

$$\frac{\partial \mathcal{L}_{RM}}{\partial r_w} = -\frac{\partial}{\partial r_w}\log\sigma(\Delta) = -(1 - \sigma(\Delta)) = \sigma(\Delta) - 1 \leq 0$$

梯度为负 → 梯度下降时 $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 好):

$$\sigma(r_B - r_D) = \sigma(2.1 - 1.3) = \sigma(0.8) = \frac{1}{1+e^{-0.8}} \approx 0.69$$

loss = $-\log(0.69) \approx 0.37$,梯度幅度 = $1 - 0.69 = 0.31$,模型需要进一步拉大 B 和 D 的差距。

对训练对 (B, C)(B 应该比 C 好):

$$\sigma(r_B - r_C) = \sigma(2.1 - (-0.5)) = \sigma(2.6) \approx 0.93$$

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 RLHF-PPO:按评判者标准「进化」

4.1 为什么需要 RL——SFT 无法做的事

有了奖励模型后,我们有了一个「自动打分机」。现在核心问题是:如何用这个分数来提升语言模型?

🏋️ 类比:运动员训练

SFT 阶段:教练(标注员)给运动员展示几千个标准动作视频,让运动员模仿。运动员学会了基本姿势,能完成基本任务。

RLHF 阶段:运动员开始实战——每次完成一个动作后,专业裁判(奖励模型)打分。运动员的目标是最大化自己的得分,不断调整动作,超越标注员示例的水平。

关键区别:SFT 限于"见过的示例水平",RL 可以通过大量尝试-反馈-调整,找到超越任何单一示例的策略。

从数学角度,RLHF 的目标是:

$$\max_\theta \; \mathbb{E}_{x \sim D,\, y \sim \pi_\theta(\cdot|x)} \left[ r(x, y) \right] - \beta \cdot \text{KL}\!\left[\pi_\theta(\cdot|x) \| \pi_{\text{ref}}(\cdot|x)\right]$$
每一项的含义
  • $\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$)的完整步骤:

1
采样(Rollout):用当前策略 $\pi_\theta$ 生成回答 $y$。
同时记录生成过程中每个 token 的 log 概率 $\log \pi_\theta(y_t \mid x, y_{<t})$(后面计算 ratio 要用)。
2
计算奖励:把 $(x, y)$ 送入奖励模型,得到分数 $r(x, y)$。
同时计算 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 加奖励模型的分数)
3
计算优势函数(Advantage):用价值网络(Critic)估计每个时刻的期望回报 $V(s_t)$,计算优势 $A_t = \hat{r}_t - V(s_t)$。
$A_t > 0$:这个 token 的选择比预期要好,应该增大其概率。
$A_t < 0$:比预期差,应该减小其概率。
4
PPO 裁剪更新:用裁剪目标函数更新策略:
$$\mathcal{L}_{PPO} = -\mathbb{E}\!\left[\min\!\left(\rho_t A_t,\; \text{clip}(\rho_t, 1-\varepsilon, 1+\varepsilon) A_t\right)\right]$$

$\rho_t = \frac{\pi_\theta(y_t)}{\pi_{\theta_\text{old}}(y_t)}$ 是新旧策略的概率比,$\varepsilon=0.2$。核心:比率被限制在 $[0.8, 1.2]$,每次更新不超过 20%。

💡 PPO 一步更新的直觉例子

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 最大的工程挑战——需要同时维护和运行四个模型:

🎭
Actor(策略网络)
被训练的语言模型,生成回答
需要更新梯度
📋
Reference(参考模型)
SFT 模型的副本,计算 KL
冻结,不更新
🏆
Reward Model(奖励模型)
给生成的回答打分
冻结,不更新
📊
Critic(价值网络)
估计期望回报,计算 Advantage
需要更新梯度
四个模型同时运行的代价:每次 PPO 更新,GPU 显存需要同时放下 4 个模型(每个都是 7B~70B 参数)。如果是 70B 模型,单次 PPO 训练需要约 数百张 A100。这也是为什么 DPO 被广泛采用——它只需要 2 个模型(Actor + Reference),省掉了 RM 和 Critic。
💡 InstructGPT 的 RLHF 实验数据(OpenAI 论文)
  • SFT 数据:约 13,000 条 prompt-response 对,由标注员撰写
  • RM 训练数据:约 33,000 条 prompt,每条有 4–9 个候选回答,标注员排序
  • PPO 训练:约 31,000 条 prompt,反复采样和更新
  • 结果:经过 RLHF 的 1.3B 模型,人类评估胜率高于未经 RLHF 的 175B GPT-3(大了 100 多倍的模型)
  • 结论:对齐质量比参数量更重要
🎯
§5 DPO:更简单的对齐方法(绕过 RL)

DPO(Direct Preference Optimization,直接偏好优化,Stanford 2023)的出发点很简单:能不能不用 RL,直接从偏好数据训练语言模型?

DPO 的核心数学洞察

数学上可以证明:在 KL 约束下的最优策略和奖励函数之间存在一个直接的解析关系:

$$r(x, y) = \beta \log \frac{\pi^*(y|x)}{\pi_\text{ref}(y|x)} + Z(x)$$

这意味着奖励函数可以直接用策略的对数概率比来表示。把这个关系代入偏好模型,化简后得到 DPO 损失:

$$\mathcal{L}_{DPO} = -\mathbb{E}\!\left[\log \sigma\!\left(\beta \log\frac{\pi_\theta(y_w|x)}{\pi_\text{ref}(y_w|x)} - \beta \log\frac{\pi_\theta(y_l|x)}{\pi_\text{ref}(y_l|x)}\right)\right]$$

直觉:让好回答 $y_w$ 相对于参考模型的概率提升,比坏回答 $y_l$ 的提升更大。

DPO vs RLHF-PPO 对比

维度RLHF-PPODPO
所需模型数量4 个(Actor / Reference / RM / Critic)2 个(Actor + Reference)
是否需要在线采样✅ 每次都要采样新回答❌ 用离线偏好数据集
训练复杂度极高(需要调 PPO 超参)类似 SFT(简单)
计算成本极高(4 个大模型同时占显存)低(约为 RLHF 的 1/4)
效果上限更高(能持续在线改进)依赖数据集质量,上限稍低
适用场景有大量实时标注资源、追求极致质量资源有限、有现成偏好数据集
典型应用GPT-4、Claude(早期)LLaMA-2-Chat、Zephyr、Mistral
开源社区为什么都用 DPO?
对于没有大量 GPU 资源和实时标注团队的开源项目,DPO 只需要:① 一个现成的 SFT 模型,② 一个公开的偏好数据集(如 Anthropic HH、OpenHermes),③ 相当于 SFT 的计算量。
这让个人或小团队也能做 RLHF 对齐,大大降低了门槛。
🧠
§6 GRPO:DeepSeek-R1 的秘密武器

GRPO(Group Relative Policy Optimization,组相对策略优化)是 DeepSeek 团队提出的算法,是 DeepSeek-R1 在数学推理上超越 GPT-4o 的核心技术。

GRPO 解决了 PPO 的什么问题

PPO 需要 Critic(价值网络)来估计每一步的期望回报。Critic 和 Actor 一样大(也是一个 7B/70B 模型),成本极高,而且估计误差会导致训练不稳定。

GRPO 的观察:对于数学题、代码等有客观答案的任务,可以不用 Critic!让模型对同一道题生成多个答案,用组内的相对好坏来计算优势:

$$\hat{A}_i = \frac{r_i - \text{mean}(r_1,\ldots,r_G)}{\text{std}(r_1,\ldots,r_G)}$$
📝 类比:学生刷题

PPO 方式(需要 Critic):老师对学生的每一步解题过程都给详细打分(需要老师投入大量精力)。

GRPO 方式(组内对比):学生对同一道题写 8 种解法,只告诉他哪几种答案对、哪几种错。学生通过对比找出规律——无需老师逐步点评,靠自己大量练习归纳。

为什么数学/代码特别适合:答案有客观对错(不需要主观判断),可以自动验证(答案是否正确),多解法自然存在(同一道题有多种做法)。

💡 DeepSeek-R1 的训练效果
  • AIME 2024 准确率:GRPO 训练前 15.6% → 训练后 79.8%
  • 最惊人的发现:模型自发涌现出了「思考-验证-回溯」的 CoT 推理行为,没有人教它这么做,是 RL 探索的结果
  • 训练后模型会在回答前写大量的「等等,让我重新想想」「这里有个错误……」——完全是自发行为
⚙️
§7 实际工程细节——落地时踩的坑

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,降低学习率
实用 Tips:如何判断 RLHF 训练是否健康?
  • RM 分数缓慢稳定上升:健康。如果上升太快说明可能 reward hacking
  • KL 散度在合理范围内:通常 < 5 是安全的,超过 10 要警惕
  • 生成多样性保持:不同 prompt 应该得到不同风格的回答
  • Benchmark 分数同步提升:RM 分数提升但 benchmark 不动甚至下降,说明 reward hacking
🗺️
§8 总结:为什么是这套流程

四阶段的必要性——每一步都不可少

阶段本质解决的问题为什么这一步不能省
预训练 自监督学习(无需标注) 获得语言理解和生成能力,积累世界知识 没有这步,模型什么都不会,后续步骤无从谈起
SFT 监督学习(需要标注) 让模型学会「对话格式」,从「续写」转向「回答」 没有 SFT,RL 无法收敛——奖励模型无法区分毫无格式的随机续写的好坏
RLHF 强化学习(需要偏好数据) 让模型超越示例水平,学会「什么是好回答」而非「模仿某个示例」 没有 RLHF,模型只能达到标注员水平,无法继续提升,也无法学会更复杂的偏好判断
持续迭代 在线学习(用户数据) 修复上线后发现的长尾问题,扩展能力边界 没有这步,模型会随着用户需求变化而逐渐落伍

RLHF 和 DPO/GRPO 的关系一图总结

🔑 「偏好对齐」的三条路径
路径 1:RLHF + PPO → 学习信号:人类偏好排序 → 优化方式:在线 RL(需要 RM + Critic)→ 适合:大公司有资源、追求上限
路径 2:DPO / IPO → 学习信号:人类偏好对 → 优化方式:离线监督学习(无需 RM)→ 适合:资源有限、有偏好数据集
路径 3:GRPO → 学习信号:客观规则验证(对/错)→ 优化方式:组内对比 RL(无需 Critic)→ 适合:数学/代码等可验证任务

三条路径都是「用人类(或规则)的偏好信号来改进模型」,只是在信号获取方式优化算法上做了不同的权衡。

大模型训练流水线的每一步,本质上都在回答同一个问题:「如何给模型一个学习信号,让它朝着我们想要的方向移动,同时不破坏它已有的能力?」

预训练用「预测下一个词」作为信号;SFT 用「标注员的示例」;RLHF 用「人类对好坏的判断」;GRPO 用「答案是否正确的规则」。信号的来源越精准、越可扩展,模型就能进化得越快、越好。

学习路线建议

📚 推荐阅读顺序

  1. InstructGPT 论文(2022):RLHF 的原始论文
  2. Constitutional AI(2022):RLAIF 方向
  3. DPO 论文(2023,Rafailov et al.)
  4. DeepSeek-Math(2024):GRPO 原始论文
  5. DeepSeek-R1(2025):GRPO 工业应用

🔧 动手实践

  • 用 LLaMA-Factory 跑一个 7B 模型的 SFT(几行配置搞定)
  • 用 TRL 库的 DPOTrainer 在 Anthropic HH 数据集上做偏好对齐
  • 跑 GRPO 在 GSM8K 上做数学对齐(TRL 有完整示例)

🔗 其他学习笔记