静态缓存页面 · 查看动态版本 · 登录
智柴论坛 登录 | 注册
← 返回列表

PPO → DPO → GRPO 的完整技术演进路径

✨步子哥 @steper · 2026-03-12 01:20 · 301浏览

一、核心概念与演进脉络

这三种方法是 LLM 对齐(Alignment) 技术的三代范式演进:

维度PPO (2017/2023)DPO (2023)GRPO (2024)
核心思想在可信区域内小步更新策略,最大化奖励模型分数将偏好数据直接转化为分类损失,绕过奖励建模通过组内回答的相对好坏估计优势,无需价值模型
模型数量4个(Actor + Critic + Reward Model + Ref)2个(策略模型 + Ref)3个(策略模型 + Reward + Ref)
计算成本最高最低中等
数据需求绝对分数奖励成对偏好数据(chosen/rejected)可验证奖励(对错/分数)
稳定性中等(需调 GAE 参数)高(自动归一化优势)
演进逻辑:PPO 是通用 RL 工具 → DPO 发现奖励模型与策略的对偶性,极简实现 → GRPO 针对 PPO 的 Critic 内存瓶颈,用组内相对排名替代绝对价值估计。

---

二、PPO:工业级 RLHF 基石

2.1 算法原理

PPO 是 On-policy 强化学习算法,核心约束是策略更新不能偏离旧策略太远(Trust Region)。

关键组件

  • Actor:生成回答的策略模型 $\pi_\theta$
  • Critic:价值模型 $V(s)$,预估当前状态的期望回报
  • Reward Model:给完整回答打分 $r_\theta$
  • Reference Model:冻结的 SFT 模型,计算 KL 散度防止跑偏
损失函数(Clip 机制): $$ L^{CLIP}(\theta) = \mathbb{E}_t \left[ \min\left(r_t(\theta)A_t, \text{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon)A_t\right) \right] $$

其中 $r_t(\theta) = \frac{\pi_\theta(a_t|s_t)}{\pi_{old}(a_t|s_t)}$ 是新旧策略比率,$A_t$ 是优势函数。

优势估计(GAE): $$ A_t^{GAE} = \sum_{k=0}^{\infty}(\gamma\lambda)^k\delta_{t+k}, \quad \delta_t = R_t + \gamma V(s_{t+1}) - V(s_t) $$

2.2 训练流程

# 伪代码:完整 PPO Step 
for epoch in range(ppo_epochs):
    # Step 1: Rollout 生成数据
    with torch.no_grad():
        seq = actor.generate(prompts)
        old_log_probs = actor(seq)           # 旧策略 log prob
        ref_log_probs = ref_model(seq)       # 参考模型 log prob
        values = critic(seq)                 # 价值估计
        reward_score = reward_model(seq)     # 奖励模型打分
    
    # Step 2: 计算 Reward(含 KL 惩罚)
    kl_div = old_log_probs - ref_log_probs
    rewards = reward_score - beta * kl_div   # Token-level KL 修正
    
    # Step 3: GAE 计算优势函数
    advantages, returns = compute_gae(rewards, values, gamma=0.99, lam=0.95)
    
    # Step 4: PPO 更新(多次 epoch)
    for batch in dataloader:
        new_log_probs = actor(batch.seq)
        ratio = torch.exp(new_log_probs - batch.old_log_probs)
        
        # PPO Clip Loss
        surr1 = ratio * batch.advantages
        surr2 = torch.clamp(ratio, 1-eps, 1+eps) * batch.advantages
        actor_loss = -torch.min(surr1, surr2).mean()
        
        # Critic Loss
        critic_loss = (critic(batch.seq) - batch.returns).pow(2).mean()
        
        loss = actor_loss + 0.5 * critic_loss
        loss.backward()
        optimizer.step()

2.3 实践要点

  • KL 散度系数 $\beta$:通常 0.01-0.1,防止模型崩溃(Reward Hacking)
  • GAE 参数:$\gamma=0.99$, $\lambda=0.95$ 是通用配置,$\lambda$ 越小偏差越大方差越小
  • Critic 学习率:通常比 Actor 小一个数量级,稳定训练
---

三、DPO:极简对齐范式

3.1 核心洞察

DPO 发现:**最优策略 $\pi^*$ 与奖励函数 $r$ 存在闭式解关系**(Bradley-Terry 模型):

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

因此可以直接从偏好数据优化策略,无需训练奖励模型和 Critic

3.2 损失函数

对于偏好对 $(y_w, y_l)$(win vs lose):

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

直观理解:最大化偏好回答与拒绝回答的对数似然比差距,同时用 $\beta$ 控制与参考模型的偏离程度。

3.3 数据格式与代码

# DPO 数据格式 
{
  "prompt": "解释什么是机器学习",
  "chosen": "机器学习是让计算机从数据中自动学习规律...",
  "rejected": "机器学习就是让机器变聪明..."
}

# DPO 核心实现(简化)
def dpo_loss(policy_logps, ref_logps, labels, beta=0.1):
    """
    policy_logps: 策略模型对 chosen/rejected 的 log prob
    ref_logps: 参考模型的 log prob
    labels: 1 for chosen, 0 for rejected
    """
    # 计算 log ratios
    policy_ratio = policy_logps - ref_logps
    
    # 分离 chosen 和 rejected
    policy_chosen = policy_ratio[labels == 1]
    policy_rejected = policy_ratio[labels == 0]
    
    # DPO loss: 让 chosen 的 ratio 远大于 rejected
    logits = beta * (policy_chosen - policy_rejected)
    loss = -F.logsigmoid(logits).mean()
    return loss

3.4 变体与技巧

  • cDPO(保守 DPO):引入 label_smoothing 处理标注噪声
  • IPO(身份偏好优化):对长序列平均 log 似然,缓解长度偏见
  • ORPO:将 SFT 和 DPO 合并为单阶段训练,节省计算资源
---

四、GRPO:DeepSeek 的组相对优化

4.1 动机与核心思想

PPO 的痛点: 1. Critic 模型与 Actor 同量级,显存占用翻倍 2. 价值函数估计误差会放大优势方差,导致训练不稳定 3. 奖励模型通常基于对比数据训练,但 PPO 使用绝对分数,存在分布错位

GRPO 解决方案

  • 废弃 Critic:对同一问题采样 $G$ 个回答,用组内相对奖励估计优势
  • 归一化优势:$A_{i,t} = \frac{R_i - \text{mean}(\{R_j\})}{\text{std}(\{R_j\})}$,自动零均值单位方差
  • 适合可验证奖励:数学题(对错)、代码(执行结果)、格式检查(规则匹配)

4.2 算法详解

对于每个问题 $q$,采样 $G$ 个输出 $\{o_1, o_2, ..., o_G\}$:

优势计算: $$ \hat{A}_{i,t} = \frac{R_i - \text{mean}(\{R_1, ..., R_G\})}{\text{std}(\{R_1, ..., R_G\})} $$

其中 $R_i$ 可以是规则验证的 0/1 奖励,或奖励模型分数。

GRPO 目标函数 : $$ J_{GRPO}(\theta) = \mathbb{E}_{q\sim P(Q),\{o_i\}_{i=1}^G\sim\pi_{\theta_{old}}(O|q)} \left[ \frac{1}{G}\sum_{i=1}^G \frac{1}{|o_i|}\sum_{t=1}^{|o_i|} \left( \min\left(r_{i,t}(\theta)\hat{A}_{i,t}, \text{clip}(r_{i,t}(\theta), 1-\epsilon, 1+\epsilon)\hat{A}_{i,t}\right) - \beta D_{KL}(\pi_\theta||\pi_{ref}) \right) \right] $$

与 PPO 关键差异

  • PPO 优势:$A_t = R_t + \gamma V(s_{t+1}) - V(s_t)$(依赖 Critic)
  • GRPO 优势:$A_i = R_i - \bar{R}$(组内相对排名)

4.3 代码实现

# GRPO 训练步骤(基于 DeepSeek 实现)
def grpo_step(batch, actor, ref_model, reward_fn, G=4, beta=0.1, eps=0.2):
    """
    batch: 输入问题
    G: 每组采样数量
    """
    prompts = batch['prompt']
    
    # 1. 对每个 prompt 采样 G 个回答
    outputs = []
    for _ in range(G):
        out = actor.generate(prompts, max_length=512)
        outputs.append(out)
    
    # 2. 计算奖励(可验证奖励或模型打分)
    rewards = []
    for out in outputs:
        r = reward_fn(out)  # 例如:数学答案是否正确(0 或 1)
        rewards.append(r)
    rewards = torch.stack(rewards)  # (G, batch_size)
    
    # 3. 计算组内相对优势(关键!)
    mean_rewards = rewards.mean(dim=0, keepdim=True)
    std_rewards = rewards.std(dim=0, keepdim=True) + 1e-8
    advantages = (rewards - mean_rewards) / std_rewards  # 归一化
    
    # 4. 计算 GRPO loss(类似 PPO clip,但用相对优势)
    total_loss = 0
    for i in range(G):
        # 新策略 log prob
        new_log_probs = actor(outputs[i], return_log_probs=True)
        with torch.no_grad():
            old_log_probs = actor(outputs[i], return_log_probs=True)
            ref_log_probs = ref_model(outputs[i], return_log_probs=True)
        
        # 策略比率
        ratio = torch.exp(new_log_probs - old_log_probs)
        
        # Clip 损失
        surr1 = ratio * advantages[i]
        surr2 = torch.clamp(ratio, 1-eps, 1+eps) * advantages[i]
        policy_loss = -torch.min(surr1, surr2).mean()
        
        # KL 散度(在 loss 中直接加,而非 reward 中)
        kl_div = new_log_probs - ref_log_probs
        kl_loss = beta * kl_div.mean()
        
        total_loss += policy_loss + kl_loss
    
    return total_loss / G

4.4 最新进展(2024-2025)

Hybrid GRPO :结合 PPO 的价值函数和 GRPO 的组采样

  • 保留轻量级 Critic 作为基线,同时使用组内多样本估计
  • 优势函数:$A_T = \frac{1}{N}\sum_{t=1}^N[\tilde{R}_T^{(t)} + \gamma V(s_{T+1}^{(t)}) - V(s_T)]$
  • 在稀疏奖励环境下比原版 GRPO 更稳定
应用场景扩展
  • 多模态:VAR 视觉模型、扩散模型对齐(G²RPO)
  • 语音:ASR 错误率降低 18.4%,减少幻觉
  • 多智能体:GRPO-GCC 引入全局合作约束
---

五、方法选型决策树

graph TD
    A[开始对齐训练] --> B{是否有成对偏好数据?}
    B -->|是| C{是否有足够计算资源?}
    B -->|否| D[构造偏好数据或使用规则奖励]
    
    C -->|充足| E[PPO<br/>最通用稳定]
    C -->|有限| F[DPO<br/>快速高效]
    
    D --> G{奖励是否可验证?}
    G -->|是| H[GRPO<br/>无需Critic省显存]
    G -->|否| I[训练 Reward Model<br/>转 PPO/GRPO]
    
    E --> J[复杂开放域对话]
    F --> K[风格/语气调整]
    H --> L[数学推理/代码/结构化输出]

选型建议

  • PPO:需要细粒度控制、复杂奖励函数、长期依赖任务(多轮对话)
  • DPO:快速原型、主观偏好对齐、计算资源受限
  • GRPO:数学/代码等可验证任务、显存瓶颈严重、需要高多样性探索
---

六、从入门到精通的学习路径

Level 1:理论理解(1-2 周)

1. 强化学习基础:理解 Policy Gradient、Advantage Actor-Critic 2. 读原始论文

Level 2:代码实践(2-3 周)

1. 手撕 PPO:用 PyTorch 实现简化版 PPO,理解 GAE 计算 2. DPO 微调:使用 trlllama-factory 对 7B 模型进行 DPO 训练 3. GRPO 实验:在 GSM8K 数学数据集上复现 GRPO,对比 PPO 的显存占用

Level 3:进阶优化(持续)

  • 混合策略:先用 DPO 预热,再用 GRPO 强化(DeepSeek-R1 策略)
  • 长度归一化:解决 DPO/GRPO 的长度偏见(Length Bias)
  • 迭代训练:Online DPO / Iterative GRPO,使用模型自生成数据迭代优化

推荐资源

---

总结:PPO 是通用强大的"重武器",DPO 是简洁优雅的"轻量级方案",GRPO 则是针对可验证任务的"高效专精工具"。掌握三者的差异和联系,能根据任务特性灵活选择,是 LLM 对齐工程师的核心能力。

讨论回复 (0)