Speculative Decoding 全家桶:从 n-gram 到 P-EAGLE/DFlash/DSpark

20 分钟阅读

Speculative Decoding 全家桶:从 n-gram 到 P-EAGLE/DFlash/DSpark

前情说明:《训练时代的黄昏》讲了推理时代的到来。当推理成为主战场,如何让每次 forward 吐更多 token就成了核心问题。这一篇专门整理 Speculative Decoding 这个杀手锏——从最朴素的 n-gram,到 SOTA 的 parallel drafting,一次讲透。 素材来自 vLLM Blog《Inside vLLM》(2025-09-05) 里的 speculative decoding 章节 + 《Parallel All the Way Down》(2026-07-28) 这篇最新的 parallel drafting 综述。


起手:Decode 阶段为什么慢?

先回顾一下 《现代 LLM 推理引擎剖析》 里那张表:

阶段 计算特征 瓶颈
Prefill 一次跑 N 个 token 算力 bound
Decode 每次跑 1 个 token 带宽 bound

Decode 阶段的根本痛点是:

每生成 1 个 token,都要把整个模型权重从 HBM 读一遍——B200 也扛不住。

以 Llama 70B 为例(140GB weights)、H100 HBM 带宽 3.35TB/s,理论极限是每秒 24 次 decode。也就是单请求最快 24 token/s——B 卡稍强一点但也是同个数量级。

要突破这个墙有三条路:

  1. 更大 batch——把多个 request 塞一起,摊薄单次 forward 的开销(continuous batching)
  2. 更多 KV 复用——减少不必要的 forward(prefix caching)
  3. 一次 forward 出多个 token——Speculative Decoding

前两条各家都在做,前面已经讲了。这篇专门讲第三条。


一、Speculative Decoding 的核心思想

1.1 一个反直觉的观察

大模型 forward 一次到底在算什么?——给出下一个 token 的概率分布。

但如果 forward 时输入是 [t0, t1, ..., tN-1],它会同时给出每个位置的下一个 token 分布——也就是 [t1|t0], [t2|t0,t1], ..., [tN|t0..tN-1] 全都能算。这是 causal attention 的天然属性。

只是我们平时只用最后一个!中间的都浪费了。

Speculative decoding 的核心洞察:如果能提前猜出接下来 K 个 token,就把它们一起塞进 forward,一次拿回 K+1 个位置的分布进行验证——一次 forward 出多个 token。

1.2 完整算法(有严格数学保证的版本)

标准 Speculative Decoding 算法:

  1. Draft:小模型跑一次,一口气提出 K 个候选 token [d1, d2, ..., dK]
  2. Verify:大模型跑一次 forward,输入 [context, d1, d2, ..., dK] → 拿到 K+1 个位置的分布 p_large
  3. Accept/Reject:从左到右扫这 K 个 token:
    • 计算 α = min(1, p_large(di) / p_draft(di))
    • 以概率 α 接受 di,否则拒绝并停止
  4. 修正采样:如果在位置 i 拒绝了,从修正分布 (p_large - p_draft)+ (clamp min 0,归一化)采一个新 token
  5. Bonus:如果 K 个全接受,大模型的第 K+1 个采样是 free 的

关键的数学保证:

这套 accept/reject 规则的期望输出分布严格等于"逐 token 从大模型采样"的分布。

不是近似!是严格等价! 一份形式化证明在原始 speculative decoding paper 里。这是这个算法的灵魂——你可以为速度做设计,但不用担心质量下降一分一毫。

一次 forward 期望能吐 E[tokens] = 1 + Σ acceptance_probs,通常在 3-8 之间。speculative decoding 是 lossless acceleration。


二、Drafter 选型:小模型太慢,大模型 rehash 也不对

朴素方案是用一个独立的小模型做 draft(比如 Llama 7B 给 Llama 70B 打草稿)。但vLLM V1 不支持这种方案。为什么?

几个原因:

  1. 小模型也有开销——weight loading、kernel launch、scheduling 都要另一套。跑 Llama 7B forward 不便宜
  2. 小模型和大模型分布差异大——acceptance rate 低(40% 左右),效益差
  3. 调度复杂度爆炸——两个引擎实例要协调 batch,几乎不可能优雅

所以 vLLM 走了三条**"轻量 drafter"**路线:n-gram、EAGLE、Medusa。

2.1 n-gram —— 零训练成本

思想:从当前 prompt 里搜历史匹配窗口。

具体算法:

def ngram_draft(context, prompt_lookup_max=5, prompt_lookup_min=3, K=3):
    for window in range(prompt_lookup_max, prompt_lookup_min - 1, -1):
        pattern = context[-window:]  # 取末尾 window 个 token
        for i in range(len(context) - window):
            if context[i:i+window] == pattern:
                # 命中!返回该位置之后的 K 个 token 当草稿
                return context[i+window : i+window+K]
    return None  # 未命中

适用场景:

  • 代码补全:变量名、函数调用重复率高
  • 结构化文档:JSON schema、SQL 语法有大量重复模板
  • 对话:"I am"、"The answer is" 之类高频短语

优势:零训练、零显存、零开销 劣势:只对"字面重复"起作用,创造性文本收益低

2.2 EAGLE —— 大模型"外科手术"

思想:不用独立小模型,而是"减配版大模型"。

EAGLE 的做法:

原大模型:  Embedding → Transformer × N 层 → LM Head
                          ↓ 换掉
EAGLE:    Embedding → Lightweight MLP × 1 层 → LM Head
              (共享)          (新训练)          (共享)

保留大模型的 embedding 层(因为语义表征很强)和 LM Head(因为 vocabulary 映射不能变),只把中间 N 层 transformer 换成轻量 MLP(几层就够)。

关键洞察:EAGLE 直接消费大模型的 hidden state——不是从原始 token embedding 出发,而是从 verify 阶段大模型已经算过的 hidden state 出发。这个 hidden state 已经浓缩了丰富的上下文信息,用轻量网络就能高效预测下一个 token。

训练成本极小(几十亿 token 的微调即可),acceptance rate 高达 70-80%。

2.3 EAGLE-3 —— 递归 drafting 的极致

EAGLE-3 是 EAGLE 的进化版:递归自回归 drafting——

Step 1: 大模型算完 → 拿 hidden state h0
Step 2: EAGLE-3 输入 h0 → 输出 d1 + 新 hidden state h1
Step 3: EAGLE-3 输入 h1 → 输出 d2 + 新 hidden state h2
...
Step K: EAGLE-3 输入 hK-1 → 输出 dK
Step K+1: 大模型 verify [d1, ..., dK]

递归指的是EAGLE-3 用自己上一步的输出作为下一步的输入——本质上是一个"精简版自回归"。

收益:K 变大时(比如 K=7),EAGLE-3 比原始 EAGLE 的 acceptance rate 明显更高,因为递归结构能保持 token 之间的因果关系。

2.4 Medusa —— 加多个头,并行预测

思想:不递归,用并行的多个 LM Head 直接一次预测 K 个未来 token。

              ┌──→ Head_1 → 位置 t+1 的分布
              │
Hidden state ─┼──→ Head_2 → 位置 t+2 的分布
              │
              ├──→ Head_3 → 位置 t+3 的分布
              │
              └──→ Head_K → 位置 t+K 的分布

每个 head 都是独立训练的线性层。看似简单粗暴,但很有效。

优势:一次 forward 就出 K 个草稿(不像 EAGLE-3 要 K 次 forward) 劣势:远处 token 的预测精度低——因为它不知道近处的 t+1, t+2 是什么,只是各自独立猜

Medusa 是 parallel drafting 的鼻祖——虽然效果不算最好,但思路开了后来 P-EAGLE / DFlash / DSpark 的先河。


三、Autoregressive 的诅咒:为什么 EAGLE-3 也不够

到 EAGLE-3 为止,drafter 都是"更快的 autoregressive 模型"——draft 阶段还是逐 token 生成,只是每个 token 便宜。

看架构就明白瓶颈在哪:

Auto-regressive drafting (EAGLE/EAGLE-3):
    Draft 1 forward → t+1
    Draft 2 forward → t+2  (需要 t+1)
    Draft 3 forward → t+3  (需要 t+1, t+2)
    ...
    Draft K forward → t+K
    大模型 verify [t+1, ..., t+K] (1 次)
    ──────────────────────────
    共 K+1 次 forward

问题:Drafter 是 K 次 forward,一次都少不了。

这带来两个生产上的痛点:

  1. 限制 drafter 容量——drafter 必须极其轻量,否则 K 次 forward 的总开销超过 verify 阶段省的时间
  2. K 是难调的超参——K 太大 drafter 拖后腿,K 太小 verify 不划算。最优 K 依赖 workload 和硬件负载,运维要不停调

vLLM 团队 2026-07-28 那篇 《Parallel All the Way Down》 直接把这个问题命名为——"the structural ceiling rooted in the way draft tokens are generated"。


四、Parallel Drafting:一次 forward 出 K 个 token

Parallel drafting 的核心动作:让 drafter 一次 forward 直接输出整个 K 个 token 的 block。

Parallel drafting:
    Drafter 1 次 forward → [t+1, t+2, ..., t+K] block
    大模型 verify 1 次    → 接受/拒绝
    ─────────────────────
    共 2 次 forward

从 K+1 次到 2 次 forward——drafter 开销跟 K 无关!

这解锁两个巨大的架构自由度:

  1. Drafter 可以更大更强——反正只跑 1 次,容量提升不加成本 → 更高 acceptance rate
  2. K 不再敏感——想 speculate 多少就多少(受 verify 硬件限制),运维不用调

Medusa 是最早的 parallel drafter,但精度太差没跑起来。真正让 parallel drafting 起飞的是 2026 上半年这三个算法:P-EAGLE、DFlash、DSpark。

4.1 P-EAGLE —— Parallel EAGLE

核心思想:把 EAGLE 的 hidden state 输入方式保留,但输出层改成一次预测多个位置。

输入: verify 模型的 hidden state h_t
      ↓
      Lightweight transformer / MLP
      ↓
输出:  [t+1 分布, t+2 分布, ..., t+K 分布]  ← 一次全出

训练技巧:block sparsification

一个致命的训练问题——如果每个训练样本都要预测未来 K 个 token 的完整分布,loss matrix 是 N×K,内存和算力开销都爆炸。

P-EAGLE 的解法:沿 K 维度做衰减采样——近处位置(t+1、t+2)loss 权重高,远处位置(t+K)大部分 dropout 掉。

直觉合理——近处预测本来就重要,远处即使不训练也无所谓(反正 acceptance rate 会自然下降)。

4.2 DFlash —— 把 hidden state 塞进 KV cache

核心思想:更进一步——不把 verify 的 hidden state 作为 input feature,而是直接注入 drafter 的 KV cache。

Standard EAGLE:
    input = [verify_hidden_state] → drafter → output

DFlash:
    input = [regular tokens]      → drafter (KV cache 里被塞了 verify hidden state)
                                                ↓
                                         Block Diffusion output

技术细节:

  • Verify hidden state 通过投影层进入 drafter 的 attention KV cache
  • Drafter 用 block diffusion(借鉴图像扩散)一次生成 K 个 token
  • Attention 变成双向——每个 draft token 能看到整个 block

训练技巧:sequence length sparsification

不同于 P-EAGLE 沿 K 维稀疏,DFlash 沿 N 维(序列长度) 稀疏——只在随机 anchor 点计算 block loss。

这是另一种权衡——P-EAGLE 训练所有位置但每位置只训近处 token;DFlash 训所有 K 个位置但只在少数序列位置。各有取舍。

4.3 DSpark —— DFlash + AR 校正 + Confidence Head

DSpark 是 2026 年 SOTA。它建立在 DFlash 的 parallel backbone 上,加了两个创新:

创新 1:Autoregressive 校正头

Parallel drafter (DFlash 主干)
    ↓
[初始 K 个候选]
    ↓
Lightweight AR head (逐 token 校正)
    ↓
[更精细的 K 个候选]

Parallel 生成保证速度,AR 校正保证 token 间的因果一致性。兼得两者优点。

创新 2:Confidence Head

问题:Parallel drafting 生成的 K 个 token 里,有些置信度低的很可能被 verify 拒绝。全部塞给 verify 会浪费 verify 的算力。

DSpark 的解法——加一个 confidence head:

draft tokens → confidence head → 每个 token 的置信度
                                    ↓
                        选择性把高置信度的送 verify

低置信度的 draft token 在到 verify 之前就被过滤——verify 只处理"大概率会被接受"的候选,verify 侧算力开销显著下降。

这是端到端优化——不只优化 drafter,还优化 verify 的算力分配。


五、Benchmarks:跑起来有多快?

vLLM 2026-07-28 那篇 blog 给了实际数据(相比 EAGLE-3 的对比):

Model 算法 场景 硬件
Qwen3-8B P-EAGLE 数学推理 (GSM8k) 1×A100
Qwen3-30B-A3B DFlash 代码 (HumanEval) 2×A100
gemma-4-31B-it DSpark 代码 (HumanEval) 2×A100

结果(相比 EAGLE-3 baseline):

  • P-EAGLE:在小模型 + reasoning 场景明显提升
  • DFlash:在 MoE + coding 场景明显提升
  • DSpark:几乎所有场景都是当前 SOTA

blog 里的具体数字有过一次 errata(07-29 更新,环境配置错了修正),但结论没变——parallel drafting > autoregressive drafting。

5.1 在 vLLM 中启用

启用 parallel drafting 就一个 config:

vllm serve Qwen/Qwen3-30B-A3B \
    --tensor-parallel-size 2 \
    --reasoning-parser qwen3 \
    --speculative-config '{
        "model": "RedHatAI/Qwen3-30B-A3B-speculator.dflash",
        "num_speculative_tokens": 7,
        "method": "dflash"
    }'

用户几乎无感——OpenAI-compatible API 保持不变,底层多了个 parallel drafter 在偷偷加速。


六、Speculators 项目:不只是算法,是生态

vLLM 联合 RedHat AI 发布 Speculators 项目——统一的 speculative decoding 生态:

  • 训练 pipeline:用相同 API 训 EAGLE / P-EAGLE / DFlash / DSpark
  • HuggingFace Hub:所有预训练 speculator 可以直接下载
  • vLLM 集成:与 vLLM 深度耦合,config 一行搞定

这是 vLLM 从"引擎"到"参考实现 + 生态"演进的又一步(详见 《vLLM 三年演进史》)。

为什么这个生态很重要? 因为 speculative decoding 有个致命的训练成本问题——每一个大模型都要配一个专门训练的 speculator。市面上有 100 个开源模型,就要 100 个 speculator。散着放没人管,用户想用就得自己训。

Speculators 项目把这个碎片化生态统一起来——每个 SOTA 模型发布时都同时发布对应的 speculator,训练 pipeline 标准化。这跟 vLLM Day-0 支持新模型的策略完全一致。


七、还有哪些没讲到?

Speculative decoding 是个活跃研究领域,本文覆盖的只是主线。还有几条支线可以了解:

MTP (Multi-Token Prediction)——DeepSeek V3 用的方案,把 speculative 集成到模型 pretrain 里。模型直接学会一次预测多 token,不需要额外 drafter。Kimi K3 也用了这个思路(详见 vLLM Kimi K3 blog)。

PARD——早期 parallel drafting 方案,DFlash 前身之一

Lookahead Decoding——不需要 drafter,用启发式规则从生成历史里找模式。开销极小,但 acceptance rate 也不高。

Retrieval-based Drafting——从外部知识库检索候选 token(比如代码补全场景),适合特定 domain。

Sampling-based (Blockwise Parallel)——Google 的方案,训练时就让模型学会多 head 预测

这个方向 2026 年会继续爆发——因为它是唯一能突破 decode 带宽墙的通用工具,任何 workload、任何硬件都能吃到收益。


八、把 speculative decoding 放回大图景

回到 fin 那三篇文章的宿命论:

  • Token throughput = HBM size × HBM 带宽(HBM 是宿命)
  • 但 speculative decoding 让 每次读一遍 HBM = 吐 5-8 个 token 而不是 1 个
  • 相当于在同样的硬件上把 throughput 拉高 5-8 倍

这是软件优化的极限吗?是——除非 attention 机制被颠覆(fin 说不太可能),否则 speculative decoding 就是 decode 阶段的最后一公里优化。

未来看点:

  • Drafter 会越来越大——parallel drafting 解锁了 drafter 容量
  • Verify 会越来越智能——DSpark 的 confidence head 只是第一步
  • 训练侧集成会成为标配——MTP 这种"pretrain 时就学多 token 预测"会普及
  • 异构硬件配合——drafter 在小 GPU / CPU 上跑,verify 在大 GPU 上跑(跟 AFD Plugin 类似思路)

Speculative decoding 是"token 时代"的最后一个宏观突破。它把 fin 讲的那个 memory-bandwidth 天花板软软地穿透了几倍——不是靠新硬件,是靠算法的智慧。

这就是软件的美——它总能在物理的天花板下面,再找出一些新的空间。


附:素材来源

主线

论文

代码 & 生态

姊妹篇


整理于 2026-07-31。原文技术细节归属 vLLM 团队和相关论文作者。中文表达可能有解读偏差,请以原文为准。

Zoe

Written by

Zoe

AI Infra Engineer · LLM Serving · GPU/RDMA · 造工具的偏执狂

评论