第 10 课:RLHF/RLVR:奖励来源、KL 与 token 对齐

参考答案版:包含完整代码实现与问答题深度参考解析。建议先独立完成练习题后再展开核对。

本课目标与完成标准

学完后你应能:区分 RLHF/RLVR 与奖励粒度,解释 KL 估计的采样条件,并实现 token 对齐的 shaped reward。

通过必须同时满足:

  • 独立补齐本课唯一的代码填空题,并通过给定检查;
  • 三个问答题均说明因果链,而不是只报术语;
  • 能指出至少一个正确性边界和一个性能取舍;
  • 总分不低于 8/10,且没有一票否决级概念错误。

前置关系

  • 课程前置:Python、概率期望、基本深度学习
  • 本课在路线中的作用:把奖励来源、参考策略正则与 token 边界连接到策略更新;KL 约束偏移,不保证回答质量。

核心心智模型

1. 它是什么,解决什么问题

基于人类反馈的强化学习(RLHF)与基于可验证奖励的强化学习(RLVR)是现代大语言模型(LLM)从初始预训练/指令微调(SFT)模型演进至具备复杂推理能力、高对齐度超级智能(如 InstructGPT、Claude、DeepSeek-R1)的核心后训练(Post-Training)范式。 在 LLM 对齐领域,必须明确区分两个完全正交的独立维度:

  1. 奖励来源轴(Reward Source):
    • RLHF(神经奖励模型):主要面向开放式创作、安全性、对话助手等无法用代码严格判定的主观任务。先基于人类偏好对 (x,yw,yl)(x, y_w, y_l) 训练一个参数化的标量奖励模型(Reward Model, RM),利用 Bradley-Terry 似然对回答打分: ℒRM(ϕ)=−𝔼(x,yw,yl)∼𝒟[logσ(rϕ(x,yw)−rϕ(x,yl))] \mathcal{L}_{\text{RM}}(\phi) = - \mathbb{E}_{(x, y_w, y_l) \sim \mathcal{D}} \left[ \log \sigma \left( r_\phi(x, y_w) - r_\phi(x, y_l) \right) \right]
    • RLVR(可验证规则奖励):主要面向数学推理(GSM8K、MATH)、代码生成(HumanEval、LeetCode)等有客观真值的形式系统。直接利用符号数学解析器、Python 代码沙箱单元测试、正则表达式等规则引擎充当真值判定器(Oracle),奖励严格为二值(0/1)或步骤过程得分。
  2. 策略优化器轴(Policy Optimizer):PPO、GRPO、REINFORCE 或 DPO。二者可自由组合(例如 DeepSeek-R1-Zero 采用的是 RLVR + GRPO,而 InstructGPT 采用的是 RLHF + PPO)。

后训练解决的核心工程问题是:如何将序列生成任务解构为 Token 级的马尔可夫决策过程,并通过参考策略(Reference Policy, πref\pi_{\text{ref}})的 KL 散度锚定,防止模型在追逐高奖励过程中发生语义漂移(Language Degradation)与奖励欺骗(Reward Hacking)。

2. 它如何工作

RLHF 四模型拓扑与端到端训练流水线如图 10-1 与图 10-2 所示:

  1. 四模型拓扑与 Token 级 KL 惩罚(图 10-1):

flowchart TD
    Prompt["输入 Prompt x"] --> Actor["生成策略模型 π_θ (Active)"]
    Prompt --> Ref["参考基础模型 π_ref (Frozen)"]
    Actor --> Response["生成回复文本 y = (y_1, y_2, ..., y_T)"]
    Response --> RM["奖励模型 r_ϕ(x, y) 或规则验证器 (RLVR)"]
    Actor & Ref --> KL["Token 级 KL 散度: D_KL = log π_θ(y_t) - log π_ref(y_t)"]
    RM & KL --> FinalReward["最终组合标量/密集奖励: R = RM(x, y) - β * D_KL"]

图 10-1:大模型对齐 RLHF 四模型拓扑与 Token 级 KL 惩罚锚定

在标准 RLHF 流程中,GPU 集群上同时运行四个模型角色: - Actor πθ\pi_\theta:正在被梯度优化的生成策略; - Reference πref\pi_{\text{ref}}:冻结的 SFT 初始模型,充当锚点; - Critic VψV_\psi:评估当前 Token 前缀价值的网络(PPO 专有); - Reward Model rϕr_\phi(或规则判题器):在生成完毕后给出终局标量打分。 为了防止策略发生不可控漂移,优化目标被形式化为最大化带 KL 惩罚的期望回报: maxθ𝔼x∼𝒟,y∼πθ[R(x,y)]−βDKL(πθ(y∣x)∥πref(y∣x)) \max_\theta \mathbb{E}_{x \sim \mathcal{D}, y \sim \pi_\theta} \left[ R(x, y) \right] - \beta D_{\text{KL}}\left(\pi_\theta(y \mid x) \parallel \pi_{\text{ref}}(y \mid x)\right)

  1. 点级 Token 奖励整形(Reward Shaping): 根据因果概率分解,全序列的 KL 散度等于各 Token 相对熵的求和 ∑t=1Tlog⁡πθ(yt)πref(yt)\sum_{t=1}^T \log \frac{\pi_\theta(y_t)}{\pi_{\text{ref}}(y_t)}。PPO 将 KL 惩罚分解注入到每个时序步的即时奖励中:
    • 对于中间生成的回答 Token(t<T−1t < T-1):rtshaped=−β(logπθ(yt∣x,y<t)−logπref(yt∣x,y<t))r_t^{\text{shaped}} = - \beta \left( \log \pi_\theta(y_t \mid x, y_{<t}) - \log \pi_{\text{ref}}(y_t \mid x, y_{<t}) \right);
    • 对于回答的最后一个有效 Token(t=T−1t = T-1): rT−1shaped=Renv(x,y)−β(logπθ(yT−1)−logπref(yT−1)) r_{T-1}^{\text{shaped}} = R_{\text{env}}(x, y) - \beta \left( \log \pi_\theta(y_{T-1}) - \log \pi_{\text{ref}}(y_{T-1}) \right) 这种塑造将终局全局奖励与密集的路径偏差惩罚平滑拼接,随后无缝输入 GAE 优势递推引擎(图 10-2)。

flowchart LR
    PromptBatch["采样 Prompt 批次"] --> VLLM["高性能推理引擎生成候选文本"]
    VLLM --> RMScore["奖励模型打分 / 编译器验证 (RLVR)"]
    RMScore --> KLPenalty["计算对参考模型的 KL 漂移惩罚"]
    KLPenalty --> GAEAdv["沿 Token 序列递归生成 GAE 优势信号"]
    GAEAdv --> PPOUpdate["分布式框架 (Megatron/DeepSpeed) 反向传播更新权重"]

图 10-2:RLHF / RLVR 后训练全链路端到端数据流水线

  1. KL 估计器的数学无偏性与点态非负性(k1k_1 vs k3k_3):
    • 经典估计量 k1k_1:k1=log⁡πθ−log⁡πrefk_1 = \log \pi_\theta - \log \pi_{\text{ref}}。在单 Token 采样点上,k1k_1 完全可以为负数(当当前策略对某个 Token 的预测概率偶然低于参考模型时),但其在策略分布下的数学期望严格收敛于真值 KL;
    • 非负估计量 k3k_3(Schulman 2020):令比率 r=πrefπθ=exp⁡(log⁡πref−log⁡πθ)r = \frac{\pi_{\text{ref}}}{\pi_\theta} = \exp(\log \pi_{\text{ref}} - \log \pi_\theta),定义 k3=r−log⁡r−1=exp⁡(d)−d−1k_3 = r - \log r - 1 = \exp(d) - d - 1。根据凸函数不等式,k3k_3 保证了在每一个 Token 采样点上恒非负(k3≥0k_3 \ge 0),有效消除了 k1k_1 局部出现负惩罚(反向奖励)给价值网络拟合带来的高频噪声。

3. 正确性条件与常见误区

  • Causal Shift 因果错位(The Shift Bug): 自回归 Transformer 输出的 Logits 形状为 [B, L, V],其中第 ii 个位置的 Logit 预测的是第 i+1i+1 个 Token!因此在对齐计算时,必须严格执行因果错位: shift_logits = logits[:, :-1, :] 对齐 shift_labels = input_ids[:, 1:]。 常见灾难性 Bug 是直接使用 logits 收集 input_ids,导致模型用生成当前 Token 的概率去监督前一个已输入的 Token,引发整个策略梯度方向完全逆反。

  • Mask 掩码必须包含有效 EOS 并严格排除 Prompt 和 Padding: 在计算策略梯度与 KL 时,必须构造精确的布尔掩码 action_mask。

    • Prompt 区域是环境变量,必须全填 0;
    • Padding 区域是物理占位符,必须全填 0;
    • 只有模型自主生成的 Completion Token 以及标志着回答自然结束的 <|im_end|> 或 EOS 必须填 1。若将 EOS 排除在外,模型永远学不会主动停机,会导致长文本胡言乱语;若把 Padding 计入,会在空白位置累积巨大的无效梯度。
  • 不可在 Loss 与 Reward 中双重施加 KL 惩罚: 若在采样阶段已经通过 Reward Shaping 在每个 Token 上扣除了 βDKL\beta D_{\text{KL}},那么在随后的 PPO 策略损失函数中绝对不能再次添加 KL 损失项!否则会导致惩罚翻倍,使模型被过度死锁在参考模型周围,彻底扼杀任何探索能力。

4. 性能与工程取舍

  • 推理引擎与训练引擎的内存分离(vLLM / SGLang 解耦架构): 在大模型后训练中,Rollout 阶段需要高并发自回归采样,而 Training 阶段需要执行大显存反向传播。现代工业系统(如 OpenRLHF、Ray-vLLM)采用解耦架构:使用 vLLM 的 PagedAttention 管理生成,通过 CPU 共享内存或高速 NCCL 将生成样本异步流转至 Megatron/DeepSpeed 训练节点,彻底消除显存碎片与架构冲突。

  • 惩罚系数 β\beta 的两难困境(Alignment Tax): β\beta 过大,模型拒绝改变现有行为,新策略无法探索出超越基座的高推理解法;β\beta 过小或为 0,模型会在数个迭代周期内迅速发现 RM 的对抗漏洞(如盲目堆砌学术套话、重复长句骗取高分),引发灾难性的 Reward Hacking。

具体演示:Causal Shift 错位与单 Token Shaped Reward 演算

假设输入序列 Token 序列为 input_ids = [10, 11, 20, 2, 0](其中 10, 11 为 Prompt;20 为回答首词;2 为 EOS;0 为 PAD):

  • Causal Shift 对齐:
    • labels = input_ids[1:] = [11, 20, 2, 0]
    • action_mask = [0, 1, 1, 0](仅监督被预测为 20 和 2 的两处时步)
  • 数值演练: 设惩罚系数 β=0.1\beta = 0.1。
    • 在第 1 个有效回答 Token(预测 20):无即时环境分(env_reward=0env\_reward = 0),若此时 log⁡π=−1.0\log \pi = -1.0,log⁡πref=−1.3\log \pi_{\text{ref}} = -1.3,则 k1=−1.0−(−1.3)=+0.3k_1 = -1.0 - (-1.3) = +0.3。 r1shaped=0−0.1×(+0.3)=−0.03 r_1^{\text{shaped}} = 0 - 0.1 \times (+0.3) = -0.03 该位置因为偏离基线被施加微弱惩罚;
    • 在第 2 个有效回答 Token(预测 EOS 2):回答自然完结,环境裁判给出终局高分 env_reward=2.0env\_reward = 2.0。若此处 log⁡π\log \pi 与 log⁡πref\log \pi_{\text{ref}} 差值相同: r2shaped=2.0−0.1×(+0.3)=1.97 r_2^{\text{shaped}} = 2.0 - 0.1 \times (+0.3) = 1.97
    • 最终沿序列构成的有效奖励流为 [0.0,−0.03,1.97,0.0][0.0, -0.03, 1.97, 0.0]。终局分精准挂载在 EOS 上,并通过 GAE 向前传播信用。

实践任务:唯一代码填空题

补齐每 token shaped reward。

规则:只能修改 TODO/______ 所在位置;不要删除断言或放宽误差。代码注释说明了每个边界条件。

Code
import math

def shaped_token_reward(env_reward, logp, ref_logp, beta):
    """非末端 env_reward 通常为 0。"""
    # TODO:只补齐下面这个表达式。
    return ______

assert abs(shaped_token_reward(0., -1., -1.3, .1) + .03) < 1e-12
assert abs(shaped_token_reward(2., -1., -1.3, .1) - 1.97) < 1e-12

# 自构造有限分布,按当前策略概率加权验证期望,不是训练 benchmark。
p, q = [.8, .2], [.5, .5]
k1 = [math.log(a / b) for a, b in zip(p, q)]
d = [math.log(b / a) for a, b in zip(p, q)]
k3 = [math.expm1(x) - x for x in d]
exact_kl = sum(a * math.log(a / b) for a, b in zip(p, q))
assert min(k1) < 0.
assert all(x >= 0. for x in k3)
assert abs(sum(a*x for a, x in zip(p, k1)) - exact_kl) < 1e-12
assert abs(sum(a*x for a, x in zip(p, k3)) - exact_kl) < 1e-12

# Causal shift 后的 label 边界:首回答 token 和 EOS 都有效。
input_ids = [10, 11, 20, 2, 0]  # prompt, prompt, answer, EOS, PAD
labels = input_ids[1:]
mask = [0, 1, 1, 0]
assert [t for t, m in zip(labels, mask) if m] == [20, 2]
last = max(i for i, m in enumerate(mask) if m)
rewards = [shaped_token_reward(2. if i == last else 0., -1., -1.3, .1)
           if m else 0. for i, m in enumerate(mask)]
assert all(abs(a-b) < 1e-12 for a,b in zip(rewards, [0., -.03, 1.97, 0.]))

检查方法

运行本单元格;所有 assert 必须通过。另手工构造一个边界输入,解释预期结果。

提交时请给出:补齐后的代码、实际运行输出(环境不可用时注明“仅静态审查”)以及对失败用例的解释。

Q1

把一次回答从 causal shift、奖励来源到 KL shaping、GAE 和策略更新串起来;说明 RLHF/RLVR 与 PPO/GRPO 为什么不是同一分类轴。

你的答案:

Q2

policy/ref 的 padding mask 不一致会怎样污染 KL 与 advantage?

你的答案:

Q3

RM reward 上升而人工胜率下降,你会如何设计诊断矩阵?

你的答案:

评分与通过规则

  • 代码 4 分:正常输入 2 分,边界输入 1 分,解释实现 1 分;
  • Q1~Q3 各 2 分;
  • 一票否决:结果碰巧正确但核心因果链错误、删除边界检查、把未运行结果说成实测。

需要提示时按四级机制请求:概念区域 → 具体方向 → 关键局部 → 完整答案。

参考答案(仅 answer 分支)

先完成题目再核对。即使代码一致,也要能解释关键步骤,并尝试更换一个输入规模。

Code
import math

def shaped_token_reward(env_reward, logp, ref_logp, beta):
    """非末端 env_reward 通常为 0。"""
    # 参考实现:表达式直接对应上文不变量。
    return env_reward - beta * (logp - ref_logp)

assert abs(shaped_token_reward(0., -1., -1.3, .1) + .03) < 1e-12
assert abs(shaped_token_reward(2., -1., -1.3, .1) - 1.97) < 1e-12

# 自构造有限分布,按当前策略概率加权验证期望,不是训练 benchmark。
p, q = [.8, .2], [.5, .5]
k1 = [math.log(a / b) for a, b in zip(p, q)]
d = [math.log(b / a) for a, b in zip(p, q)]
k3 = [math.expm1(x) - x for x in d]
exact_kl = sum(a * math.log(a / b) for a, b in zip(p, q))
assert min(k1) < 0.
assert all(x >= 0. for x in k3)
assert abs(sum(a*x for a, x in zip(p, k1)) - exact_kl) < 1e-12
assert abs(sum(a*x for a, x in zip(p, k3)) - exact_kl) < 1e-12

# Causal shift 后的 label 边界:首回答 token 和 EOS 都有效。
input_ids = [10, 11, 20, 2, 0]  # prompt, prompt, answer, EOS, PAD
labels = input_ids[1:]
mask = [0, 1, 1, 0]
assert [t for t, m in zip(labels, mask) if m] == [20, 2]
last = max(i for i, m in enumerate(mask) if m)
rewards = [shaped_token_reward(2. if i == last else 0., -1., -1.3, .1)
           if m else 0. for i, m in enumerate(mask)]
assert all(abs(a-b) < 1e-12 for a,b in zip(rewards, [0., -.03, 1.97, 0.]))

Q1 参考答案

  1. 大模型后训练全链路执行时序:
    • 自回归推理与因果对齐(Causal Shift):Actor 策略 πθ\pi_\theta 生成候选 Completion。取模型生成的 Logits logits[:, :-1, :] 与右移标签 labels = input_ids[:, 1:] 做对齐,通过 Gather 提取生成 Token 的条件对数概率序列 log⁡πθ(yt)\log \pi_\theta(y_t) 与参考模型 log⁡πref(yt)\log \pi_{\text{ref}}(y_t),同时生成精准的回答有效掩码 action_mask(Prompt 与 PAD 为 0,有效回答与 EOS 为 1);
    • 终局奖励与点态 KL 塑造(Reward Shaping):奖励来源(RM 或编译器沙箱)对完整文本给出终局标量反馈 RR。中间各 Token 扣除采样 KL 漂移项 −β(log⁡πθ−log⁡πref)-\beta (\log \pi_\theta - \log \pi_{\text{ref}}),终局有效 Token(EOS)叠加终局奖励 RR,构成密集奖励序列;
    • 优势估计与策略迭代:密集奖励输入 GAE 模块,利用 Critic 估计逆序递归生成各时步优势 Ât\hat{A}_t;随后输入 PPO 优化器利用新旧策略比率 rt(θ)r_t(\theta) 实施悲观裁剪更新(或输入 GRPO 按照组内均值归一化更新)。
  2. 正交分类轴论证:
    • 奖励来源轴(打分裁判是谁):RLHF(神经偏好奖励模型)vs RLVR(确定性形式规则验证器,如 LeetCode 测试用例 / SymPy 验证器)。该维度决定了环境反馈信号的保真度与抗利用性;
    • 优化算法轴(参数如何基于分数演进):PPO(需要 Critic 拟合与 GAE 优势)vs GRPO(组内相对排名、无需 Critic)vs DPO(离线解析闭式替代)。 二者在系统架构上完全解耦,可任意正交组合(如 DeepSeek-R1-Zero 即为典型的“RLVR 打分 + GRPO 优化”的经典组合)。

Q2 参考答案

若策略模型 πθ\pi_\theta 与参考模型 πref\pi_{\text{ref}} 的 Padding 掩码未严格对齐、或未在进入损失前剔除 Padding Token,会引发严重的无效填充区假性梯度污染与优势逆向倒灌(Padding Mask Contamination):

  1. 伪 KL 散度数值失真:Padding 占位符(如 <pad>)在语义上毫无意义。若将 Padding 区域的 Logits 纳入计算,由于两个模型在未训练的 Pad 标记上的未定概率随机波动,log⁡πθ(pad)−log⁡πref(pad)\log \pi_\theta(\text{pad}) - \log \pi_{\text{ref}}(\text{pad}) 会产生不可控的巨大伪 KL 噪声;

  2. 终局奖励挂载错位:若依据错误包含 Padding 的序列寻找最后一个有效 Token,终局的判题高分 RR 会被错误挂载在尾部某个毫无意义的 Padding Token 上,导致真实的有效结束符 EOS 无法获得正确信用分配;

  3. GAE 逆向反传污染整个回答:GAE 是自后向前递归累加的:At=δt+γλAt+1A_t = \delta_t + \gamma \lambda A_{t+1}。一旦尾部的 Padding 位置包含了虚假的巨大残差,该错误信号会沿着递归通道一路向左反向渗透,将整条有效推理链中所有正确生成的数学/代码 Token 的优势值彻底污染,导致模型策略发生剧烈语义退化。

Q3 参考答案

当监控曲线出现“训练集 RM 奖励持续攀升、但第三方盲测集人工胜率或基准测试准确率断崖式下跌”时,确凿表明发生了严重的奖励黑客/奖励过度优化(Reward Hacking / Goodhart’s Law)。标准工业级诊断矩阵如下:

  1. 长度与格式偏好诊断(Length & Formatting Bias):
    • 统计回答平均 Token 长度变化。RM 通常对长文本、排版精美(Markdown 丰富、分点清晰)的回答存在系统性虚高偏好。若发现长度暴涨数倍而关键逻辑并无增加,说明模型正在通过堆砌废话骗取 RM 高分;
  2. 独立黄金裁判交叉对照(Cross-Evaluation Matrix):
    • 外部 SOTA 模型裁判:使用未参与训练的独立裁判(如 GPT-4o / Claude 3.5 Sonnet)基于固定评价细则(Rubric)进行盲测胜率评估;
    • 客观规则基准:在标准客观评测集(如 GSM8K、MMLU、HumanEval)上运行规则验证,若客观题准确率未升反降,确凿证明核心推理能力已发生灾难性遗忘;
  3. KL 散度与分布漂移审计(Drift Audit):
    • 检查对参考模型的全局 KL 散度。若 KL 曲线呈指数级突破安全阈值(例如 DKL>10D_{\text{KL}} > 10),说明惩罚系数 β\beta 过小,模型已脱离预训练语言模型的自然表达分布,必须立即增大 β\beta 或引入长度惩罚项重新校准。

参考资料

奖励、KL 与 loss 的具体组织以所选算法配方为准。