SPEC-RL: Accelerating On-Policy RL with Speculative Rollouts
速读卡片 (TL;DR)
一句话:把上一个 epoch 的 rollout 当 draft,在当前 policy 下做 speculative verify,接受的前缀直接复用、第一个被拒位置之后再用当前 policy 续写——不用新增 draft model,纯 rollout-stage 的"插件式"加速,2–3× 而不掉点。
立场:这条路线里最"轻"的一篇——核心创新只是"draft = 上一轮自己"。简单到几乎不像 paper,但正因如此它揭示的是 RLVR 训练里一种被普遍浪费掉的结构性冗余。
1 · 动机:为什么"上一轮的轨迹"是免费的 draft
1.1 历史脉络
RLVR (Reinforcement Learning with Verifiable Rewards) 已经成为推理模型训练的主流——DeepSeek-R1、Tülu-3、Qwen3 都靠它。但把一个 RL step 拆开看,rollout 占了大头:在 8B 模型上 generation 通常吃掉 60–70% 的 step time(NeMo-RL 论文给的是 65–72%)。当模型从 1.7B 涨到 32B、单条 trajectory 从 512 token 涨到 4k 甚至 32k(long-CoT、agentic 多轮)时,这个比例只升不降。
已有的加速思路大致三条:
- 并行 rollout(Xu et al. 2025)——更多 GPU 同时跑,边际收益递减、同步开销升高。
- 修改目标 / 数据(CPPO、Prefix Grouper、各种 down-sampling)——往往引入 bias 或者打破 on-policy 假设。
- replay buffer——直接复用旧轨迹做训练数据,但 policy 一变就 stale。
SPEC-RL 选了第四条,和 NeMo-RL 那篇用 EAGLE-3 的思路同源 (speculative decoding),但把 draft model 替换成了"上一个 epoch 的 cache"。换句话说,draft 是免费的——你训练的时候本来就在生成它。
1.2 别的方案为什么不够
| 方案 | 核心思路 | 主要妥协 |
|---|---|---|
| Parallel rollout | 更多机器并发跑 rollout | GPU 翻倍才能换 1.x×;同步成本增长 |
| CPPO / 数据精选 | 只 update 信息量大的 sample | 引入 selection bias,与 on-policy 冲突 |
| Replay buffer | 把旧 trajectory 直接当训练数据 | policy 一变就严重 off-policy |
| EAGLE-3 + RL (NeMo-RL) | 训练一个独立 draft head 配 target | 多一个 model 要 maintain;draft 还得跟着 policy 重训 |
| RhymeRL | 从 history 建 suffix tree,做 token 级 SD | 系统复杂、suffix-tree 维护成本 |
| SPEC-RL (本文) | 上一 epoch 的 rollout 作为 draft + lenience | 需要小数据集多 epoch;首 epoch cold start |
1.3 为什么这事不平凡
"复用旧轨迹"听上去显然——但要做对,有几个非平凡的点:
- 样本分布不能漂。如果直接拼接旧 prefix + 新生成,就是 off-policy 数据,会破坏 PPO/GRPO 的 importance sampling 假设。SPEC-RL 用 speculative decoding 的拒绝采样保证最终 trajectory 仍然等价于从 πcurr 采样,这就把"复用"和"on-policy"调和了。
- policy 必须更新得足够慢。RLVR 实际场景里"小数据 × 多 epoch"非常常见(论文引用的研究表明数据质量比数量更重要,经常训几十到上百 epoch),作者用 ROUGE-1 量了一下:连续两 epoch 的 token overlap 稳定在 0.5–0.7 之间(Figure 2)。这是这个方法存在的事实基础——如果每 epoch 都有 50% 的 token 重复,那就有 50% 的 FLOPs 在白烧。
- acceptance rate 在训练过程中会非线性变化。早期 policy 更新激进,reuse 率反而下降;中后期 policy 趋稳,reuse 率回升(Figure 4c 的 U 形)。这意味着 lenience 不是一个静态超参,但本文用一个 fixed ℓ 也已经够用,留 adaptive scheduling 给后续工作。
- lenience 让 SD 从"无损"变成"可控有损"。原版 speculative decoding 严格保证从 q 采样,SPEC-RL 引入 ℓ ≥ 1 把验证门槛放宽,接受率↑、reuse 率↑、但分布有偏。这是 deliberate 的工程权衡——RL 不像 inference 那样要严格无损。
2 · 背景速查
| 术语 | 含义 |
|---|---|
| RLVR | RL with Verifiable Rewards——reward 是 0/1 的可验证信号 (数值匹配 / unit test 通过) |
| rollout | 一次完整生成 y ∼ π(·|x),RL 训练数据来源 |
| on-policy | 训练数据必须用当前 πθ 采样;PPO/GRPO 的核心假设 |
| draft & verify | SD 范式:draft p 提议、target q 并行验证、按比率接受 |
| acceptance prob | αi = min(1, q/p) 保证最终从 q 采样 |
| lenience ℓ | 放宽接受准则的乘子,ℓ=1 复原标准 SD,ℓ→∞ 全部复用 |
| epoch | RLVR 里一个 epoch = 全部 prompt 都被 rollout 一遍 |
| verified prefix | 从位置 1 起连续被接受的 token,直到第一个 rejection 位置 n |
3 · 核心机制:Speculative Rollout
3.1 整体流程
每个 prompt x 在 epoch t 的 rollout 流程:
- 从 cache 取出 epoch t−1 时同一 prompt 生成的 yprev,以及当时的 token 概率 pprevi = πprev(yprevi | x, yprev)。
- 在当前 πθ 上做一次并行 forward 整条 yprev,得到 pcurri。这是单次 prefill,不是逐 token 解码——这就是 SD 加速的来源。
- 对每个位置 i 计算 α̃i = min(1, ℓ · pcurri/pprevi),抽 u ∼ U(0,1),u > α̃ ⇒ reject。
- 取第一个 rejection 位置 n,verified prefix = yprev
。 - 从位置 n 起,用 πθ 自回归续写得到 ycurr≥n。
- 最终 ycurr = yprev
⊕ ycurr≥n,写回 cache 覆盖旧值。
3.2 Worked example: 一道数学题在 epoch 5 → 6 的演化
Prompt: x = "Solve x²=9, find y if y=x+0."。Epoch 5 cache 里的 yprev 是 8 个 token: ["Let","x","=","3",",","so","y²","=9"],每个位置上 πprev 的概率分别是 [0.45, 0.78, 0.91, 0.62, 0.55, 0.48, 0.30, 0.40]。
Epoch 6 时 πθ 微调过,在同样上下文下给出概率 pcurr = [0.50, 0.80, 0.92, 0.65, 0.58, 0.20, 0.31, 0.42]。注意位置 6 的 "so" 概率从 0.48 掉到 0.20——πθ 现在更倾向于直接说 "thus"。取 ℓ = e0.5 ≈ 1.649,逐位算 α̃:
| i | token | pprev | pcurr | ratio | α̃ = min(1, ℓ·ratio) | u | 结果 |
|---|---|---|---|---|---|---|---|
| 1 | "Let" | 0.45 | 0.50 | 1.111 | 1.000 | 0.31 | accept |
| 2 | "x" | 0.78 | 0.80 | 1.026 | 1.000 | 0.62 | accept |
| 3 | "=" | 0.91 | 0.92 | 1.011 | 1.000 | 0.04 | accept |
| 4 | "3" | 0.62 | 0.65 | 1.048 | 1.000 | 0.71 | accept |
| 5 | "," | 0.55 | 0.58 | 1.055 | 1.000 | 0.22 | accept |
| 6 | "so" | 0.48 | 0.20 | 0.417 | 0.687 | 0.84 | REJECT (n=6) |
verified prefix = 5 个 token。从位置 6 起 πθ 接管自回归生成,可能给出 ["thus","y²","=9","…"]。最终拼起来的 ycurr:5 个旧 token + ~k 个新 token。如果 prev 长度是 800、reused 600、新生成 200,那就只解码了 200 个 token,加上 1 次 prefill 跑 800 token——prefill 在 GPU 上比逐 token decode 高效 1–2 个数量级,这就是加速的物理来源。
3.3 反向论证: 如果省掉 verify 直接拼接会怎样?
就成了 "Random Reuse" 消融——论文 Table 2 给出的对照: token 反而更多 (304.5M vs 182.7M),但 average accuracy 从 37.3 掉到 31.4。原因是无验证地拼接相当于把上一轮 πprev 的 sampling 噪声直接注入当前的训练数据,policy gradient 用错了 logπ,KL 爆炸,训练偏移。这条消融非常重要——它证明 "speculative verify 这一步" 不是装饰,是把"复用"和"on-policy"调和起来的关键。
4 · Lenience 参数 ℓ 的物理意义
ℓ 是 SPEC-RL 唯一新加的超参,直接控制"宁可激进复用还是宁可保 on-policy":
| ℓ | 语义 | 含义 | 论文实测 speedup / acc |
|---|---|---|---|
| → 0 | 所有 token 都拒绝 | 退化为 vanilla RLVR | 1.00× / 34.9 |
| = 1 | 标准 SD,无损 | 分布严格 = πcurr | 1.22× / 35.8 |
| = e0.2 | 轻微宽松 | 少量 off-policy | 1.86× / 36.3 |
| = e0.5 ★ | 论文推荐 sweet spot | 速度/质量最佳平衡 | 2.29× / 37.3 |
| = e0.8 | 偏激进 | 开始掉精度 | 2.64× / 33.9 |
| = e2.0 | 很激进 | 明显劣化 | 3.05× / 30.3 |
| → ∞ | 全盘复用 | 完全 off-policy | 14.86× / 29.2 (训练崩) |
诊断指标 (Figure 5): 当 ℓ < e0.5 时,clip fraction 稳定 < 0.0025、KL < 0.1、entropy 与 baseline 几乎重合;一旦 ℓ ≥ e1.0 这三个量同时炸,训练失稳。这给了一个非常实用的经验:把 KL 当 ℓ 的早期警报。
反向论证: 为什么不能直接把 ℓ 设成 ∞?因为这等价于强制把 πprev 的整条 trajectory 当作 πcurr 的样本——用错了 log-prob,policy gradient 的方向直接错了。Table 3 里 ℓ→∞ 的 14.86× 速度看着很美,但 acc 29.2 比不训还差。这是"speedup ≠ benefit"的典型反例。
5 · 系统实现
5.1 Cache 模块
每个 prompt 维护一个最新 rollout(包括 token 序列和当时的 token-level log-prob)。"最新"意味着每个 epoch 把生成的 ycurr 写回去,覆盖前一个 yprev。这一点很关键——消融里 "Delayed Reuse"(用 epoch t−2 而非 t−1 的轨迹)直接把 speedup 从 2.29× 降到 1.44×,因为 policy gap 越大,reuse rate 越低。
5.2 批量 verify
实现层面,作者把整个 batch 的 prompt + 对应 cached prefix 拼成一个 padded batch,左 pad 对齐,送进 vLLM 一次 forward 拿到 logits,所有 prompt 并行 verify。这避免了"每条 prompt 单独验证"导致的 GPU 利用率塌陷。完成 verify 后,只对那些"还要继续生成"的 prompt 再发起 vLLM continuation request。
5.3 Worked example: batch 维度的吞吐
设 batch=1024,平均 yprev 长度 = 800,平均 reused = 600,新生成 = 200。
- baseline: 1024 × 800 token 的 autoregressive decode,GPU 利用率受 KV cache & batch size 限制,大约 N 秒。
- SPEC-RL Stage 1: 1024 × 800 的 prefill,在 H100 上几乎 free——FLOPs 跟 decode 同量级但无 sequential dependency,实测占用通常 < 0.1N。
- SPEC-RL Stage 2: 1024 × 200 的 decode,大约 0.25N。
- 合计: ~0.35N,~2.85× speedup,与论文 DAPO 的 2.88× 实测吻合。
6 · 公式推导与数值敏感性
原版 SD 的接受概率 (Leviathan et al. 2023):
SPEC-RL 把 (p, q) 替换成 (πprev, πcurr) 并加入 lenience:
物理直觉: ratio = πcurr/πprev 衡量"在新 policy 眼里这个 token 还有多自然"。当两个 policy 几乎一样时 ratio ≈ 1,token 几乎都被接受;当 policy 在某个位置剧烈分歧时 ratio ≪ 1,大概率拒绝。乘以 ℓ 相当于给 ratio 一个全局放大,鼓励"勉强还行的 token 也接受"。
数值敏感性 (固定 ratio = 0.5,看 ℓ 变化):
| ℓ | ℓ·ratio | α̃ | 解释 |
|---|---|---|---|
| 0.5 | 0.25 | 0.25 | 更挑剔,接受 25% |
| 1.0 | 0.50 | 0.50 | 标准 SD,接受 50% |
| 1.5 | 0.75 | 0.75 | 明显宽松 |
| 2.0 | 1.00 | 1.00 | 无条件接受 |
| 5.0 | 2.50 | 1.00 | 同上,饱和了 |
关键观察:对 ratio = 0.5 这种"中等分歧"的 token,ℓ 从 1 涨到 2 就把接受率从 50% 拉到 100%——非线性放大。这也是为什么 ℓ = e0.5 ≈ 1.65 已经显著改善 reuse rate。
反向: 当 ratio > 1/ℓ 时永远 accept。 也就是只要新 policy 在该 token 的概率不比旧 policy 低太多,就直接接受。具体边界: ℓ = e0.5 ⇒ 只要 πcurr ≥ 0.607 · πprev 就 100% accept。
7 · 实验关键结果
7.1 主表 (Table 1) 摘录
| Model | Algo | Tokens (M) ↓ | Speedup | AVG acc | baseline acc |
|---|---|---|---|---|---|
| Qwen3-1.7B | GRPO | 554.8 → 182.7 | 2.29× | 37.3 | 34.9 (+2.4) |
| Qwen3-1.7B | PPO | 565.1 → 230.8 | 1.94× | 35.0 | 34.8 (≈) |
| Qwen3-1.7B | DAPO | 543.1 → 171.6 | 2.17× | 32.8 | 31.9 (+0.9) |
| Qwen3-8B | GRPO | 1033 → 336 | 2.51× | 59.3 | 57.5 (+1.8) |
| Qwen3-8B | PPO | 984 → 400 | 1.94× | 57.2 | 57.3 (≈) |
| Qwen3-8B | DAPO | 1052 → 326 | 2.88× | 57.3 | 55.0 (+2.3) |
| Llama3.2-1B | GRPO | 554 → 162 | 2.60× | 15.5 | 15.2 (+0.3) |
读表: 9 个组合全部加速 1.94×–2.88×,平均 2.31×。准确率没掉,常常还有 +1~2 分提升 (尤其 OOD: Qwen3-8B GRPO 的 IFEval 从 41.2 → 47.7,+6.5)。这与"reuse old prefix → 训练数据多样性下降"的直觉相反——Figure 6 给出解释: 早期 epoch 的 prefix 比晚期更多样(那时 policy 还没收敛),复用早期 prefix 反而保住了 trajectory diversity。
7.2 关键消融 (Table 2)
| 变体 | Tokens | Speedup | AVG acc | 结论 |
|---|---|---|---|---|
| SPEC-RL (ours) | 182.7M | 2.29× | 37.3 | baseline |
| Random Reuse | 304.5M | 2.35× | 31.4 | 没 verify ⇒ 数据被污染 |
| Delayed Reuse (t−2) | 308.8M | 1.44× | 36.3 | cache 越旧 reuse rate 越低 |
这两个消融在我看是论文最重要的——它们分别证伪了两种"看起来更简单"的选择: 不验证(直接拼接)、不及时更新 cache(用更旧的 trajectory)。两条都比 SPEC-RL 差。
7.3 训练动力学 (Figure 4-5)
- verified prefix length 在训练初期下降(policy 大步更新)、中期回升(policy 趋稳)。这是个 U 形曲线,意味着一个 fixed ℓ 在不同训练阶段的实际 reuse rate 是变化的——adaptive ℓ scheduling 是显然的后续。
- ℓ < e0.5 时 KL/entropy/clip ratio 都和 baseline 几乎重合; ℓ ≥ e1.0 时三者同时飙升,训练就开始崩。
8 · 与同类工作对比
| 方法 | Draft 来源 | Verify 严格性 | 系统复杂度 | 典型加速 | 主要妥协 |
|---|---|---|---|---|---|
| SPEC-RL | 上一 epoch 自己的 rollout | SD + lenience (ℓ可调) | 极低 (一个 cache) | 2–3× | 需多 epoch 训;cold start |
| NeMo-RL × EAGLE-3 | 训一个 EAGLE-3 draft head | 严格 SD,无损 | 高 (要重训 draft、要 vLLM 集成) | 1.4× (8B sync) ~ 2.5× (235B async) | 多一个 model 维护 |
| ReSpec (Sun et al. 2025) | 独立 draft model + 适配 RL | SD,有 fidelity 控制 | 中 | 1.5–2× | draft 仍要重训 |
| DAS / RhymeRL | 历史回答构建 suffix tree,token 级匹配 | SD on tree branch | 高 (suffix tree 维护) | 1.5–2× | tree 内存占用、匹配复杂 |
| Replay buffer | 历史轨迹直接当训练数据 | 不验证 | 低 | (节省 rollout 数,不是单条 rollout 加速) | off-policy 偏差 |
纵观三篇:
- NeMo-RL EAGLE-3 走"通用 SD 工程化"路线——花最大力气把 draft model 维护起来,获得理论上最干净的 lossless 加速。代价是工程复杂度。
- ReSpec 在 NeMo-RL 同期但 focus 在"训练 draft 时怎么和 RL 兼容"的细节上,介于 SPEC-RL 和 NeMo-RL 之间。
- SPEC-RL 走"系统极简"路线——不要 draft model、不要 suffix tree、不要重训任何东西,只要"上一轮的自己"就行了。
- DAS / RhymeRL 走"信息抽取最大化"路线——挖掘所有 historical responses 里能匹配的 token n-gram。理论上 reuse rate 上限更高,但工程量大。
四者并不严格冲突:SPEC-RL 的"上一 epoch 整条 cache" 可以看作 DAS 的特例 (匹配整条 history, 不分 token);反过来 SPEC-RL 也可以叠加 EAGLE-3 做"小段填充"。论文没做这种组合实验,但这是显然的后续。
9 · 局限 / 个人 take / 待验证问题
- 第一个 epoch 没加速。cache 是空的,得先跑一遍 baseline rollout 才能开始 SD。如果训练只跑 2-3 个 epoch,SPEC-RL 的均摊收益打折扣。论文实验都是几十个 epoch,这是隐含前提。
- 多轮 / agentic / 多模态没覆盖。论文明确说只处理 single-turn reasoning。当 trajectory 包括 tool call、外部环境观察时,"上一 epoch 的相同 prompt 仍能匹配同一条 trajectory" 这个假设不成立——环境是 stochastic 的。
- fixed ℓ 在动力学上不是最优。Figure 4c 那个 U 形曲线说明 reuse rate 自身在变,如果 ℓ 自适应跟着调(比如根据 KL 反馈),理论上可以再榨一点速度而不掉点。
- OOD 涨点机制存疑。论文的解释("早期 epoch prefix 更多样所以保 diversity")合理但偏故事化。需要更细的对照才能确认到底是 (a) regularization 效果 (b) implicit replay (c) 还是单纯 lucky seed。
- cache 占用。每个 prompt 存一条 ~4k token 的轨迹和 token-level log-prob。1024 batch × 4096 tok × float32 × 几个 prompt set 加起来大约几个 GB,不致命但也不可忽略。
- 个人 take: 这是这个系列里最"道法自然"的一篇——idea 几乎是显然的,但显然的事情有人认真做出来才是工程贡献。它揭示的事实(连续 epoch 50–70% token overlap)更值得关注:这意味着 RLVR 训练系统里大量算力在 deterministic 重生成,真正有信息的探索只发生在 30–50% 的尾部 token。
待验证问题清单 (按优先级)
- SPEC-RL × EAGLE-3 组合: verified prefix 之后的 continuation 阶段能否再叠 EAGLE-3 加速?
- Adaptive ℓ schedule(根据 batch KL 自动调): 收益多少?会不会让 sweet spot 漂移?
- 多轮 agentic RL 下,环境响应不可缓存,SPEC-RL 还能发挥多少?是否要拆 trajectory 到环境调用边界?
- 更大 model (32B / 70B) 上 reuse rate 是否还稳定在 50–70%?直觉上越大越稳。
- cold start 缓解: 第一 epoch 用 sample 的小子集 + 后续才上 SPEC-RL,实际墙钟收益?
- cache 写回粒度: 如果每个 step 都写回(而非每 epoch),会不会引入死循环式的低多样性?