02. 强化学习与后训练:从 RLHF 到 OAPL 的完整推导
02. 强化学习与后训练:从 RLHF 到 OAPL 的完整推导
上一章讲了 LLM 的架构和预训练。预训练后的模型像一个”博览群书的人”——知识丰富但不一定听话、安全、或擅长特定任务。后训练用强化学习把它变成一个”训练有素的专家”。
一、RL 的数学框架
1.1 基本定义
在 LLM 的 RL 中,我们有:
- 状态 (state):当前的输入 prompt x
- 动作 (action):模型生成的完整输出 y(一个 token 序列)
- 策略 (policy):模型本身,记作 ,即给定 x 生成 y 的概率
- 奖励 (reward):,环境对输出 y 的打分
RL 目标:
展开来说:从数据集 D 中取 prompt x,用当前策略 生成 y,希望 y 得到的奖励尽可能高。
1.2 为什么不直接用监督学习?
监督学习需要”标准答案”。但对于很多任务:
- 正确答案不唯一(“请推荐一本好书”)
- 答案质量是连续谱(不是对/错二分)
- 获取标准答案极其昂贵
RL 的优势:只需要一个打分函数(reward),不需要标准答案。模型自己探索,环境打分,好的行为被强化。
1.3 一个具体例子
x = "法国的首都是哪里?"
策略 π 生成 8 条 rollout:
y1 = "法国的首都是巴黎。" → r = 1.0 (完全正确)
y2 = "巴黎是法国首都,位于塞纳河畔。" → r = 1.0 (正确且详细)
y3 = "法国首都是里昂。" → r = 0.0 (错误)
y4 = "Paris." → r = 0.8 (正确但没用中文)
y5 = "法国是欧洲国家..." → r = 0.2 (没回答问题)
...
RL 的目标: 调整 π 的参数,让 y1, y2 这类高分输出的概率增加,y3, y5 这类低分输出的概率降低。
二、期望(Expectation)与蒙特卡洛估计
2.1 期望的定义
直觉:模型的”平均得分”——对所有可能的输出 y,按概率加权求和。
问题:输出空间是指数级的(每个位置 50257 种选择,输出 100 token → 种可能),无法精确计算。
2.2 蒙特卡洛估计
实际做法:从 中采样 G 个 rollout,用平均值近似期望:
G 就是 KARL 中每个 prompt 的 rollout 数(通常 8 条)。G 越大,估计越准,但计算成本越高。
三、KL 正则化
3.1 直接最大化奖励的问题
如果只最大化 :
问题: "2+3=?"
正常回答: "5" (reward = 1.0)
退化回答: "5555555555" (如果 reward 函数只检查是否包含 "5",也得 1.0)
模型可能学到各种”作弊”行为——形式上得到高分但实质退化。另一个风险是模式坍塌(mode collapse):模型只学会一种固定的回答模式。
3.2 KL 散度的定义
KL 散度衡量两个概率分布 P 和 Q 的”差异”:
具体计算例子:
P = [0.7, 0.2, 0.1] (当前策略对 3 个输出的概率)
Q = [0.4, 0.4, 0.2] (参考策略对同样 3 个输出的概率)
KL = 0.7 * log(0.7/0.4) + 0.2 * log(0.2/0.4) + 0.1 * log(0.1/0.2)
= 0.7 * 0.559 + 0.2 * (-0.693) + 0.1 * (-0.693)
= 0.391 - 0.139 - 0.069
= 0.184
性质:
- KL >= 0 恒成立
- KL = 0 当且仅当 P = Q
- KL 不对称:KL(P||Q) != KL(Q||P)
3.3 KL 正则化的 RL 目标
- :参考策略(通常是 SFT 后的模型,或上一轮迭代的模型)
- :正则化强度,控制”探索自由度”
直觉:
β 大 (如 0.5):
"你可以自由探索,但每偏离参考模型一步都要付出代价"
→ 保守训练,稳定但提升幅度有限
β 小 (如 0.01):
"随便探索,偏离参考模型的代价很低"
→ 激进训练,可能大幅提升但也可能不稳定
3.4 KL 正则化的最优策略
这个优化问题有闭合形式的解(数学推导见附录):
其中 是归一化常数。
直觉:最优策略在参考策略的基础上,按奖励的指数函数重新加权:
如果 r(x,y) 很高 → exp(r/β) 很大 → π*(y|x) 远高于 π_ref(y|x)
如果 r(x,y) 很低 → exp(r/β) 很小 → π*(y|x) 远低于 π_ref(y|x)
这个闭合解是 OAPL 算法的理论基础。
四、优势函数(Advantage)
4.1 定义
- :这条 rollout 的实际得分
- :这个 prompt x 的期望得分(“baseline”)
4.2 为什么需要 baseline
假设有两个 prompt:
prompt A (简单): 8 条 rollout 的分数 = [1.0, 1.0, 0.9, 1.0, 0.8, 1.0, 1.0, 0.9]
prompt B (困难): 8 条 rollout 的分数 = [0.1, 0.0, 0.3, 0.0, 0.2, 0.0, 0.1, 0.0]
如果直接用 reward 作为训练信号:
- prompt A 的所有 rollout 都得到强正信号 → 模型没有有效学习(因为都差不多)
- prompt B 中 0.3 的那条应该被鼓励(它比平均好很多),但绝对分数很低
用优势函数:
prompt A: V(A) ≈ 0.95
A(A, y3) = 0.9 - 0.95 = -0.05 (略差于平均)
A(A, y5) = 0.8 - 0.95 = -0.15 (差于平均)
prompt B: V(B) ≈ 0.09
A(B, y3) = 0.3 - 0.09 = +0.21 (远好于平均! 应该鼓励)
A(B, y1) = 0.1 - 0.09 = +0.01 (略好于平均)
优势函数自动归一化了难度差异。
4.3 V(x) 的估计方式
GRPO 的方式(组内归一化):
简单直接:组内平均作为 baseline,除以标准差归一化。
OAPL 的方式(log-sum-exp):
这不是简单平均,而是 log-sum-exp 平均,它更关注高分 rollout。
数值例子(β = 0.1):
r = [0.3, 0.1, 0.8, 0.0]
exp(r/β) = [exp(3), exp(1), exp(8), exp(0)]
= [20.09, 2.72, 2981, 1.0]
mean(exp(r/β)) = (20.09 + 2.72 + 2981 + 1.0) / 4 = 751.2
V̂* = 0.1 * log(751.2) = 0.1 * 6.62 = 0.662
注意:简单平均 mean(r) = 0.3,但 V̂* = 0.662,因为 exp 函数让高分 rollout(0.8)的权重指数级放大。
直觉:V̂* 更接近”最优策略下的期望回报”,而不是”当前策略下的平均回报”。
五、RLHF 的完整流程
RLHF(Reinforcement Learning from Human Feedback)是最早广泛使用的 LLM RL 方案(InstructGPT / ChatGPT 使用)。
5.1 三步流程
Step 1:SFT
在高质量 instruction-response 数据上微调,得到 。
Step 2:训练 Reward Model
对每个 prompt x:
让 π_SFT 生成多个回答 y_1, y_2, ..., y_k
人类标注员排序: y_3 > y_1 > y_2 > ...
训练 Reward Model R_φ:
对于每个偏好对 (y_w, y_l)(w=win, l=lose):
L = -log(sigmoid(R_φ(x, y_w) - R_φ(x, y_l)))
这个损失(Bradley-Terry 模型)确保 Reward Model 给排名靠前的回答更高分。
Step 3:PPO 优化
用 PPO(Proximal Policy Optimization)最大化:
PPO 的核心是 clipped surrogate objective:
其中 通常是 0.2。clipping 防止单步更新过大。
5.2 RLHF 的问题
- 需要训练单独的 Reward Model:额外成本
- Reward Model 可能被 hack:模型学会生成”骗过 RM 但实际不好”的输出
- PPO 工程复杂:需要同时维护 actor、critic、reference model、reward model——4 个模型
六、GRPO:简化版 RL
6.1 核心思想
GRPO(Group Relative Policy Optimization)去掉了 Reward Model 和 Critic,直接用任务奖励。
对每个 prompt x:
1. 用当前策略 π_θ 采样 G 条 rollout: {y_1, ..., y_G}
2. 计算每条的奖励 r_i(直接用任务 metric,不需要 RM)
3. 组内归一化:
Â_i = (r_i - mean(r)) / (std(r) + ε)
4. 更新策略:
L = -1/G * Σ min(ρ_i * Â_i, clip(ρ_i, 1-ε, 1+ε) * Â_i)
其中 ρ_i = π_θ(y_i|x) / π_old(y_i|x) (重要性比率)
6.2 数值走一遍
prompt x = "2+3=?"
G = 4 条 rollout:
y1 = "5" → r1 = 1.0
y2 = "23" → r2 = 0.0
y3 = "5.0" → r3 = 0.8
y4 = "五" → r4 = 0.5
mean(r) = 0.575
std(r) = 0.377
归一化优势:
Â1 = (1.0 - 0.575) / 0.377 = 1.127 → 强烈鼓励
Â2 = (0.0 - 0.575) / 0.377 = -1.526 → 强烈抑制
Â3 = (0.8 - 0.575) / 0.377 = 0.597 → 适度鼓励
Â4 = (0.5 - 0.575) / 0.377 = -0.199 → 略微抑制
6.3 On-policy 的代价
GRPO 是 on-policy 的——每轮训练都要用最新模型重新采样。
训练循环:
Step 1: π_θ_0 采样 10000 条 rollout (耗时 2 小时)
Step 2: 在这些数据上更新几步 → π_θ_1
Step 3: 丢弃所有数据
Step 4: π_θ_1 重新采样 10000 条 (又 2 小时)
Step 5: 更新 → π_θ_2
...
对于 KARL 的 Agent 场景:每条 rollout 包含大量工具调用(BrowseComp-Plus 可达 200 步),采样成本极高。每轮重新采样不可接受。
七、OAPL:KARL 的核心算法
7.1 从最优策略出发
回忆 KL 正则化 RL 的最优策略:
两边取对数:
重排:
注意 (最优价值函数),所以:
这个等式说:最优策略相对参考策略的对数概率比,正好等于最优优势函数。
7.2 OAPL 的损失函数
OAPL 用最小二乘回归让当前策略逼近这个等式:
左边:当前策略相对参考策略的”偏好变化量” 右边:根据奖励和价值函数,应该变化多少
训练就是让两边尽量一致。
7.3 数值走一遍
prompt x, β = 0.1
4 条 rollout:
y1: r=0.8, log(π_θ/π_ref) = 0.5
y2: r=0.2, log(π_θ/π_ref) = -0.3
y3: r=0.6, log(π_θ/π_ref) = 0.2
y4: r=0.0, log(π_θ/π_ref) = -0.8
估计 V̂*(x):
exp(r/β) = [exp(8), exp(2), exp(6), exp(0)]
= [2981, 7.39, 403.4, 1.0]
mean = 848.2
V̂* = 0.1 * log(848.2) = 0.675
目标优势:
A1 = 0.8 - 0.675 = 0.125
A2 = 0.2 - 0.675 = -0.475
A3 = 0.6 - 0.675 = -0.075
A4 = 0.0 - 0.675 = -0.675
当前 "偏好变化量":
β * log(π_θ/π_ref):
y1: 0.1 * 0.5 = 0.05
y2: 0.1 * (-0.3) = -0.03
y3: 0.1 * 0.2 = 0.02
y4: 0.1 * (-0.8) = -0.08
损失:
L1 = (0.05 - 0.125)^2 = 0.0056
L2 = (-0.03 - (-0.475))^2 = 0.198
L3 = (0.02 - (-0.075))^2 = 0.009
L4 = (-0.08 - (-0.675))^2 = 0.354
总损失 = 0.0056 + 0.198 + 0.009 + 0.354 = 0.567
梯度会让模型调整参数,使得:
- y1 的 log(π_θ/π_ref) 从 0.5 增大(因为 A1 > 0,应该更鼓励)
- y2 的 log(π_θ/π_ref) 从 -0.3 减小(因为 A2 < 0,应该更抑制)
7.4 双 β 设计
KARL 实际使用两个独立的 β:
- :用于计算 (控制价值估计的平滑度)
- :用于损失函数中的 log ratio 缩放(控制 KL 约束强度)
V̂* = β_1 * log(mean(exp(r / β_1))) ← β_1 控制
L = (β_2 * log(π/π_ref) - (r - V̂*))^2 ← β_2 控制
为什么要分开?
- 影响”哪些 rollout 被认为是好的”——小 β_1 → 只有最好的 rollout 有显著正优势
- 影响”每步更新多大”——小 β_2 → 每步更新更小、更保守
两个 β 分开调节,提供更灵活的训练控制。
八、On-policy vs Off-policy 的深入对比
8.1 分布偏移问题
Off-policy 的核心挑战:训练数据来自旧策略 ,但我们要优化当前策略 。
如果 和 差距很大,训练数据的分布不代表当前策略的行为 → 学习信号有偏。
GRPO 处理方式:importance weight clipping + 丢弃偏离太大的数据 OAPL 处理方式:回归目标本身就包含 log(π_θ/π_ref),天然考虑了分布差异
8.2 工程对比
GRPO 训练系统:
[推理引擎] ←→ [训练引擎] ←→ [奖励计算]
↕ ↕
同步运行,互相等待
需要: 4 份模型权重(actor, ref, possibly critic, RM)
OAPL 训练系统:
阶段 1: [推理引擎] → 离线生成大量 rollout → 存磁盘
阶段 2: [训练引擎] → 从磁盘读 rollout → 多次训练
需要: 2 份模型权重(actor, ref),完全解耦
8.3 超参搜索成本
GRPO: 尝试 10 组超参 → 每组都要重新采样 → 10x 推理成本
OAPL: 采样一次数据 → 同一批数据跑 10 组超参 → 1x 推理成本
这个优势在 KARL 的 Agent 场景中被放大:每条 rollout 涉及数十到数百次工具调用,推理成本是纯语言 RL 的 10-100 倍。
九、多步 Agent 场景的特殊处理
9.1 Token 掩码
Agent 的 rollout 交替包含模型生成和工具返回:
[模型: "我需要搜索..." ] ← 计算损失
[工具: "文档A: ..." ] ← 掩码(不计算损失)
[模型: "根据文档分析..." ] ← 计算损失
[工具: "文档B: ..." ] ← 掩码
[模型: "最终答案是..." ] ← 计算损失
为什么掩码工具返回?因为这些 token 不是模型”决定”生成的,它们是环境返回的。对它们计算 log probability 没有意义——模型无法控制工具返回什么。
9.2 长轨迹分段
BrowseComp-Plus 的 rollout 可能超过 200 步,涉及多次上下文压缩。整条轨迹可能有 32000+ token,直接放进 GPU 训练不现实(显存不够)。
KARL 的分段策略:
完整轨迹:
[prompt] → [搜索 1-50] → [压缩 1] → [搜索 51-100] → [压缩 2] → [搜索 101-150] → [答案]
分段:
段 1: x = prompt, y = 搜索 1-50 的模型输出部分
段 2: x = 压缩 1 的摘要, y = 搜索 51-100 的模型输出部分
段 3: x = 压缩 2 的摘要, y = 搜索 101-150 + 答案的模型输出部分
每段都使用整条轨迹的最终奖励作为 r
9.3 压缩步骤也训练
压缩步骤额外创建训练样本:
x = 待压缩的完整历史(很长)
y = 模型生成的压缩摘要(短)
r = 整条轨迹的最终奖励
这让模型学到:为了最终答对而压缩。
Table 6 的交叉实验证明了这个设计的价值:
GLM Air 搜索 + GLM Air 压缩 = 0.44
GLM Air 搜索 + KARL 压缩 = 0.54 (+0.10)
KARL 搜索 + GLM Air 压缩 = 0.46 (+0.02)
KARL 搜索 + KARL 压缩 = 0.57 (+0.13)
KARL 训练的压缩能力本身就价值巨大——单独使用就能提升 10 个百分点。
十、迭代自举训练
10.1 流程
Iter 0: π_ref = GLM 4.5 Air
→ 用 π_ref 合成训练数据(QA 对 + rollout)
→ pass-rate 过滤(阈值 0.6)
→ OAPL 训练 → π_1 (KARL Iter.1)
Iter 1: π_ref = π_1
→ 用 π_1 合成更好的数据(模型更强 → 数据质量更高)
→ pass-rate 阈值提高到 0.7
→ OAPL 训练 → π_2 (KARL Iter.2)
Iter 2: π_ref = π_2
→ 用 π_2 合成更难的数据
→ pass-rate 阈值提高到 0.9
→ OAPL 训练 → π_3 (KARL Iter.3)
10.2 关键观察
每轮的改进是持续的,没有到达平台期:
KARL-TREC (只在 TREC-Biogen 训练, 3 轮):
基座 → Iter.1: TREC 66.0 → 76.0 (+10.0)
Iter.1 → Iter.2: 76.0 → 82.0 (+6.0)
Iter.2 → Iter.3: 82.0 → 85.0 (+3.0)
分布外泛化也持续改善:
FreshStack: 52.9 → 52.2 → 56.7 → 56.7
QAMPARI: 45.9 → 48.2 → 49.8 → 50.8
10.3 为什么不会过拟合
因为每轮都重新合成数据,数据分布在变化。模型不是在同一批固定数据上反复训练——而是每轮面对新的、更难的挑战。
十一、多任务 RL vs 蒸馏
11.1 实验设计
KARL 在两个结构性不同的任务上训练:
- BrowseComp-Plus(深搜:反复缩小范围,轨迹中位数 50-200 步)
- TREC-Biogen(广搜:大范围收集信息,轨迹中位数 4-6 步)
对比方案——多专家蒸馏:
- 分别训练 BCP Expert 和 TREC Expert
- 收集两个 Expert 在各自任务上的最佳 rollout
- 用 SFT 在这些 rollout 上训练一个统一模型(蒸馏)
11.2 关键结果
分布内 分布外 总分
蒸馏 69.1 59.4 64.7
蒸馏 + TTC(N=10) 75.3 59.6 64.8 (分布外仅 +0.2)
KARL RL 69.4 53.7 58.9
KARL RL + TTC 78.4 62.7 67.9 (分布外 +9.0)
蒸馏 + TTC 在分布外几乎没有提升(59.4 → 59.6)。为什么?
蒸馏学的是”模仿专家的具体行为模式”——在训练任务上,模仿很有效;在新任务上,专家的具体模式不适用,模仿就失效了。
RL 学的是”通用搜索策略”——什么时候该深搜、什么时候该广搜、什么时候该换查询、什么时候该停止。这种策略迁移到新任务上仍然有效。
十二、RL Beyond Sharpening
12.1 Sharpening 假说
有人认为 RL 后训练只是”sharpening”——把基座模型已有的能力”锐化”,让它更一致地给出正确答案,但没有真正扩展能力边界。
如果这个假说成立:
- max@1 会提升(更一致 → 单次就对的概率提高)
- max@k 在 k 很大时不会变(因为能力边界没扩展)
12.2 max@k 分析
max@k = 采样 k 次,取最好的一次的分数。它衡量”模型在最好情况下能做到什么”。
BrowseComp-Plus 上:
基座 max@1 = 0.45, max@8 = 0.65, max@16 = 0.72
KARL max@1 = 0.59, max@8 = 0.78, max@16 = 0.85
观察:
KARL max@1 (0.59) ≈ 基座 max@8 (0.65) → 单次就接近基座 8 次的最佳
KARL max@2 > 基座 max@16 → 2 次就超过基座 16 次的最佳
KARL max@16 (0.85) > 基座 max@16 (0.72) → 能力上限真的扩展了
max@k 在所有 k 值上都提升 → 证明 RL 真正扩展了能力边界,不只是 sharpening。
12.3 Pass@16 流动分析
论文还做了更细致的分析——在训练前后,哪些问题的状态发生了变化:
Unsolved → Partial: 37.2% (原来完全不会的,现在能部分解决)
Partial → Solved: 33.3% (原来部分对的,现在完全对)
Solved → Partial: 6.4% (极少数退步)
Solved → Unsolved: 0% (没有完全退步的)
特别值得注意:训练数据的 pass-rate 过滤已经把 Solved(全对)和 Unsolved(全错)的问题都删掉了。所以在这些 unseen 问题 上的改善完全来自泛化。
本章总结
你现在应该能理解:
- RL 目标:最大化期望奖励,用蒙特卡洛估计
- KL 正则化:防止模型偏离参考策略太远,β 控制约束强度
- 优势函数:相对表现,自动归一化难度差异
- RLHF 流程:SFT → Reward Model → PPO,工程复杂
- GRPO:去掉 RM,组内归一化,但 on-policy 成本高
- OAPL:从最优策略推导出回归目标,off-policy 更高效
- 双 β 设计:分别控制价值估计和 KL 约束
- 多步 Agent RL:token 掩码、轨迹分段、压缩端到端训练
- 迭代自举:模型越强 → 数据越好 → 训练更有效
- RL vs 蒸馏:RL 学通用策略,蒸馏学特定行为,泛化差异巨大
- RL beyond sharpening:max@k 全面提升证明能力边界真正扩展
下一篇:03. RAG、Agent 与推理扩展 — 从”一次问答”到”多步智能搜索”