GRPO、Dr. GRPO、DAPO 原来都是同一个公式的变体
训练推理模型的朋友大概都撞过这个画面:G=8 跑起来,某些问题收敛得很顺,换个数据集 loss 直接横盘。有人说是 GRPO 归一化的问题,有人建议换 Dr. GRPO,还有人吹 DAPO 动态采样多厉害——但没人说清楚为什么。
三套算法其实就是同一个公式的三种 σ 关系,谈不上谁更"高级",只是对难度梯度的处理哲学不同。
下一篇
Documan:用AI把需求管理从Excel泥潭里捞出来 →
UIUC 那篇 arXiv:2607.00152 把三者的关系捅破了:它们全在操作同一个数,组内奖励标准差 σ。对二元奖励(对=1,错=0),每个 prompt 的梯度更新长这样:
g = σ · (s̄₊ - s̄₋)σ = √(k(G-k)/G),s̄₊ 是正确回答的平均 log 概率,s̄₋ 是错误回答的。三种算法的区别就一个:怎么处理 σ。
三者的本质差异用加粗列表拆开看——
- GRPO: 用 g/σ = s̄₊ - s̄₋。除以 σ 等于乘了 1/√(p(1-p)),这就是 arcsin(√p) 的导数。难题(p=0.05)比中等难度(p=0.5)多拿约 10 倍的梯度,天然带难度偏置。
- Dr. GRPO: 直接用 g = σ(s̄₊ - s̄₋),保留 σ 做自然难度加权,不做归一化。
- DAPO: σ=0 的组直接丢掉。当 p=0.05 且 G=8 时,整批全错的概率是 (0.95)^8 ≈ 66%。这批数据梯度为零,纯浪费算力,DAPO 选择跳过。
三套算法其实就是同一个公式的三种 σ 关系,谈不上谁更"高级",只是对难度梯度的处理哲学不同。
代码我也整理了一份,核心函数就这三个:
import numpy as np
import torch
# The three algorithms — all variations on σ
def grpo_advantage(rewards: torch.Tensor, eps: float = 1e-8) -> torch.Tensor:
"""Divide by σ → arcsine-transform weighting (amplifies hard/easy problems)."""
mean_r = rewards.float().mean()
std_r = rewards.float().std() + eps
return (rewards.float() - mean_r) / std_r
def dr_grpo_advantage(rewards: torch.Tensor) -> torch.Tensor:
"""No σ division → natural difficulty weighting."""
return rewards.float() - rewards.float().mean()
def dapo_advantage(rewards: torch.Tensor, eps: float = 1e-8):
"""Discard silent groups (σ=0), then apply GRPO."""
k = rewards.sum().item()
G = rewards.shape[0]
if k == 0 or k == G:
return None # Caller should skip this batch
return grpo_advantage(rewards, eps)
# 两个实际用得上的公式
def silent_group_rate(p: float, G: int) -> float:
"""Fraction of batches that produce zero gradient (wasted compute)."""
return p**G + (1 - p)**G
def min_group_size(p: float, epsilon: float = 0.05) -> int:
"""Minimum G for 1-ε statistical fidelity (Group-Size Law)."""
return int(np.ceil(1 / (8 * epsilon * p * (1 - p))))两个公式直接背走:silent_group_rate 算你训练时到底有多少 batch 在静默浪费算力,min_group_size 告诉你按当前通过率应该设多大 G。
实操建议是开训前先做一次语料审计:如果静默率超过 20%,就把 G 按 Group-Size Law 往上提。这篇把以前靠感觉调的超参数变成了可计算的东西,算是一个真正有用的理论工具。