研究博客 · 大模型推理中的强化学习

别再为学不到东西的 rollout 买单

介绍 POPO:一个即插即用的简单方法,无需额外生成任何样本,就能让 RLVR 的每个训练批次都装满有效数据。

arXiv:2606.01281 兼容 GRPO · RLOO · PPO 数学 · 规划 · 视觉几何
一句话总结
100%每个训练批次都是有效数据;原始 GRPO 通常不到 50%
~30%只需 DAPO 约三成的 rollout 预算即可达到相当准确率
0无额外 rollout,也无额外似然计算;缓冲区仅存一个批次

GRPO 里悄无声息的浪费

可验证奖励强化学习(RLVR)就像一位极有耐心的家教。给模型一道题,让它尝试 k 次,逐一判对错,再引导它向“比平均水平更好”的回答靠拢。GRPO 把这件事写成优势函数:每个回答的奖励减去组内均值,再除以组内标准差。

现在设想一道模型每次都能做对的题,或者一道它从来做不对的题。每次尝试得分都一样,这组回答里不存在“比平均更好”,所有优势值都是零。模型花了大量 GPU 时间写出八条长长的思维链,却什么也没学到。

亲手试试:点击下面的回答,在对错之间切换。

演示 · 一道题,八次尝试

这并不是个别现象。论文实验显示,原始 GRPO 训练批次中有效组的比例通常不到一半。无论是数学(DeepScaleR)、算术规划(Countdown)还是视觉几何都是如此。而且随着模型变强,更多题目会变成“全对”的简单组,问题会越来越严重。

现有的三种解法,各有各的代价

过采样 + 过滤

DAPO

多 rollout 一些题目,丢掉零方差的,不断重复直到凑满一个批次。

代价:额外 rollout 的开销可能比 RL 更新本身还大。

先预测

预测式采样

在 rollout 前预测哪些题目是“中等难度”,例如 MoPPS、GRESO。

代价:模型在不断变化、每道题历史数据又少,预测很难做准。

回放答案

轨迹回放

往全错的组里塞一个历史上的正确答案,例如 ARPO。

代价:全对的组依然无效;组内混入不同策略的回答,会让优势估计失真。

POPO 的思路:回放整组,而不是单条回答

想象一位足球教练,他有一条铁律:场上每个位置都必须由真正能踢的人来填。首发球员临场受伤时,他不会去街上现拉一个陌生人,那相当于 DAPO 的额外 rollout。他也不会硬塞一个半伤不愈的球员凑数,那相当于轨迹回放。他会换上上一场刚踢得不错的替补。

这就是优先组回放,它遵循两条规则:

为什么“新”能代表“接近当前策略”?PPO 式裁剪让每一步更新都很小。因此两个策略之间的差距,按总变差距离衡量,最多随间隔步数线性增长:相隔 n 步时不超过 εn/2。所以新鲜度就是一个免费的相似度代理,完全不用计算 KL 散度。

演示 · 看 POPO 如何填满一个批次(每步 12 道题)
新鲜 rollout(GRPO 实际训练的数据)第 0 步
POPO 的训练批次(无效组被替换为最近的回放组;数字为相隔步数)
回放缓冲区(FIFO,容量 = 批次大小;最新的在右侧)
新鲜且有效 新鲜但无效(零方差) 从缓冲区回放
GRPO 有效样本比例–
POPO 有效样本比例–

两个细节让它既便宜又干净。第一,缓冲区最多只存一个批次的组,内存开销几乎可以忽略。第二,由于是整组回放,组内每条回答都来自同一个策略。这样组内优势依然定义良好,生成数据的行为策略也是确定的,而这一点对下一步至关重要。

用“稍旧”的数据,却不被它拖累

回放组是由一个较旧的策略生成的,记作 πβ。如果直接假装它是新数据,梯度就会有偏差。已有工作有两种处理方式,POPO 采取了第三种。

当作在策略数据

clip(πθ/πold)

✗ 只要 πβ ≠ πold 就有偏

消融实验中直接崩溃。

向旧策略裁剪

clip(πθ/πβ)

✓ 无偏

✗ 把模型拴在一个更差的策略上,而且每个回放样本拴的位置还不一样。裁剪触发过于频繁。

POPO:把两件事解耦

(πold/πβ) · clip(πθ/πold)

✓ 无偏:第一个因子负责校正分布

✓ 与在策略数据同一根“缰绳”:裁剪始终锚定 πold

关键技巧在于:完整的重要性比率 πθ/πβ 可以拆成两个因子,各司其职:

πθ/πβ  =  πθ/πold  ×  πold/πβ
lossreplay  =  w · min( r·Â , clip(r, 1±ε)·Â )    // w 为常数,不回传梯度

橙色因子校正数据从哪里来。蓝色比率决定这一步最多能走多远,它使用的信任域与新鲜数据完全相同。

论文证明:在两个比率都未被裁剪的位置,该目标与完整校正的 πβ 版本给出完全相同的值和梯度。唯一的区别是缰绳拴在哪个策略上。这就像一个导航仪:它会考虑地图是在哪里绘制的,但下一次转弯永远从你此刻所站的位置算起。

校正过去,锚定当下。

整个算法写在一张餐巾纸上就够了

落到代码上,POPO 只多了一个过滤、一个小队列和一次乘法:

buffer = FIFO(capacity=B)          # 存储组及其 rollout 时的对数概率

for step in range(T):
    groups  = rollout(policy, sample_prompts(B), k)          # 与 GRPO 成本相同
    on_eff  = [g for g in groups if std(g.rewards) > 0]      # 丢弃无效组
    off_eff = buffer.newest(B - len(on_eff))                  # 用最新的组补满

    loss = grpo_loss(on_eff)
    w    = exp(logp_old(off_eff) - off_eff.logp_beta).clamp(max=2.0).detach()
    loss += (w * grpo_token_loss(off_eff)).aggregate()       # 解耦校正
    optimize(policy, loss)

    buffer.push(on_eff)                                       # 存入本步的有效组

实际实现中,论文还将重要性权重 w 截断在 2.0 以内,以抑制罕见的离群值。论文未观察到由方差引起的不稳定。

效果如何?选一个基准看看

POPO 的对比对象包括 GRPO、DAPO(昂贵的“金标准”)、MoPPS(预测式采样)和 ARPO(轨迹回放)。实验覆盖四种设置:Countdown 上的 Qwen2.5-3B、Geometry3k 上的 Qwen2.5-VL-3B,以及 DeepScaleR 数学数据上的 DeepSeek-R1-Distill-Qwen 1.5B 与 7B。

交互式结果 · 最终准确率与开销
准确率(%)
训练耗时(小时)
生成的 rollout 总数(千条)

规律在所有设置中都成立:POPO 与 DAPO 持平或非常接近,并明显领先 GRPO、ARPO 和 MoPPS,而开销却与原始 GRPO 相近。相比 GRPO 略多的训练时间其实是个好信号:POPO 训练出的模型会写出更长的推理链,趋势与 DAPO 一致,而更长的推理被认为与更强的推理能力相关。

几个值得细看的亮点:

故意拆掉零件:消融实验告诉我们什么

POPO 的每个部件都不可或缺。下面是在 Countdown 上去掉或替换各个部件后的结果:

🐢
POPO-KL — 从 5 倍大的缓冲区中按 KL 距离挑选回放组

准确率相当,但更慢:4.8 小时对比 3.2 小时。新鲜度是免费且有效的距离替代指标。

📉
GRPO-filter — 丢弃无效组,用更小的批次训练

只比 GRPO 略好。批次变小会让梯度更嘈杂,所以关键在于补满批次。

🗑️
POPO-ineff — 回放最近的组,不管是否有效

性能明显下降,说明质量过滤很重要。

🧟
POPO-stale(10) — 从 10 倍大的缓冲区中随机回放有效组

直接崩溃。而 1 倍随机缓冲区只是略差,说明新鲜度很重要,但 POPO 对新鲜度的小幅变化并不脆弱。

⚓
POPO-πβ — 改为向行为策略裁剪

学习变慢,这正是“缰绳拴在过去”问题的体现。

💥
POPO-πold — 把回放数据当作在策略数据

由于偏差未被校正,性能崩溃。

与其他方法和谐共处

POPO 是一个插件,而不是一套全新的算法,论文也正是这样测试它的:

坦诚面对局限

只记住最近一个批次的缓冲区是刻意“健忘”的。它放弃复用更早但可能有价值的组,换取更小的离策略差距,也不会刻意挑选最有信息量的组。作者提出了一个未来方向:短期缓冲区加上一个小型长期“蓄水池”,用来保存特别有价值的组。他们还建议每步生成更少的新 rollout,更多地依赖回放。此外,零方差判定假设奖励是二值(或近似二值)的。对于连续奖励,可以改用低方差阈值。

结语

生成是 RLVR 中最昂贵的环节,而其中相当大一部分却因零梯度组被白白浪费。POPO 的解法小巧得令人耳目一新:保留上一批次的有效组,替换掉今天的无效组,再乘上一个重要性权重,让数学保持严谨。回报是:以 GRPO 量级的 rollout 预算,得到 DAPO 级别的结果。

批次里的每一个位置都应该教会模型一点东西,而做到这一点不应再多花一次 rollout。