Skip to content

GRPO 改进系列

📅 发表于 2025/11/03
🔄 更新于 2025/11/03
👁️ -- 次访问
📝 0 字
0 分钟
grpo
#GRPO
#DAPO
#熵坍塌
#Clip机制不公平
#探索
#奖励噪声
#训练不稳定
#Clip-Higher
#动态采样
#Token-Level Loss
#Overlong 奖励设计
#GSPO
#Token-Level 重要性权重
#Seq-Level 重要性权重
#MoE Routing RePlay
#CISPO
#重要性权重 Clip
#Dr.GRPO

(2508) LitePPO (淘天)

摘要
  • LitePPO: DeepDive into RL for LLM Reasoning

  • 最核心:针对常见RL技巧做总结,并对比实验验证真实有效和适用性

    • 任务分难度、模型分Base和Instruct等。
    • 验证了 Clip-Higher优势归一化Loss聚合策略超长过滤
  • 最后提出LitePPO

    • LitePPO:Group Mean + Batch Std + TokenLevelLoss,加PPO Loss。
    • 在Base小模型上,超过GRPO、DAPO。

问题背景: RL技巧各执一词有矛盾

❓问题背景

RL技巧各执一词充满矛盾

目前RL技巧太多,各说各的好,缺乏统一应用指南和深入研究

  • 关于归一化

  • 关于组优势时除以标准差

    • GRPO要除以标准差

      A^i,t=A^i=rimean(r)std(r)
    • Dr.GRPO不除以标准差

      A^i,t=A^i=rimean(r)
  • 关于Loss聚合

    • DAPOToken-Level Loss

      LDAPO(θ)=1i=1G|oi|i=1Gt=1|ot|所有Token直接做平均min(πθ(oi,tq,oi,<t)πθold(oi,tq,oi,<t)A^i,t,clip(πθ(oi,tq,oi,<t)πθold(oi,tq,oi,<t),1ϵ,1+ϵ)A^i,t)
    • GSPOResponse-Level Loss

      JGSPO(θ)=ExD,{yi}i=1Gπθold[1Gi=1Gmin(si(θ)A^i,clip(si(θ),1ϵ,1+ϵ)A^i)]

可能的原因

  • 实验环境、训练数据、初始模型、评测准则等等都不一样。

RL 技巧及研究实验

0. 实验设置

实验配置

模型

  • Qwen3-4B Qwen3-8B 对齐及Base版本 Qwen3-4B-Base, Qwen3-8B-Base

任务

  • 训练数据:SimpleRL-Zoo-Data, DeepMath
    • 移除了答案仅为“是/否”的样本,避免噪声
    • 难度分级
      • 简单:5k,从SimpleRL-Zoo-Data-Easy随机筛选。
      • 中等:5k,从DeepMath-103k选择最简单的。
      • 困难:5k,从DeepMath-103k按难度抽样。
    • 按照不同难度的训练数据去进行实验
  • 评测数据
    • Math500, OlympiadBench, MinervaMath, 部分 AIME24, AIME25, AMC

算法

  • loss:PPO loss
  • 优势:REINFORCE?

框架

  • ROLL,而非Verl

超参

  • 全局:bs=1024,
  • Rollout:bs=128, rollout.n=8
  • 回复长度:8k
  • lr: 1e-6
  • temperature: 0.99, top_p=0.99, top_k=100

不同难度数据

实验总结

任务难度

  • 任务难度影响
    • 随着epoch增加,准确率曲线也不同
  • 基础模型影响
    • RL对Base模型提升较大;但对已经高度优化过的模型,提升小一些,仅2%。

RL 技巧

  • Group归一化适应各种奖励,Batch归一化在奖励范围较大时,有点作用。
  • Group Mean + Batch Std 效果稳健。
  • Clip-Higher的最佳上限和具体模型有关,对对齐模型有作用,4B-0.32, 8B-0.28最佳。
  • Token Loss 对Base模型更友好,Seq Loss 对对齐模型效果更好。
  • 超长过滤在短回复任务中有效(8k),但在长回复任务中(20k) 似乎没啥效果。

1. 基线设计

RL 技巧

1. 基线设计

2. 裁剪策略

裁剪策略

2. 裁剪策略

  • 目的:裁剪,限制单次更新幅度防止策略过度更新
  • 方法:可以裁剪重要性权重、奖励、优势等。
    • PPO-Clip:裁剪重要性权重,[1ϵ,1+ϵ]
      • 缺点:抑制了低概率token,高概率token进一步强化,导致策略确定,熵坍塌
    • DAPO-Clip-Higher:裁剪IS权重,调高上限,保持探索性[1ϵlow,1+ϵhigh]
    • CISPO只裁剪IS权重的值仍然保留token的梯度
    • Value-Clip:裁剪价值网络,防止更新过快。
实验结果

Clip-Higher

  • ϵhigh=0.28,可以减轻熵坍塌
  • 提高裁剪上限,和基础模型也有关。
    • 针对base模型对熵影响不大
      • 裁剪率很低,连续策略之间差别不大,限制了探索能力。
      • base模型性能较差可能不知如何探索限制了探索能力
      • Base 预测最常见的下一词,概率分布尖锐。
    • 针对instruct模型能明显缓解熵崩溃提升一些性能
      • 与base模型比,在初始阶段,高概率词汇更少。
        • 提高裁剪上界,可以有效减少token之间的概率差距,缓解熵崩溃。
        • 有一定经验,可能性多,概率更均匀地分布在多个有效开局上
      • 有一定经验给予探索空间能突破瓶颈,达到更高效果。
  • 表示转折的token容易被clip掉,会影响模型的探索,增大clip允许模型探索更广
    • clip调高后,这些转折token就容易被优化到,促进多种探索。
  • clip最佳上限值具体模型有关需要调优
    • 4B模型:clip 从0.2 ->0.32,效果一直增长。
    • 8B模型,clip 从0.2->0.32,最优效果是0.28。需要调优

Clip-Higher 在Base模型上效果一般,在对齐后的模型上有效果。

Base模型高概率token多。

转折词汇经常被clip,提高上限后,转折词就能被优化,促进多种探索。

3. 归一化策略

归一化策略

3. 归一化策略

  • 含义:归一化奖励、优势

  • 目的:稳定梯度大小拉到同一水平线进行比较,让不同任务可比较。

  • 方法:

    • GRPO/RLOO组归一化

      A^i,t=A^i=rimean({rj}j=1K)std({rj}j=1K)
    • REINFORCE++Batch归一化 ,使用全局批次的平均奖励作为基线

      A^i,t=A^i=rimean({rj}j=1bsK)std({rj}j=1bsK)
    • Dr.GRPO不除以标准差,避免难度偏差。 A^i,t=A^i=rimean(r)

      • 论文没研究此方法,只研究了不归一化。
实验结论

关于归一化方法

  • 归一化肯定比不归一化好。
  • 组归一化整体都比较好
    • 在{0,1}稀疏奖励下,比批归一化稳定性好、效果好。
    • 适用于不同reward设置。
  • 批归一化
    • 在{-1,1}大范围奖励下,批归一化有优势,个人感觉也不是很明显

标准差

  • 不稳定的任务上,标准差大
  • 除以大标准差,会削弱或抑制那些偶然成功的高奖励学习信号

关于去掉标准差

  • 简单任务下,标准差有害,不用标准差更好
    • 奖励几乎相同标准差很小接近0,会导致梯度爆炸、训练崩溃。
  • 复杂任务下,标准差无害,可以用标准差。
    • 奖励比较多样化,标准差比较稳定,但用和不用标准差训练都比较稳定

关于鲁棒归一化

  • group mean + batch std 更鲁棒。
    • global std(batch) 比 local std(group) 要好一点。
    • batch std 提供了强大平滑全局的缩放,控制整体更新幅度。

0-1 reward下,组归一化,效果稳定。

-1-1 reward下(reward scale 较大时),作者说批归一化要好点,但我没看出来,明显Group的结果要比Batch要好一些。

  • 简单任务,std比较小,没有std会好一些。
  • 复杂任务,std比较大,有无std差不多。

batch的global std、比group的local std 看起来要好一点?

4. 过滤策略

过滤策略

4. 过滤策略

  • 含义:

    • 在梯度计算前,筛选出有价值的样本过滤掉无信息、不理想的样本。
    • 但:若对推理正确仅超长样本直接给-1,则会带来奖励噪声
  • 方法:

    • DAPO:超长回复过滤/奖励设计
    • 过滤错误特别多、过于简单的样本、过滤难度之外的样本。
    • DAPO:过滤掉全对或全错的batch
过滤实验

超长过滤

  • 超长过滤能降低 重复但无法终止的样本。
  • 效果有效性
    • 在截断长度较短时(8k):使用超长过滤会有效果。
    • 在长度较长时(20k):使用超长过滤,效果就不大了。

5. Loss 聚合策略

Loss 聚合策略

5. Loss 聚合粒度

  • 含义:决定了每个token对总目标的贡献权重。

  • 方法

    • GRPOSequence-Level Loss:序列内平均、再序列间平均。

      Lseq-level(θ)=1Gi=1G1|oi|t=1|ot|序列内平均min(ri,t(θ)A^i,t,clip(ri,t(θ),1ϵlow,1+ϵhigh)A^i,t)
    • DAPOToken-Level Loss,减少长度偏差,所有token贡献相同,Verl默认就是这个。

      Ltolen-level(θ)=1i=1G|oi|i=1Gt=1|ot|所有Token直接做平均min(ri,t(θ)A^i,t,clip(ri,t(θ),1ϵlow,1+ϵhigh)A^i,t)
    • GSPO序列级重要性权重序列级clip 更新

      JGSPO(θ)=ExD,{yi}i=1Gπθold[1Gi=1Gmin(si(θ)A^i,clip(si(θ),1ϵ,1+ϵ)A^i)]
Loss聚合策略 实验结果

关于聚合粒度

  • 对Base模型(Qwen3-8B),token-level-loss 效果更好
  • 对对齐模型(Qwen3-8B),seq-level-loss 效果更好。收敛速度和准确率都高于token-level loss。

对Qwen3-8B-Base,Token-Level Loss 效果更好。

对Qwen3-8B,Seqence-Level Loss 效果更好。

6. 附加loss策略

附加loss

6. 附加loss函数

  • 目标:补充主目标对训练进行正则化
  • 方法
    • KL Loss:约束和参考模型差异。
    • SFT Loss:保持对齐

7. 奖励函数设计

奖励函数设计

7. 奖励函数设计

  • 长度惩罚、格式化奖励、长度相关的准确度奖励等。

核心方法:LitePPO

📕核心方法

核心方法

LitePPO

  • 2项技术 (针对Base小模型有效果)
    • Group Mean + Batch Std
    • Token-Level-Loss
  • 使用 vanilla PPO Loss

算法实验

✍️实验设置

LitePPO。

🍑关键结果

关键结果
  • 对于非对齐小模型(4B-Base和8B-Base)
    • group mean + batch std 归一化,效果好。
    • Token-Level-Loss 效果好。
  • Lite PPO+Base模型,在相关Bench上,比GRPO和DAPO效果好。

⛳未来方向

未来方向

(2507) GSPO (Qwen)

摘要
  • Group Sequence Policy Optimization

  • 认为Token级重要性权重每个token IS权重不同,导致方差噪声大

    • 并不能实现预期的序列分布修正作用
    • 并被序列累积+Clip放大了,导致训练崩溃
  • 提出序列级重要性权重每个Token IS权重相同

    • 增加了稳定性和公平,序列奖励+序列更新
  • GSPO 优点

    • 比GRPO训练更稳定、效率更高。
    • 对MoE RL训练也增加稳定性,无需Routing Replay。
  • 缺点

    • 似乎没有什么具体指标、也没有讲训练数据。干货比较少。
    • 没有对比DAPO、CISPO。

问题背景

❓问题背景

Token级重要性权重噪声大容易训崩

GRPO Token-Level 重要性采样 训练崩溃

GRPO 存在训练稳定性问题

  • Scale RL训练 需要稳定性。
  • GRPO训大模型不稳定:经常出现严重不可逆转的训练崩溃

根本原因:Token-Level重要性采样权重

  • GRPO在每个token上计算重要性权重

    • 但LLM是整个序列,逐字去比较不太合理。

    • 单个token概率变化并不能反映 整个序列的概率变化

    • 使得重要性权重 并不能真正修正分布差异

      rt(θ)=πθ(atst)πθold(atst)=πθ(ytx,y<t)πθold(ytx,y<t)
    ri,t(θ)=πθ(yi,tx,yi,<t)πθold(yi,tx,yi,<t)
  • 而且,奖励信号是序列级别,但重要性权重优化却是token级别不匹配

Token-Level IS 产生训练崩溃的原因

  • 单token重要性权重 方差大、噪声大,不稳定,时好时坏,
  • 噪声随序列变长而逐渐累积,滚雪球。
  • 维稳的Clip机制,反而放大了噪声
    • 充满噪声的权重,被频繁裁剪,会扭曲整个学习信号
    • 嘈杂的信号 变成 持续的、错误的信号,反而加速模型崩溃

MoE专家激活不稳定容易训崩

MoE + GRPO 训练不稳定

MoE 专家激活不稳定 影响收敛

  • 对于同一个输入,新旧策略 激活的专家 大约有10%不一样
  • 这种不稳定性,导致GRPO Token级重要性权重,发生剧烈波动阻碍收敛

解决方法1:Routing Replay

  • 计算重要性权重时,缓存旧策略πθold激活的专家
  • 计算新策略πθ时,强制重复旧策略激活的专家
  • 确保新旧策略在每个token使用相同的专家,恢复IS的稳定性。
  • 不使用Routing Replay 很快就会崩溃。

解决方法2:GSPO

  • 从算法上去掉token级重要性权重,采用序列级重要性权重
  • 评价整个句子好不好,评价尺度更宏观,对底层专家模型不敏感
  • 无需Routing Replay。

有Routing Replay 训练没有崩。

核心方法

📕核心方法

GSPO-序列重要性权重

序列级Token权重

Token级重要性权重

ri,t(θ)=πθ(yi,tx,yi,<t)πθold(yi,tx,yi,<t)

序列级重要性权重

  • 对序列做重要性权重更好地衡量整句话的概率变化
si(θ)=πθ(yix)πθold(yix)
  • 长度归一化:降低方差,避免太长导致连乘概率太小

    si(θ)=(πθ(yix)πθold(yix))1|yi|=exp(1|yi|t=1|yi|logπθ(ytx,yi,<t)πθold(ytx,yi,<t))

GSPO 训练目标

  • 序列级奖励序列级优化相一致
  • clip整个序列
JGSPO(θ)=ExD,{yi}i=1Gπθold[1Gi=1Gmin(si(θ)A^i,clip(si(θ),1ϵ,1+ϵ)A^i)]

GSPO 梯度

  • 每个token使用相同的Seq-IS权重。
θJGSPO(θ)=θExD,{yi}i=1Gπθold(|x)[1Gi=1Gsi(θ)A^i]=ExD,{yi}i=1Gπθold(|x)[1Gi=1Gsi(θ)A^iθlogsi(θ)]=ExD,{yi}i=1Gπθold(|x)[1Gi=1G(πθ(yi|x)πθold(yi|x))1|yi|序列重要性权重A^i序列优势1|yi|t=1|yi|θlogπθ(yi,t|x,yi,<t)序列梯度]θJGSPO(θ)=ExD,{yi}i=1Gπθold(|x)[1Gi=1G(πθ(yi|x)πθold(yi|x))1|yi|A^i1|yi|t=1|yi|θlogπθ(yi,t|x,yi,<t)]

对比GRPO梯度

  • 其中:A^i,t=A^i
θJGRPO(θ)=ExD,{yi}i=1Gπθold(|x)[1Gi=1G1|yi|t=1|yi|πθ(yi,t|x,yi,<t)πθold(yi,t|x,yi,<t)token重要性权重A^i,ttoken优势θlogπθ(yi,t|x,yi,<t)token梯度]

GSPO vs GRPO

  • GRPO:每个token有自己的重要性权重。
    • 各token IS权重不同累积起来,波动性很大,带来很大噪声。
  • GSPO:每个token都使用相同的序列级重要性权重。
    • 各token IS权重相同,增加了稳定性和公平

GSPO 代码

py
@register_policy_loss("gspo")
def compute_policy_loss_gspo(
    old_log_prob: torch.Tensor,
    log_prob: torch.Tensor,
    advantages: torch.Tensor,
    response_mask: torch.Tensor,
    loss_agg_mode: str = "seq-mean-token-mean",
    config: Optional[DictConfig | ActorConfig] = None,
    rollout_is_weights: torch.Tensor | None = None,
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
    """
    Compute the clipped policy objective and related metrics for GSPO.

    See https://arxiv.org/pdf/2507.18071 for more details.

    Args:
        old_log_prob (torch.Tensor):
            Log-probabilities of actions under the old policy, shape (batch_size, response_length).
        log_prob (torch.Tensor):
            Log-probabilities of actions under the current policy, shape (batch_size, response_length).
        advantages (torch.Tensor):
            Advantage estimates for each action, shape (batch_size, response_length).
        response_mask (torch.Tensor):
            Mask indicating which tokens to include in the loss, shape (batch_size, response_length).
        loss_agg_mode (str, optional):
            Aggregation mode for `agg_loss`. For GSPO, it is recommended to use "seq-mean-token-mean".
    """

    assert config is not None
    assert isinstance(config, ActorConfig)
    clip_ratio_low = config.clip_ratio_low if config.clip_ratio_low is not None else config.clip_ratio
    clip_ratio_high = config.clip_ratio_high if config.clip_ratio_high is not None else config.clip_ratio

    negative_approx_kl = log_prob - old_log_prob

    # compute sequence-level importance ratio:
    # si(θ) = (π_θ(yi|x)/π_θold(yi|x))^(1/|yi|) =
    # exp [(1/|y_i|) * Σ_t log(π_θ(y_i,t|x,y_i,<t)/π_θold(y_i,t|x,y_i,<t))]
    seq_lengths = torch.sum(response_mask, dim=-1).clamp(min=1)
    negative_approx_kl_seq = torch.sum(negative_approx_kl * response_mask, dim=-1) / seq_lengths

    # Combined ratio at token level:
    # s_i,t(θ) = sg[s_i(θ)] · π_θ(y_i,t|x, y_i,<t) / sg[π_θ(y_i,t|x, y_i,<t)]
    # In log space: log(s_i,t(θ)) = sg[log(s_i(θ))] + log_prob - sg[log_prob]
    log_seq_importance_ratio = log_prob - log_prob.detach() + negative_approx_kl_seq.detach().unsqueeze(-1) 
    log_seq_importance_ratio = torch.clamp(log_seq_importance_ratio, max=10.0) # # clamp for numerical stability

    # finaly exp() to remove log
    seq_importance_ratio = torch.exp(log_seq_importance_ratio)

    pg_losses1 = -advantages * seq_importance_ratio
    pg_losses2 = -advantages * torch.clamp(seq_importance_ratio, 1 - clip_ratio_low, 1 + clip_ratio_high)
    pg_losses = torch.maximum(pg_losses1, pg_losses2)

    # Apply rollout importance sampling weights if provided
    if rollout_is_weights is not None:
        pg_losses = pg_losses * rollout_is_weights

    # for GSPO, we need to aggregate the loss at the sequence level (seq-mean-token-mean)
    pg_loss = agg_loss(loss_mat=pg_losses, loss_mask=response_mask, loss_agg_mode="seq-mean-token-mean") 

    # For compatibility, return zero for pg_clipfrac_lower (not used in standard GSPO)
    pg_clipfrac = verl_F.masked_mean(torch.gt(pg_losses2, pg_losses1).float(), response_mask)
    pg_clipfrac_lower = torch.tensor(0.0, device=pg_loss.device)

    ppo_kl = verl_F.masked_mean(-negative_approx_kl, response_mask)

    return pg_loss, pg_clipfrac, ppo_kl, pg_clipfrac_lower
python
def agg_loss(loss_mat: torch.Tensor, loss_mask: torch.Tensor, loss_agg_mode: str):
    """
    Aggregate the loss matrix into a scalar.

    Args:
        loss_mat: `(torch.Tensor)`:
            shape: (bs, response_length)
        loss_mask: `(torch.Tensor)`:
            shape: (bs, response_length)
        loss_agg_mode: (str) choices:
            method to aggregate the loss matrix into a scalar.
    Returns:
        loss: `a scalar torch.Tensor`
            aggregated loss
    """
    if loss_agg_mode == "token-mean":
        loss = verl_F.masked_mean(loss_mat, loss_mask)
    elif loss_agg_mode == "seq-mean-token-sum":
        seq_losses = torch.sum(loss_mat * loss_mask, dim=-1)  # token-sum
        loss = torch.mean(seq_losses)  # seq-mean
    elif loss_agg_mode == "seq-mean-token-mean":
        seq_losses = torch.sum(loss_mat * loss_mask, dim=-1) / torch.sum(loss_mask, dim=-1)  # token-mean
        loss = torch.mean(seq_losses)  # seq-mean
    elif loss_agg_mode == "seq-mean-token-sum-norm":
        seq_losses = torch.sum(loss_mat * loss_mask, dim=-1)
        loss = torch.sum(seq_losses) / loss_mask.shape[-1]  # The divisor
        # (loss_mask.shape[-1]) should ideally be constant
        # throughout training to well-replicate the DrGRPO paper.
        # TODO: Perhaps add user-defined normalizer argument to
        # agg_loss to ensure divisor stays constant throughout.
    else:
        raise ValueError(f"Invalid loss_agg_mode: {loss_agg_mode}")

    return loss

GSPO-Token 变体

提示

目标函数

  • 其中 A^i,t=A^i
JGSPO-token(θ)=ExD,{yi}i=1Gπθold[1Gi=1G1|yi|t=1|yi|min(si,t(θ)A^i,t,clip(si,t(θ),1ϵ,1+ϵ)A^i,t)]
  • 其中token重要性权重计算如下:实际仍然是序列权重?只是sg=stop gradient了。
si,t(θ)=sg[si(θ)]πθ(yi,tx,yi,<t)sg[πθold(yi,tx,yi,<t)]

算法实验

实验设置

✍️实验设置

实验配置

模型

  • Qwen3-30B-A3B-Base

算法

  • GSPO vs GRPO

任务

  • 训练数据:
  • 评测数据:AIME24(avg Pass@1, 32次采样)、LiveCodeBench (avg Pass@1, 8次采样)、CodeForces

超参

  • mini-bs=4,
  • Clip比例:
    • GSPO:[13104,1+4104]
    • GRPO:[10.2,1+0.27]

关键结果

🍑关键结果

关键结果
  • GSPO 训练过程很稳定。
  • GRPO 训练效率高于GRPO。
  • GSPO Clip的token比GRPO多2个数量级(15% vs 0.13%),却更稳定效果好,说明很多token嘈杂低效
  • GSPO 对MoE RL训练有好处,新旧策略重要性权重能保持稳定,不用Routing Replay。

比较稳定,减少训练资源

GSPO Clip的token比GRPO多2个数量级,15% vs 0.13%,

未来方向

⛳未来方向

未来方向

(2506) CISPO (MiniMax)

问题背景

❓问题背景

Clip 抑制了稀有关键token

GRPO Clip抑制了关键token

GRPO-Clip 抑制了关键token

  • 稀有关键token低概率、但高优势,在基座模型中很少出现
  • 策略更新时IS权重很大被CLIP掉了,导致无法影响梯度更新
  • 过滤了关键token,导致模型无法学会复杂推理能力

DAPO Clip-Higher

  • 设置ϵhigh,但效果不好

核心方法

📕核心方法

CISPO 算法

CISPO 算法

核心思想

公式

JCISPO(θ)=EqD,{oi}i=1Gπθold[1iG|oi|i=1Gt=1|oi|tokensg(r^i,t(θ))A^i,tlogπθ(oi,tq,oi,<t)]r^i,t(θ)=clip(ri,t(θ),1ϵlowIS,1+ϵhighIS)

算法实验

✍️实验设置

实验配置

模型

  • Qwen2.5-32B

算法

  • CISPO vs GRPO vs DAPO

任务

  • zero-RL 设置
  • 训练数据:DAPO-MATH-17K 数据
  • 评测数据:AIME24

🍑关键结果

关键结果
  • CISPO 效果好,AIME24 超过 DAPO 和 GRPO。
  • CISPO 效率高,仅需DAPO 50% step 达 DAPO效果。

⛳未来方向

未来方向

(2504) DAPO (Seed)

DAPO 摘要
  • DAPO paper
  • 围绕GRPO 熵坍塌奖励噪声训练不稳定等问题展开。
  • 提出了Clip-Higher动态采样Token-Level LossOverlong 奖励设计、数据转换等4大核心方法。
  • DAPO 效果和效率都很好,20pt提升,做了丰富的组件消融实验,都很有用。

问题背景

❓问题背景

熵坍塌

参考文章

PPO/GRPO 熵坍塌

熵坍塌表现

  • 模型熵迅速下降
  • 失去新意,停止探索过早进入利用阶段,需要探索利用平衡。

普通Clip机制不公平

  • 普通CLIP把策略更新幅度 限制在对称区间 [1ϵ,1+ϵ][10.2,1+0.2]
  • 不同概率的token来说,不公平。压制了低概率但具有探索性好步骤
  • 假设2个以下token都是好词,A^>0
    • 利用token已知可靠路径,旧策略概率0.9,概率提升上限0.9*1.2=1.08
    • 探索token探索的更优解,旧策略概率0.01,概率提升上限0.01*1.2=0.012
    • 探索的token,只能从1%提升到1.2%几乎没有变化对其太严格了。

熵坍塌本质:Clip上界压制了探索性

  • 上界压制了低概率、具有探索性好步骤
  • 放任已知高概率安全步骤变得更加绝对概率更高
  • 久而久之,探索性路径被堵死,模型只会走老路,导致熵坍塌

奖励噪声

奖励噪声

来源

  • 来源1:超长回答的粗暴惩罚
    • 超长了要惩罚、但生成内容又是对的,这就带来奖励困扰。直接给-1惩罚,有问题。
  • 来源2:对复杂答案的解析识别错误
    • 模型可能给出逻辑相同的答案,但仅仅因为无法解析,导致错误。
    • DAPO:对数据做转换,所有答案都变成数字

训练不稳定

问题背景

训练不稳定

核心方法

📕核心方法

Clip-Higher

Clip-Higher

问题

  • 解决熵坍塌问题。

Clip-Higher:奖励时要重奖励、惩罚时要谨慎惩罚

  • 上下界由[1ϵ,1+ϵ] 变成 [1ϵlow,1+ϵhigh]
  • 保持下界ϵlow=0.2不变
    • 如果太大,模型惩罚错误时,会过于激进,抑制了探索。
  • 提高上界ϵhigh=0.28
    • 设置更高探索天花板,当探索到好路径时,策略可以大幅度更新。
  • Loss
LDAPO(θ)=1i=1G|oi|i=1Gt=1|ot|所有Token直接做平均min(ri,t(θ)A^i,t,clip(ri,t(θ),1ϵlow,1+ϵhigh)A^i,t)
  • 目标函数
JDAPO(θ)=E(q,a)D,{oi}i=1Gπθold(q)[1i=1G|oi|i=1Gt=1|ot|所有Token直接做平均min(ri,t(θ)A^i,t,clip(ri,t(θ),1ϵlow,1+ϵhigh)A^i,t)]

熵坍塌问题里的例子

  • 原始:0.01 * 1.2 = 0.012,概率从1% -> 1.2%,最多增长0.2
  • 现在:0.01 * 1.28 = 0.0128,概率1% -> 1.28%,最多增长0.28比之前扩大了40%的增幅

动态采样

动态采样

GRPO 组采样问题

  • 如果采样到1组的Reward全为1或全为0,会导致优势全为0,导致梯度为0训练无效

动态采样

  • 采样:对每个问题,生成G个回答,
  • 评估:评估每个回答准确率
  • 过滤:过滤掉准确率为0或100%的问题及其答案
  • 重复1-3步

Token-Level Loss

Token-Level Loss

PPO/GRPO 标准Seq-Loss 缺点

  • 两层平均,对长度不敏感

    • 长序列token权重被稀释
    • 训练贡献度长序列token << 短序列token
  • 长度不敏感后果

    • 导致模型更关注短序列优化
    • 忽略长序列token里的错误,导致难以学会处理长序列问题。
Lppo(θ)=1Gi=1G1|oi|t=1|ot|序列内平均min(πθ(oi,tq,oi,<t)πθold(oi,tq,oi,<t)A^i,t,clip(πθ(oi,tq,oi,<t)πθold(oi,tq,oi,<t),1ϵ,1+ϵ)A^i,t)

DAPO Token-Level Loss

  • 直接对所有Token做平均每个token权重相同,不论来自长或短序列。
LDAPO(θ)=1i=1G|oi|i=1Gt=1|ot|所有Token直接做平均min(πθ(oi,tq,oi,<t)πθold(oi,tq,oi,<t)A^i,t,clip(πθ(oi,tq,oi,<t)πθold(oi,tq,oi,<t),1ϵ,1+ϵ)A^i,t)
python
def agg_loss(loss_mat: torch.Tensor, loss_mask: torch.Tensor, loss_agg_mode: str):
    """
    Aggregate the loss matrix into a scalar.

    Args:
        loss_mat: `(torch.Tensor)`:
            shape: (bs, response_length)
        loss_mask: `(torch.Tensor)`:
            shape: (bs, response_length)
        loss_agg_mode: (str) choices:
            method to aggregate the loss matrix into a scalar.
    Returns:
        loss: `a scalar torch.Tensor`
            aggregated loss
    """
    if loss_agg_mode == "token-mean":
        loss = verl_F.masked_mean(loss_mat, loss_mask)
    elif loss_agg_mode == "seq-mean-token-sum":
        seq_losses = torch.sum(loss_mat * loss_mask, dim=-1)  # token-sum
        loss = torch.mean(seq_losses)  # seq-mean
    elif loss_agg_mode == "seq-mean-token-mean":
        seq_losses = torch.sum(loss_mat * loss_mask, dim=-1) / torch.sum(loss_mask, dim=-1)  # token-mean
        loss = torch.mean(seq_losses)  # seq-mean
    elif loss_agg_mode == "seq-mean-token-sum-norm":
        seq_losses = torch.sum(loss_mat * loss_mask, dim=-1)
        loss = torch.sum(seq_losses) / loss_mask.shape[-1]  # The divisor
        # (loss_mask.shape[-1]) should ideally be constant
        # throughout training to well-replicate the DrGRPO paper.
        # TODO: Perhaps add user-defined normalizer argument to
        # agg_loss to ensure divisor stays constant throughout.
    else:
        raise ValueError(f"Invalid loss_agg_mode: {loss_agg_mode}")

    return loss

Overlong 奖励设计

超长 奖励设计

问题背景

  • 超长回答,通常直接给-1惩罚

  • 若该回答很好,仅仅是超长。·直接给-1,会有矛盾混淆奖励噪声干扰训练过程

    • 答案正确性奖励:分值高,你做的好
    • 长度惩罚:分值低,你做的差

Soft 超长惩罚

  • 设置cache长度Lcache硬顶线Lmax,安全线等于Lsafe=LmaxLcache
    • 低于安全线,不惩罚;
    • 超过硬顶线,直接给-1.
    • 介于安全线和硬顶线之间,根据超出长度来计算惩罚,线性下降。
Rlength(y)={0|y|LmaxLcacheLmaxLcache|y|LcacheLmaxLcache<|y|Lmax1|y|Lmax

数据转换

数据转换
  • 收集数据并做格式转换,为了方便reward,把所有答案都转换成数字最终17k数据集
  • 格式转换
    • 转换前:输出复杂, 不好计算reward,如a+bc
    • 转换后:输出简单,方便计算reward,如a+b+c,如llm输出11+2+6=19即可。
    • 让llm去改写question

算法实验

实验设置

✍️实验设置

实验配置

任务

  • 数学

数据

  • 训练数据:DAPO-MATH-17K
  • 评测数据:AIME24,avg@32,temp=1.0, topp=0.7

算法

  • GRPO

模型

  • Qwen2.5-32B

超参设计

  • 1e-6,学习率;最初20 steps 线性warm up
  • Rollout 阶段:bs=512个prompt,rollout 16
  • Training 阶段:mini_bs=512,共16次梯度更新
  • 长度16k,cache 4k;硬顶线为20k
  • Clip-Higher:[1-0.2, 1+0.28]

关键结果

🍑关键结果

关键结果
  • DAPO 效果和效率都很好。
    • 基础GRPO算法仅30%准确率,DAPO 达50%,超过DeepSeek-R1-Distill-32B (47%)。
      • AIME24,Qwen2.5-32B 从0% -> 50%,。
    • 且仅需DS-R1 一半的训练步数。
  • 各重要组件的消融实验
    • 超长过滤、CLIP-Higher、Soft 超长惩罚都能带来6-11pt的提升。
    • DAPO 带来20pt提升。
  • RL 不仅仅是强化已知正确路径,更多是创造、涌现、新的推理模式。
    • 即探索的力量。

消融实验:

RL 训练观测三大指标

RL 训练观测三大指标

回复长度

  • 和稳定性、性能相关;
  • 更长度代表更大的探索空间:回复越长,越有机会探索复杂的推理链。
  • 健康趋势:缓慢、稳定增长。
  • 长度停止或下降:结合验证集准确率来看,可能训崩了。

Reward

  • 代表对任务的拟合程度
  • 健康趋势:稳定增长
  • 小心:过拟合

  • 和模型探索能力相关,代表创造力、不确定性,需要维持在一个合理范围。
  • 熵过低/熵坍塌:概率分布过于尖锐、导致探索能力下降。
  • 熵过高:过度探索、回答过于随机、胡言乱语。
  • DAPO:通过Clip-Higher,解决了熵坍塌问题;维持熵缓慢增长,有助于性能提升。
  • 健康趋势:缓慢、稳定增长。

未来方向

⛳未来方向

未来方向
  • 涌现的机制是什么?
  • 如何更好地引导涌现?
  • 涌现是通用的吗?
  • 可解释性

(2503) Dr.GRPO

摘要
  • Understanding R1-Zero-Like Training: A Critical Perspective
  • 针对回复变长但效果降低、性能提升不稳定的问题,认为是GRPO里的长度偏置优势除以标准差带来的问题难度偏置造成的。
  • 提出Dr.GRPO,去掉序列内平均优势不除以标准差,提升了Token效率,回复长度变短。

问题背景

❓问题背景

R1-Zero背后的问题

R1-Zero 背后的问题

类R1-Zero训练中的不稳定现象

  • 回复长度增加,但性能提升不稳定
  • 有时生产更长、更啰嗦的错误答案

R1-Zero存在问题需要探究

  • 性能提升来源是什么?
    • 激发潜在推理能力、还是预训练知识被激活
  • 回答变长一定是好事吗?
  • 基础模型扮演何种角色,不同基座对最终RL训练效果差别大吗?

GRPO里的偏差

GRPO里的偏差

计算优势时引入Bias

  • 回复长度Bias

    LGRPO(θ)=1Gi=1G1|oi|t=1|ot|序列内平均min(πθ(oi,tq,oi,<t)πθold(oi,tq,oi,<t)A^i,t,clip(πθ(oi,tq,oi,<t)πθold(oi,tq,oi,<t),1ϵ,1+ϵ)A^i,t)
  • 问题难度Bias

    • 计算优势时,除以标准差。
    • 简单问题:方差小,标准差小优势大权重大
    • 困难问题:方差大,标准差大优势小权重小
    • 导致对于真正需要学习的困难问题,学习不足。
A^i,t=A^i=r^i=rimean(r)std(r)

核心方法

去掉2个偏差项(序列内平均,标准差)

📕核心方法

Dr.GRPO

背景

  • 解决GRPO里的长度偏差问题难度优势偏差问题。

核心思想

  • 去掉2个偏差项:去掉序列内平均计算优势时不除以标准差

公式

  • 去掉序列内平均,消除长度偏差
LDr.GRPO(θ)=1Gi=1Gt=1|ot|min(πθ(oi,tq,oi,<t)πθold(oi,tq,oi,<t)A^i,t,clip(πθ(oi,tq,oi,<t)πθold(oi,tq,oi,<t),1ϵ,1+ϵ)A^i,t)
  • 计算优势时,去掉除以标准差,消除问题难度偏差。困难问题一样有高优势权重,可以更新。A^i,t=A^i=rimean(r)

算法实验

✍️实验设置

实验配置

模型

  • Qwen2.5-1.5B-Base, Qwen2.5-Base

任务

  • 训练数据:MATH Dataset
  • 评测数据:AIME24、AMC、MATH500、Minerva Math、OlympiadBench

算法

  • Dr.GRPO vs GRPO

🍑关键结果

关键结果
  • Dr.GRPO 修复了GRPO里的2个偏差,提升了token效率,输出回复更短,能有效抑制错误答案增长?
    • 回答变长是GRPO里优化算法的偏见,而非长思考?
  • 模板和基础模型不匹配,效果也不会好。
    • Qwen2.5-MATH-1.5B数学能力已经较好,强行套用R1模板,会损害性能。
  • 基座模型能力较弱时,先进行预训练,再进行RL,才能提升效果。

Token效率更高

⛳未来方向

未来方向

(2402) GRPO (DeepSeek Math)

总访客数:   ·   总访问量:
PLM's Blog @ 2016 - 2025