概述
Group Relative Policy Optimization (GRPO) 已成为训练推理模型(如DeepSeek-R1)的核心算法。本文介绍四种前沿扩展,分别针对特定场景优化:
| 方法 | 核心关注 | 关键创新 | 效率提升 |
|---|---|---|---|
| Latent-GRPO | 隐式空间RL | 流形感知优化 | 质量提升 |
| SPPO | 长程推理 | 序列级 bandit | 5.9× |
| BPPO | 训练效率 | 二值前缀选择 | 6.08× |
| LamPO | 信用分配 | 成对优势分解 | ~1.5× |
1. Latent-GRPO:隐式推理的GRPO
论文:arXiv:2604.27998
1.1 问题背景
隐式推理将中间推理步骤压缩到连续表示中,显著缩短推理链。但直接将GRPO应用于隐式空间会因概率密度和采样机制的差异而失败。
1.2 三个核心瓶颈
1. 内在隐式流形的缺失
无约束探索会推动rollouts离开有效流形,产生无法解码或正确评估的无效状态。
2. 探索-优化不对齐
轨迹级奖励(二值成功/失败)导致不正确的token级更新,模型无法区分哪些中间决策贡献了最终结果。
3. 隐式混合非闭合
当共同强化多条正确隐式路径时,对其平均化会产生无效的混合状态——与”正确路径的混合应该有效”的期望相矛盾。
1.3 关键创新
无效样本优势掩码
for each_sample in batch:
if is_valid_latent(sample): # 检查流形有效性
compute_advantage(sample)
else:
mask_advantage(sample) # 清零无效样本的优势单侧噪声采样
只在保持流形有效性的方向添加噪声:
最优正确路径首token选择
当存在多条正确推理路径时,选择首token最优的路径:
correct_paths = [p for p in paths if p.reward == 1]
if correct_paths:
optimal_path = min(correct_paths, key=lambda p: p.first_token_score)
use_only(optimal_path)1.4 实验结果
| 基准类别 | 指标 | Latent-GRPO vs 隐式基线 | Latent-GRPO vs 显式GRPO |
|---|---|---|---|
| 低难度 (GSM8K等) | Pass@1 | +7.86分 | - |
| 高难度 (AIME) | Pass@1 | - | +4.27分 |
| 链长度 | 压缩率 | - | 3-4×更短 |
2. SPPO:序列级PPO
论文:arXiv:2604.08865,ACL 2026
2.1 问题背景
标准token级PPO在处理长链思维推理时存在:
- “尾部效应”:价值函数仅在序列末端附近区分正确/错误轨迹,中间步骤的优势信号噪声大或消失
- 内存成本:完整token级价值模型对大语言模型需要大量内存
- 多采样开销:GRPO通过每prompt采样N>1实现稳定,限制了吞吐量
2.2 核心洞察
GRPO的成功源于隐式地将推理视为序列级上下文 bandit。
整个推理链本质上是一个原子动作;prompt是上下文,最终的二值奖励对动作进行整体评估。
2.3 序列级上下文Bandit重构
标准PPO MDP:
状态:s_t(位置t的token)
动作:y_t(下一个token)
奖励:仅终止时(r_T)
SPPO Bandit形式:
上下文:s_p(仅prompt)
动作:o(整个响应序列)
奖励:仅终止时(r ∈ {0, 1})
视野:1(概念上)
2.4 标量价值函数
SPPO使用单一标量critic而非token级价值:
通过二元交叉熵训练:
2.5 实验结果
模型:DeepSeek-R1-Distill-Qwen-7B
| 方法 | AIME24 | AIME25 | AMC23 | MATH500 | Minerva | 平均 |
|---|---|---|---|---|---|---|
| Base | 45.20 | 35.42 | 85.31 | 88.48 | 27.80 | 56.44 |
| PPO | 45.20 | 35.42 | 85.31 | 88.48 | 27.80 | 56.44 |
| GRPO N=8 | 47.08 | 35.00 | 86.25 | 90.15 | 28.74 | 57.44 |
| SPPO | 50.83 | 35.00 | 86.25 | 90.13 | 28.35 | 58.11 |
| SPPO + 1.5B | 52.29 | 34.58 | 87.19 | 89.88 | 28.86 | 58.56 |
训练效率:
- SPPO在约22小时内达到峰值性能,而GRPO显著更长
- 5.9×加速(相比N=8的GRPO)
3. BPPO:二值前缀策略优化
论文:arXiv:2605.28028
3.1 问题背景
GRPO更新每组采样的所有completion,产生:
- 大量计算成本 — 每prompt处理G个响应
- 冗长推理的强化 — 更长响应主导梯度更新
- 冗余学习信号 — 同类completion(全部正确或全部错误)产生相似梯度
3.2 关键观察:梯度相似性
在同一prompt组内:
- 同类completion通常产生高度相似的更新方向
- 正确-错误对提供更多不同的对比信号
3.3 关键创新
1. 二值前缀选择
不更新所有G个completion,而是选择两个代表性样本:
- 最短正确completion ()
- 最短错误completion ()
GRPO更新: ∇θ Σ_{k=1}^{G} f(θ, o_k)
BPPO更新: ∇θ [f(θ, o_c^{min}) + f(θ, o_i^{min})]
2. 自适应完成调度
if current_accuracy < threshold:
sample_more_completions() # 需要多样性信号
else:
use_binary_prefix() # 使用对比对微调3. 前缀聚焦优化
不更新整个响应,而是聚焦于前缀:
# 只更新响应前缀(前T个token)
for response in [shortest_correct, shortest_incorrect]:
prefix = response[:prefix_length] # 通常30-50%的响应
compute_gradient(prefix)3.4 实验结果
基准:GSM8K, MATH, Geo3K
| 方法 | 相对GRPO加速 | 准确率 | 响应长度 |
|---|---|---|---|
| GRPO | 1× (基线) | 基线 | 基线 |
| BPPO | 6.08× | 持平 | 缩短30-50% |
4. LamPO:Lambda风格策略优化
论文:arXiv:2605.19416(已撤回,内容仍可获取)
4.1 问题背景
GRPO的标量基线(组均值)引入关系瓶颈:
- 信息丢失:轨迹间丰富的关系拓扑被压缩为两个标量矩(μ, σ)
- 排列不变性:基线不区分哪些同伴表现更好或更差
- 高频抑制:细粒度排序和非对称性能差距被压制
4.2 核心洞察:LambdaRank启发
借鉴Learning-to-Rank文献(LambdaRank),LamPO将标量优势分解为成对比较:
不说:“这个轨迹比均值高0.5个标准差”
LamPO计算:“这个轨迹比同伴A、B、C(不同地)好,但比D差”
4.3 关键创新
成对分解优势(PDA)
对于组内轨迹 :
其中:
- :轨迹间的奖励差
- :策略置信度差
- :置信度加权
- :温度超参数
4.4 实验结果
基准:AIME24, AIME25, MATH-500, GPQA-Diamond
| 方法 | AIME24 | MATH-500 | GPQA | 训练稳定性 |
|---|---|---|---|---|
| GRPO | 基线 | 基线 | 基线 | 中等 |
| LamPO | 更高 | 更高 | 更高 | 更稳定 |
5. 综合对比分析
5.1 方法对比矩阵
| 方面 | Latent-GRPO | SPPO | BPPO | LamPO |
|---|---|---|---|---|
| 主要目标 | 隐式空间RL | 长程稳定性 | 效率+简洁 | 细粒度信用 |
| 核心创新 | 流形感知优化 | 序列级bandit | 二值前缀选择 | 成对分解 |
| 理论基础 | 隐式变量模型 | 上下文bandit | 梯度相似性 | LambdaRank |
| 样本效率 | 取决于编码器 | N=1 | 每组2个 | G个比较 |
| 内存效率 | 类似GRPO | 减少12.8% | 减少50%+ | 类似GRPO |
| 输出长度 | 缩短3-4× | 类似 | 缩短30-50% | 类似 |
| 训练稳定性 | 解决特定故障模式 | 高 | 高 | 改善 |
5.2 与原始GRPO的关系
| 方法 | 如何扩展GRPO |
|---|---|
| Latent-GRPO | 适配连续隐式空间的流形约束 |
| SPPO | 使GRPO的隐式bandit结构显式化 |
| BPPO | 通过智能采样减少GRPO的计算开销 |
| LamPO | 将GRPO的标量基线丰富为关系结构 |
6. 代码实现
6.1 SPPO伪代码
#include <bits/stdc++.h>
using namespace std;
struct PolicyUpdate {
vector<int> tokens;
double log_prob;
int reward; // 0 or 1
};
double sppo_update(
Policy& actor,
Critic& critic,
const vector<int>& prompt,
const PolicyUpdate& response,
double epsilon = 0.2
) {
// 1. 序列级优势
double value_estimate = critic.forward(prompt); // V(s_p)
double advantage = response.reward - value_estimate;
// 2. 更新critic(使用BCE)
double critic_loss = -(
response.reward * log(value_estimate + 1e-8) +
(1 - response.reward) * log(1 - value_estimate + 1e-8)
);
critic.backward(critic_loss);
// 3. 更新actor(使用裁剪PPO,但优势是序列级的)
double total_loss = 0.0;
for (int i = 0; i < response.tokens.size(); i++) {
double ratio = actor.log_prob(response.tokens[i]) /
old_actor.log_prob(response.tokens[i]);
double clipped = clamp(ratio, 1 - epsilon, 1 + epsilon);
double token_loss = -min(
ratio * advantage, // 序列级优势应用于所有token
clipped * advantage
);
total_loss += token_loss;
}
actor.backward(total_loss);
return total_loss;
}6.2 BPPO伪代码
struct BPPOConfig {
int group_size = 16;
double epsilon = 0.2;
double prefix_ratio = 0.4; // 使用前40%的token
};
double bppo_update(
Policy& policy,
RewardModel& reward_model,
const vector<int>& prompt,
const BPPOConfig& config
) {
// 1. 采样完整组
vector<PolicyUpdate> responses;
vector<double> rewards;
for (int i = 0; i < config.group_size; i++) {
auto response = policy.sample(prompt);
double reward = reward_model.check(response);
responses.push_back(response);
rewards.push_back(reward);
}
// 2. 计算组统计量(用于归一化)
double mu = accumulate(rewards.begin(), rewards.end(), 0.0) / rewards.size();
double sigma = 0.0;
for (double r : rewards) sigma += (r - mu) * (r - mu);
sigma = sqrt(sigma / rewards.size()) + 1e-8;
// 3. 选择二值对
vector<PolicyUpdate*> correct, incorrect;
for (int i = 0; i < responses.size(); i++) {
if (rewards[i] == 1.0) correct.push_back(&responses[i]);
else incorrect.push_back(&responses[i]);
}
// 找最短的
auto shortest = [](const vector<PolicyUpdate*>& v) {
return *min_element(v.begin(), v.end(),
[](PolicyUpdate* a, PolicyUpdate* b) {
return a->tokens.size() < b->tokens.size();
});
};
PolicyUpdate* shortest_correct = correct.empty() ? nullptr : shortest(correct);
PolicyUpdate* shortest_incorrect = incorrect.empty() ? nullptr : shortest(incorrect);
// 4. 计算梯度
double total_loss = 0.0;
auto update_on_prefix = [&](PolicyUpdate* resp, bool is_correct) {
if (!resp) return;
// 计算前缀长度
int prefix_len = int(resp->tokens.size() * config.prefix_ratio);
// 计算优势
double adv = is_correct ? (1.0 - mu) / sigma : (0.0 - mu) / sigma;
// 计算前缀上的损失
for (int i = 0; i < prefix_len; i++) {
double ratio = policy.log_prob(resp->tokens[i]) /
old_policy.log_prob(resp->tokens[i]);
double clipped = clamp(ratio, 1 - config.epsilon, 1 + config.epsilon);
double loss = -min(ratio * adv, clipped * adv);
total_loss += loss;
}
};
update_on_prefix(shortest_correct, true);
update_on_prefix(shortest_incorrect, false);
return total_loss;
}7. 选择指南
使用决策树
你的模型使用隐式/连续表示吗?
├─ 是 → Latent-GRPO
└─ 否 ↓
训练效率是主要约束吗?
├─ 是 → BPPO(如果同时需要简洁性)
└─ 否 ↓
你有长推理链(>1000 token)吗?
├─ 是 → SPPO(稳定性和内存效率)
└─ 否 ↓
细粒度信用分配重要吗?
├─ 是 → LamPO
└─ 否 → GRPO(标准)
各方法最佳场景
| 方法 | 最佳使用场景 |
|---|---|
| Latent-GRPO | 隐式推理模型、推理链压缩、有限推理成本 |
| SPPO | 长链思维推理、有限GPU内存、高吞吐量需求 |
| BPPO | 训练效率至关重要、减少响应冗长、推理成本约束 |
| LamPO | 排名敏感任务、细粒度信用分配、AIME等难题微调 |