LLM 后训练实践
第4课:RLHF与GRPO

4.1 经典 RLHF 流程

InstructGPT 三阶段流程、奖励模型训练、PPO 核心组件、四模型架构及常见不稳定性

引言:从 DPO 回到 RLHF

在第3课中,我们学习了 DPO——一种跳过奖励模型和 RL 循环的偏好优化方法。但要真正理解后训练的全貌,我们需要回到 DPO 的"前辈"——RLHF(Reinforcement Learning from Human Feedback)

RLHF 是 ChatGPT、Claude 和 Gemini 等商业模型对齐的核心技术。尽管 DPO 在很多场景中可以替代 RLHF,但 RLHF 的在线探索能力在训练推理模型时不可替代。理解 RLHF 也是理解 GRPO(下一节)的必要基础。

InstructGPT 三阶段流程

InstructGPT(Ouyang 等,OpenAI,2022)确立了后训练的经典范式,分为三个阶段:

阶段1:监督微调(SFT)

在人类标注的高质量指令-回复对上进行监督微调,获得初始策略模型 πSFT\pi_{\text{SFT}}

这一阶段对应我们第1-2课的内容。SFT 为后续的 RL 训练提供一个良好的起点——如果从纯基座模型开始做 RL,模型连基本的指令跟随都不会,RL 的探索效率极低。

LSFT=E(x,y)Ddemo[logπθ(yx)]\mathcal{L}_{\text{SFT}} = -\mathbb{E}_{(x,y) \sim \mathcal{D}_{\text{demo}}} \left[\log \pi_\theta(y|x)\right]

阶段2:奖励模型训练(RM)

训练一个奖励模型 rϕ(x,y)r_\phi(x, y),它对每个(提示,回复)对输出一个标量分数,反映人类对该回复的偏好程度。

输入:人类标注的偏好对 {(x,yw,yl)}\{(x, y_w, y_l)\} 输出:一个参数化的打分函数 rϕr_\phi

阶段3:PPO 优化

使用 PPO(Proximal Policy Optimization)强化学习算法,优化策略模型以最大化奖励模型的评分,同时通过 KL 惩罚约束策略不偏离 SFT 模型太远。

奖励模型训练

Bradley-Terry 偏好模型

奖励模型的训练基于 Bradley-Terry 偏好模型(与 DPO 使用的是同一个模型)。给定偏好对 (yw,yl)(y_w, y_l),人类偏好 ywy_w 的概率为:

P(ywylx)=σ(rϕ(x,yw)rϕ(x,yl))P(y_w \succ y_l | x) = \sigma\left(r_\phi(x, y_w) - r_\phi(x, y_l)\right)

奖励模型损失

最大化偏好数据的对数似然,等价于最小化以下损失:

LRM=E(x,yw,yl)Dpref[logσ(rϕ(x,yw)rϕ(x,yl))]\mathcal{L}_{\text{RM}} = -\mathbb{E}_{(x, y_w, y_l) \sim \mathcal{D}_{\text{pref}}} \left[\log \sigma\left(r_\phi(x, y_w) - r_\phi(x, y_l)\right)\right]

注意对比:这个损失与 DPO 损失的形式非常相似!区别在于:RM 损失训练的是一个独立的奖励模型 rϕr_\phi,而 DPO 损失直接用策略模型的对数概率比作为隐式奖励。

奖励模型的架构

奖励模型通常基于与策略模型相同的预训练模型初始化,但将最后的语言模型头替换为一个标量输出头

# 奖励模型架构示意
class RewardModel(nn.Module):
    def __init__(self, base_model):
        super().__init__()
        self.backbone = base_model  # 预训练 Transformer
        self.reward_head = nn.Linear(hidden_size, 1)  # 输出标量

    def forward(self, input_ids, attention_mask):
        hidden_states = self.backbone(input_ids, attention_mask)
        # 取最后一个 token 的隐藏状态
        last_hidden = hidden_states[:, -1, :]
        reward = self.reward_head(last_hidden)
        return reward.squeeze(-1)  # shape: (batch_size,)

奖励模型的质量至关重要

奖励模型的质量直接决定了 RLHF 的上限:

RM 质量后果
高质量 RMPPO 训练有效,模型对齐良好
有偏差的 RM策略学到 RM 的偏差(如偏向长回复)
低质量 RM奖励黑客严重,PPO 训练失败

PPO 优化详解

PPO 目标函数

PPO 在 RLHF 中的目标函数为:

maxπθ  ExD,yπθ(x)[rϕ(x,y)]βKL[πθ(yx)πref(yx)]\max_{\pi_\theta} \; \mathbb{E}_{x \sim \mathcal{D}, \, y \sim \pi_\theta(\cdot|x)} \left[r_\phi(x, y)\right] - \beta \, \text{KL}\left[\pi_\theta(y|x) \| \pi_{\text{ref}}(y|x)\right]

这与 DPO 推导的第一步完全一致——DPO 正是从这个目标出发的。区别在于:DPO 通过解析求解绕过了 RL,而 RLHF 直接用 PPO 算法来优化这个目标。

PPO 核心组件

截断代理损失(Clipped Surrogate Loss)

PPO 的核心创新是截断代理目标,它限制每步更新的幅度,确保训练稳定:

LPPO=Et[min(ρtA^t,  clip(ρt,1ϵ,1+ϵ)A^t)]\mathcal{L}_{\text{PPO}} = -\mathbb{E}_t \left[\min\left(\rho_t \hat{A}_t, \; \text{clip}(\rho_t, 1-\epsilon, 1+\epsilon) \hat{A}_t\right)\right]

其中:

  • ρt=πθ(atst)πθold(atst)\rho_t = \frac{\pi_\theta(a_t|s_t)}{\pi_{\theta_{\text{old}}}(a_t|s_t)} 是概率比(importance sampling ratio)
  • A^t\hat{A}_t 是优势估计(advantage estimate)
  • ϵ\epsilon 是截断参数(通常为 0.2)

截断的直觉

  • A^t>0\hat{A}_t > 0(好的 action):允许 ρt\rho_t 增大但不超过 1+ϵ1+\epsilon
  • A^t<0\hat{A}_t < 0(坏的 action):允许 ρt\rho_t 减小但不低于 1ϵ1-\epsilon
  • 这防止了单步更新过大导致的训练崩溃

广义优势估计(GAE)

GAE(Generalized Advantage Estimation)用于计算每个 token 的优势值:

A^tGAE=l=0Tt(γλ)lδt+l\hat{A}_t^{\text{GAE}} = \sum_{l=0}^{T-t} (\gamma \lambda)^l \delta_{t+l}

其中 TD 残差 δt=rt+γV(st+1)V(st)\delta_t = r_t + \gamma V(s_{t+1}) - V(s_t)

在 RLHF 的语境中:

  • 每个 token 的生成是一个"action"
  • "reward" 通常只在序列结束时给出(即 rϕ(x,y)r_\phi(x, y) 是整个回复的奖励)
  • V(st)V(s_t) 是价值模型预测的中间状态价值

KL 散度惩罚

为防止策略偏离参考模型太远,在奖励中加入 KL 惩罚项:

rtotal(x,y)=rϕ(x,y)βtlogπθ(ytx,y<t)πref(ytx,y<t)r_{\text{total}}(x, y) = r_\phi(x, y) - \beta \sum_t \log \frac{\pi_\theta(y_t|x, y_{<t})}{\pi_{\text{ref}}(y_t|x, y_{<t})}

KL 惩罚可以按 token 计算(如上式),也可以作为整体约束。

四模型架构

PPO 在 RLHF 中需要同时维护四个模型,这是其计算开销巨大的主要原因:

模型作用是否更新典型大小
策略模型(Actor) πθ\pi_\theta生成回复7B-70B
参考模型(Reference) πref\pi_{\text{ref}}计算 KL 惩罚否(冻结)与策略相同
奖励模型(Reward) rϕr_\phi评估回复质量否(预训练好)7B-13B
价值模型(Critic) VψV_\psi估计状态价值,计算优势7B-13B

内存估算(以7B模型为例,FP16):

总内存4×7B×2bytes=56GB\text{总内存} \approx 4 \times 7\text{B} \times 2\text{bytes} = 56\text{GB}

加上优化器状态和激活值,实际需要 150-200GB 以上的 GPU 内存,通常需要多卡并行。

四模型架构的工程挑战:需要精心设计的内存管理、模型并行和流水线策略。这也是为什么 RLHF 在实践中远比 DPO 复杂——DPO 只需要2个模型(策略 + 参考),内存需求约为 PPO 的一半。

常见不稳定性

奖励黑客(Reward Hacking)

现象:策略模型学会利用奖励模型的漏洞,生成 RM 评分高但实际质量低的回复。

典型表现

  • 生成过长的回复(很多 RM 对长度有正偏差)
  • 使用特定的"讨好"模式(如过度使用"当然!这是一个非常好的问题!")
  • 输出包含看似专业但无意义的术语

缓解策略

  • 增大 KL 惩罚系数 β\beta
  • 定期更新奖励模型(对抗性训练)
  • 使用多个奖励模型的集成
  • 添加长度惩罚

KL 散度爆炸

现象:策略模型快速偏离参考模型,KL 散度急剧增大。

典型表现

  • 模型输出变得不自然、重复或无意义
  • 训练损失突然变化
  • 生成质量骤降

缓解策略

  • 增大 KL 系数 β\beta
  • 降低学习率
  • 减小 PPO 截断参数 ϵ\epsilon
  • 设置 KL 上限(当 KL > 阈值时提前停止)

价值模型崩溃

现象:价值模型的预测值变得不准确或发散。

典型表现

  • 优势估计噪声过大,PPO 更新方向不稳定
  • 训练奖励先升后降
  • 不同 prompt 的价值估计方差极大

缓解策略

  • 价值模型使用独立的学习率
  • 增加价值模型的训练步数(相对于策略模型)
  • 对价值目标进行截断

RLHF vs DPO 对比

全面对比

维度RLHF (PPO)DPO
核心思想训练 RM → RL 优化策略直接在偏好对上优化
模型数量4个2个
训练阶段3个(SFT→RM→PPO)2个(SFT→DPO)
实现复杂度极高
训练稳定性低(多种不稳定性)
计算资源极高中等
在线探索有(关键优势)
数据效率中等(需要生成+评估)高(直接用偏好数据)
推理训练适合(可探索新推理路径)不适合
聊天对齐过度工程(DPO 足够)首选

何时选择 RLHF vs DPO

实践指南

  • 通用聊天对齐:使用 DPO/SimPO。RLHF 的复杂度不值得。
  • 推理能力训练:使用 RLHF/GRPO。DPO 无法让模型探索新的推理策略。
  • 安全对齐:DPO 可以作为基线,但追求极致安全可能需要 RLHF。
  • 资源有限:DPO,因为内存和计算需求低得多。
  • 资源充足且追求极致:RLHF,因为在线探索可能发现更好的策略。

从 PPO 到 GRPO 的进化

RLHF 的四模型架构和不稳定性是巨大的工程负担。GRPO(Group Relative Policy Optimization) 的核心贡献是:

  1. 消除价值模型:用组统计量替代价值网络,减少一个模型
  2. 消除奖励模型(在 RLVR 场景下):使用可验证的正确性奖励,再减少一个模型
  3. 保留在线探索:仍然从当前策略采样,保持 RL 的探索优势

最终,GRPO 将四模型架构简化为二模型(策略 + 参考),同时保留了 PPO 的核心优势。我们将在下一节详细介绍 GRPO。

PPO 在 RLHF 中的完整伪代码

# RLHF with PPO - 伪代码
def rlhf_ppo_training(
    policy_model,      # π_θ: 策略模型
    ref_model,         # π_ref: 参考模型(冻结)
    reward_model,      # r_φ: 奖励模型(冻结)
    value_model,       # V_ψ: 价值模型
    prompts,           # 训练提示集
    beta=0.01,         # KL 惩罚系数
    epsilon=0.2,       # PPO 截断参数
    num_steps=1000,
):
    for step in range(num_steps):
        # 1. 采样:从策略模型生成回复
        prompt = sample(prompts)
        response = policy_model.generate(prompt)

        # 2. 评估:计算奖励和 KL 惩罚
        reward = reward_model(prompt, response)
        kl_penalty = compute_kl(policy_model, ref_model, prompt, response)
        total_reward = reward - beta * kl_penalty

        # 3. 优势计算:使用价值模型和 GAE
        values = value_model(prompt, response)  # 每个 token 的价值估计
        advantages = compute_gae(total_reward, values)

        # 4. PPO 更新:截断代理目标
        ratio = policy_model.log_prob(response) - old_log_prob(response)
        ratio = ratio.exp()
        clipped_ratio = ratio.clamp(1 - epsilon, 1 + epsilon)
        policy_loss = -min(ratio * advantages, clipped_ratio * advantages).mean()

        # 5. 价值模型更新
        value_loss = (values - returns).pow(2).mean()

        # 6. 参数更新
        policy_model.update(policy_loss)
        value_model.update(value_loss)