Loading...
正在加载...
请稍候

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

✨步子哥 (steper) 2026年03月12日 01:20
## 一、核心概念与演进脉络 这三种方法是 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 训练流程 ```python # 伪代码:完整 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 数据格式与代码 ```python # 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 代码实现 ```python # 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 引入全局合作约束 --- ## 五、方法选型决策树 ```mermaid 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. **读原始论文**: - PPO: [Proximal Policy Optimization Algorithms](https://arxiv.org/abs/1707.06347) (Schulman et al., 2017) - DPO: [Direct Preference Optimization](https://arxiv.org/abs/2305.18290) (Rafailov et al., 2023) - GRPO: [DeepSeekMath](https://arxiv.org/abs/2402.03300) (Shao et al., 2024) ### Level 2:代码实践(2-3 周) 1. **手撕 PPO**:用 PyTorch 实现简化版 PPO,理解 GAE 计算 2. **DPO 微调**:使用 `trl` 或 `llama-factory` 对 7B 模型进行 DPO 训练 3. **GRPO 实验**:在 GSM8K 数学数据集上复现 GRPO,对比 PPO 的显存占用 ### Level 3:进阶优化(持续) - **混合策略**:先用 DPO 预热,再用 GRPO 强化(DeepSeek-R1 策略) - **长度归一化**:解决 DPO/GRPO 的长度偏见(Length Bias) - **迭代训练**:Online DPO / Iterative GRPO,使用模型自生成数据迭代优化 ### 推荐资源 - **代码库**:[huggingface/trl](https://github.com/huggingface/trl)(包含 PPO/DPO/GRPO 实现) - **可视化理解**:[The Illustrated PPO](https://iclr-blog-track.github.io/2022/03/25/ppo-illustrated/) - **实践教程**:MiniMind 项目的 [train_ppo.py](https://github.com/jingyaogong/minimind/blob/master/train_ppo.py) 实现 --- **总结**:PPO 是通用强大的"重武器",DPO 是简洁优雅的"轻量级方案",GRPO 则是针对可验证任务的"高效专精工具"。掌握三者的差异和联系,能根据任务特性灵活选择,是 LLM 对齐工程师的核心能力。

讨论回复

0 条回复

还没有人回复,快来发表你的看法吧!