Skip to content

GRPO / Dr.GRPO:不用 critic 的分组相对优势

GRPO 很适合 RLVR 场景:同一道题采样多条 response,用可验证 reward 判断谁更好。它不训练 critic,而是让同一个 prompt 下的多条 response 互相当参照系。

小白可以这样想:一道数学题采样 8 个答案,答对的 reward 高,答错的 reward 低。GRPO 不问“这个答案绝对值是多少”,而问“它比同题其他答案好多少”。

核心公式

对同一个 prompt 的一组 response,先得到每条 response 的总 reward:

Ri=tri,t

原始 GRPO 使用组内均值和标准差:

Ai=Riμgσg+ϵ

其中 g 表示同一个 prompt 的 group。Dr.GRPO 常用的关键变化之一是关掉标准差归一:

Ai=Riμg

在 verl 里,这个开关是:

text
algorithm.norm_adv_by_std_in_grpo=True   # GRPO
algorithm.norm_adv_by_std_in_grpo=False  # Dr.GRPO-style

GRPO 得到的是 response 级别的优势标量,最后会乘 response_mask 广播到每个有效 token:

Ai,t=Ai1{t 是有效 response token}

源码实现怎么读

1. 分组 id 从哪里来

GRPO 的分组不是“batch 里连续 8 条就是一组”这么脆弱。RayPPOTrainer.fit() 会先给每个原始 prompt 写入 uid,然后 rollout 前后都按 actor_rollout_ref.rollout.n repeat:

text
raw prompts:      uid = [u1, u2, ...]
repeat n times:   [u1, u1, ..., u2, u2, ...]
rollout outputs:  每个 uid 对应多条 response

后面 compute_grpo_outcome_advantage()data.non_tensor_batch["uid"] 分组。所以你改数据流时,一旦丢了 uid,GRPO 的“同题比较”就坏了。

2. compute_grpo_outcome_advantage() 的变量表

源码变量公式符号含义
token_level_rewardsr_{i,t}每条 response 每个 token 的 reward
scores = token_level_rewards.sum(-1)R_i每条 response 的总 reward
indexg(i)分组 key,也就是 uid
id2mean[index[i]]mu_g同 prompt 组内均值
id2std[index[i]]sigma_g同 prompt 组内标准差
response_maskmask有效 response token
scores.unsqueeze(-1) * response_maskA_{i,t}把 response 级优势广播到 token

伪代码如下:

text
scores = sum(token_level_rewards over response tokens)
for each uid:
    mean = average(scores in this uid)
    std = std(scores in this uid)
for each response i:
    if norm_adv_by_std_in_grpo:
        adv_i = (score_i - mean_uid) / (std_uid + eps)
    else:
        adv_i = score_i - mean_uid
advantages = adv_i[:, None] * response_mask
returns = advantages

compute_grpo_vectorized_outcome_advantage() 做的是同一件事,只是用 group_mean_std() 向量化,适合大 batch。

3. trainer 入口:compute_advantage()

ray_trainer.compute_advantage() 中,GRPO 分支只传入:

text
token_level_rewards=data.batch["token_level_rewards"]
response_mask=data.batch["response_mask"]
index=data.non_tensor_batch["uid"]
norm_adv_by_std_in_grpo=...

它不会传 values,也不会训练 critic。returns 直接等于 advantages,这就是 GRPO critic-free 的实现痕迹。

GRPO 和 PPO 的关系

GRPO 仍然常用 PPO-style clipped policy loss 更新 actor。区别主要在 advantage 来源:

维度PPO + GAEGRPO
critic需要 value model通常不需要
advantagereward + value 递推同 prompt 多 response 的相对 reward
reward 形态dense 或 outcome 都可通常是 outcome reward
关键配置adv_estimator=gaeadv_estimator=grpo
工程重点critic 稳定性uidrollout.n、组内 reward 方差

所以 GRPO 不是“没有 PPO loss”。更准确地说:GRPO 用 group-relative advantage,actor loss 仍可走 compute_policy_loss_vanilla() 或其他 policy loss。

Dr.GRPO 在 verl 里怎么看

官方 docs 建议 Dr.GRPO-style 关注三处:

text
algorithm.norm_adv_by_std_in_grpo=False
actor_rollout_ref.actor.loss_agg_mode=seq-mean-token-sum-norm
actor_rollout_ref.actor.use_kl_loss=False

其中:

  • norm_adv_by_std_in_grpo=False:不除组内标准差,避免标准差归一带来的某些长度和难度偏置。
  • seq-mean-token-sum-norm:先按 response 求和,再除固定 horizon 或 loss_scale_factor,减少长短 response 因 token 数不同带来的权重偏差。
  • use_kl_loss=False:一些 Dr.GRPO/R1-Zero-like recipe 会关闭 actor KL loss,但这不是“所有 GRPO 必须关闭 KL”。

配置里先看什么

GRPO 示例脚本集中在 examples/grpo_trainer/。常见关键项是:

text
algorithm.adv_estimator=grpo
algorithm.use_kl_in_reward=False
actor_rollout_ref.actor.use_kl_loss=True
actor_rollout_ref.rollout.n=${ROLLOUT_N}
actor_rollout_ref.actor.clip_ratio_low=0.2
actor_rollout_ref.actor.clip_ratio_high=0.28  # 一些大模型/DAPO-style 脚本会这样设

actor_rollout_ref.rollout.n > 1 是算法条件,不只是吞吐参数。如果 n=1,组内比较几乎没有信息,源码里单样本 group 会退化成均值 0、标准差 1。

哪些地方不适合初学者硬啃

  • 不要把 rollout.n 当成普通并行采样参数。它决定每个 prompt 有多少 siblings 可以比较。
  • 不要把 token_level_rewards.sum(-1) 理解成“每个 token 都有 dense reward”。很多 RLVR reward 只有最后一个 token 非零,sum 只是取 outcome。
  • 不要只看 adv_estimator=grpo 就以为所有 GRPO 配置一样。KL 放 reward 侧还是 actor loss 侧、loss aggregation 怎么做,会显著改变训练行为。
  • 不要忽略 reward 方差。如果一个 group 全对或全错,优势接近无学习信号,DAPO 的 dynamic sampling 就是在处理这个问题。

本节参考与延伸阅读

  • verl 源码:verl/trainer/ppo/core_algos.pycompute_grpo_outcome_advantage()compute_grpo_vectorized_outcome_advantage()agg_loss()
  • verl 源码:verl/trainer/ppo/ray_trainer.pycompute_advantage()RayPPOTrainer.fit()uidrepeat(rollout.n)、reward、KL、advantage 相关上下文。
  • verl 官方文档:docs/algo/grpo.md,尤其 GRPO 配置和 Dr.GRPO 配置。
  • 示例脚本:examples/grpo_trainer/README.mdexamples/grpo_trainer/run_qwen3_8b_fsdp.shrun_qwen3_30b_a3b_megatron.shrun_deepseek_v3_671b_megatron.sh
  • 论文:DeepSeekMath: Pushing the Limits of Mathematical Reasoning in Open Language Models
  • 论文:Understanding R1-Zero-Like Training: A Critical Perspective

面向源码阅读的 verl 学习文档。