Speculative Decoding#

前三节讲的是标准的 prefill + autoregressive decode 路径。这一节处理一种不同的执行模式:speculative decoding。它让 ModelRunner 在每一步不再只生成一个 token,而是先用一个轻量 draft model 猜多个 token,再用 target model 一次性验证,从而在不改变输出分布的前提下显著提升吞吐。

这一节解决什么问题#

这一节主要回答三件事:

  1. speculative decoding 在 SGLang 里怎样被表达成执行配置;
  2. draft + verify 两阶段怎样和 ForwardBatch / ModelRunner 结合;
  3. 为什么 speculative decoding 不改变输出分布,以及这个保证在 SGLang 里怎样落实。

一张图先看整体结构#

flowchart TB
    subgraph Draft["Draft 阶段(轻量模型)"]
        A["当前 token"] --> B["draft model forward x K 步"]
        B --> C["候选 token 序列 [t1, t2, ..., tK]"]
    end

    subgraph Verify["Verify 阶段(target model)"]
        C --> D["target model 并行 forward K+1 个位置"]
        D --> E["接受/拒绝每个候选 token"]
    end

    subgraph Accept["输出"]
        E --> F["接受的 token(可能 0 ~ K 个)"]
        F --> G["最多接受 K 个 + 1 个 bonus token"]
    end

这张图最值得记住的一点是:target model 只需要跑一次 forward(K+1 个位置并行),就能完成 K 个 token 的验证。当 draft model 的猜测大部分正确时,一次 target forward 等于原本 K 次 autoregressive decode。

speculative decoding 怎样进入 ModelRunner#

SGLang 的 speculative decoding 通过 speculative_algorithm 参数进入执行层。从 ModelRunner 的初始化可以看到:

self.spec_algorithm = SpeculativeAlgorithm.from_string(
    server_args.speculative_algorithm
)

目前支持的主要模式包括:

  • EAGLE / EAGLE2:使用专门训练的 eagle draft model
  • DRAFT_MODEL:使用另一个小模型作为 draft model(如用 7B 草稿 70B)
  • MEDUSA:多头 draft,不用单独的 draft model,在 target model 上加额外预测头

这个字段在 ForwardBatch 建立时会影响前向的执行路径,让 ModelRunner 在 decode 阶段进入 speculative 路径,而不是标准的单 token 路径。

两阶段执行:draft forward 和 verify forward#

Draft 阶段

Draft model 对当前 context 做 K 步 autoregressive 前向,得到 K 个候选 token。这 K 步通常很快,因为 draft model 参数量远小于 target model(EAGLE 的 draft 部分只有一层 transformer)。

Draft 阶段的输出不只是 K 个 token,还包括这 K 个位置的 logits 分布 q(t_i),用于后面的 rejection sampling。

Verify 阶段

Target model 对 [原始 context + K 个 draft tokens] 做一次并行 forward,计算 K+1 个位置的 logits 分布 p(t_i)

  • 前 K 个位置对应 draft tokens 的验证;
  • 第 K+1 个位置是在所有 draft token 都被接受时的 bonus token。

这次 forward 和标准 prefill 在计算上没有本质区别,差别只在于 K+1 个 token 是"待验证的候选",而不是"需要算 KV 的 prompt"。

接受/拒绝:为什么不改变输出分布#

这是 speculative decoding 最关键的性质:token 的最终分布和完全用 target model autoregressive decode 一样。

对第 i 个 draft token t_i,接受条件是:

uniform(0, 1) < min(1, p(t_i) / q(t_i))
  • 如果 p(t_i) >= q(t_i)(target 认为 draft 猜的这个 token 比 draft 自己更确信),直接接受;
  • 如果 p(t_i) < q(t_i)(target 没有 draft 那么确信),以 p/q 的概率接受;
  • 如果被拒绝,从修正分布 max(0, p - q) / Z 中重新采样一个 token。

这套 rejection sampling 机制保证:无论 draft 猜的好不好,最终的输出分布都等价于直接用 target model 采样。

在 SGLang 里,这套逻辑在 speculative_algorithm.verify_step(...) 中实现,发生在 verify forward 之后、output processing 之前。

什么时候 speculative decoding 有收益#

收益来自 draft model 的接受率。如果 K 个 draft token 中有 k 个被接受,这次 target forward 等于完成了 k+1 次 decode(k 个接受的 + 1 个 bonus token),吞吐提升约为 (k+1) / 1。

收益受以下因素影响:

有利条件

  • Draft model 和 target model 在该分布上接近(例如同系列模型);
  • 生成内容有较强规律性(代码、格式化输出),draft 容易猜对;
  • Batch size 小(target verify forward 的 batch overhead 相对划算)。

不利条件

  • 高温度采样(随机性高,draft 难以准确预测);
  • Batch size 很大时,K 个 draft forward 的总计算量接近跑 K 次 target forward。

这也是为什么 speculative decoding 在大 batch 高并发推理场景下收益有限,但在 interactive / low-latency 场景下(batch size 小、对 TTFT 和 token latency 敏感)价值最显著。

KV cache 在 speculative decoding 里怎样处理#

Speculative decoding 引入了一个 KV cache 管理上的复杂性:draft 阶段产生的 KV 可能最终不被 target 接受,这些"推测性 KV"不能直接 commit 到 RadixCache

SGLang 的处理方式是:

  1. Draft forward 生成的 KV 写入 speculative_kv_buffer,而不是正式的 token_to_kv_pool
  2. Verify 之后,只有被接受的 token 对应的 KV 才 commit 到主 KV pool;
  3. 拒绝的 token 及其 KV 直接丢弃,不需要回滚。

这个设计的代价是需要额外的 buffer 来暂存 draft KV,但避免了接受/拒绝结果出来之前就污染主 KV pool。

调试 speculative decoding 时先看哪里#

如果你看到的现象是:

  • 开启了 speculative decoding,但吞吐没有明显提升;
  • 某类请求开启 spec decoding 后输出质量下降(这通常意味着实现有 bug,不该发生);
  • 延迟比普通 decode 更高;

更稳的顺序通常是:

  1. 先确认 draft model 的 acceptance rate 是否达到预期(通常需要 > 0.7 才有净收益);
  2. 如果 acceptance rate 低,检查 draft model 和 target model 是否来自同一 family;
  3. 如果延迟更高,看当前 batch size 是否太大,导致 draft K 次 forward 的开销超过了 target verify 的收益;
  4. 如果输出分布有疑问,回到 verify_step 里确认 rejection sampling 的实现是否正确。

小结#

这一节真正要建立的是一个框架:

  • speculative decoding 不改变输出分布——这是它能被用在生产上的前提;
  • 它通过 draft + verify 两阶段实现"一次 target forward 等于多个 decode 步";
  • 收益依赖 draft acceptance rate,而 acceptance rate 依赖 draft model 质量和采样参数。

理解了这个框架,再看 SGLang 里具体的 EAGLEDRAFT_MODELMEDUSA 实现时,就能更快判断它们在哪个环节做了什么权衡。