Speculative Decoding
LLM 推理 · 投机解码 · 2022–2024

Speculative Decoding:draft 先猜,target 一次验证

自回归解码一次只出一个 token,瓶颈是 memory bandwidth,不是算术峰值。 论文让小模型先连续猜 γ 个 token,目标模型一次 forward 并行打分;拒绝采样保证输出分布仍然是目标模型的分布。 代价是 draft 成本、额外 KV 和 batch 下的收益衰减。

Leviathan et al. · Chen et al. · Li et al. (EAGLE) Leviathan v2 ↗ Chen v1 ↗ EAGLE v3 ↗ vLLM e740bad ↗
TL;DR · 30 秒抓住核心

解码的瓶颈是串行与访存:生成 K 个 token 要跑 K 次目标模型,每次都要把权重和 KV 从显存读一遍,算术单元经常闲着。 Speculative decoding 把“猜”和“验”分开:draft 模型串行猜 γ 个 token,目标模型一次并行验证,从而把多次目标模型调用压成一次。

核心是拒绝采样:draft 样本 x 以 min(1, p(x)/q(x)) 的概率被接受;一旦拒绝,就从 norm(max(0, p − q)) 重采样。 这样输出分布仍然是 p,draft 模型可以任意选,不需要重训目标模型。

期望输出 token 数是 (1 − αγ+1) / (1 − α),walltime 加速比是 E / (1 + γc)。 α 低、c 高或 γ 过大时,加速比可以小于 1;γ 不是越大越好。

vLLM 把 draft 侧做成可插拔:n-gram / suffix 不加载模型,draft_model 用独立小模型,EAGLE 用目标模型的 hidden states。 验证仍走 rejection sampler;KV 为 γ 个 draft 位置预留,拒绝后按 num_rejected 回滚计数。

3.4×
T5-XXL 11B / WMT EnDe / T5-small draft / temp=0 / γ=7 / α=0.75
2.6×
同设置 / temp=1;α 从 0.75 降到 0.62
3.1×
T5-XXL 11B / CNN/DM / T5-small draft / temp=0 / γ=5 / α=0.65
01

解码是串行的 memory-bound 循环,算力常常闲着

读完这节你应该知道:为什么“多猜几个 token”有收益空间,以及为什么直接接受 draft 会改变输出分布。

自回归解码的第 t 步只依赖前 t−1 个 token。生成 K 个 token 需要 K 次串行的目标模型调用;每次调用都要把模型权重和 KV Cache 从显存读到计算单元。对 Transformer 来说,这一步的算术量远小于访存量,因此 latency 主要由 memory bandwidth 决定,而不是 GPU 的峰值算力(Leviathan et al. §1;Chen et al. §1)。

论文的观察是:并非所有 token 都一样难。有些位置的目标分布很尖,小模型也能猜中;有些位置很难,必须由目标模型决定。如果把“猜”交给更便宜的模型,再把多个猜测拼成一个更长的序列,让目标模型一次 forward 并行验证,就能在目标模型调用次数不变的情况下多产出几个 token(§2.1)。

要求 A

并行:一次验证多个候选

目标模型对 prefix + x1 … xγ 做一次 causal forward,得到 γ+1 个位置的分布。并行验证利用的是“目标模型在 memory-bound 下还有空闲算术资源”这一条件,而不是减少总计算量。

要求 B

同分布:输出仍然是 p

如果直接接受 draft 的样本,输出分布会变成 q;如果只在 greedy 下比较 argmax,又只能覆盖贪心解码。要在随机采样下保持 p,需要一套接受/拒绝规则。

这两个要求合在一起,才是 speculative decoding 的问题定义:不改变目标模型的参数、架构和输出分布,只改变解码的调度方式。论文的贡献不是“用小模型猜”,而是给出了一个能纠正 draft 分布、并且对任意 draft 模型都成立的采样规则(§2.3、§3.6)。

关键判断

难点不在 draft,而在“拒绝之后怎么补”

draft 模型可以很粗糙,甚至可以是 n-gram;只要拒绝采样把“多出来的 q 质量”扣掉、把“p 比 q 多出来的质量”补上,输出就仍然是 p。这个修正步骤必须是串行调用之外的一次采样,不能靠再跑一次目标模型。

02

前人要么改模型,要么只能 greedy;两条路都换不来无损并行

读完这节你应该知道:speculative decoding 与量化/蒸馏、block/parallel decoding 的分界线,以及 EAGLE 在这条线上的位置。

路线 A

把每一步做便宜

量化、蒸馏、剪枝、early exit 和自适应计算都试图降低单步成本。它们通常要改权重、改架构或重新训练,而且可能改变输出质量;它们没有利用“有些 token 很容易猜”这一结构,也不提供“输出分布不变”的保证(§1 的相关工作)。

路线 B

一次生成多个 token

block/parallel decoding 让多个 token 同时参与计算,但早期方法往往只支持 greedy,或者用近似接受规则而偏离目标分布;它们没有把“随机采样下的精确性”作为约束(Leviathan et al. §1、§5;EAGLE §5 的相关工作回顾)。

Speculative decoding 的位置在两者之间:目标模型完全不动,draft 可以任意便宜,输出分布仍然按目标模型来。代价从“改模型”变成了“多一个 draft 模型 + 一次并行验证 + 拒绝后的重采样”。这也是它能在 off-the-shelf 模型上直接使用的原因(§1、§3.6)。

并列方法

EAGLE:换 draft 侧,不换接受规则

EAGLE 仍在 speculative sampling 框架内,但不在 token 层自回归:它在目标模型倒数第二层特征(second-to-top-layer,LM head 之前的 hidden state)上自回归,并把 token 序列前移一步,以减少特征预测的不确定性;draft 树用 tree attention 验证(EAGLE §1、§4.3)。 它改变的是“草稿怎么来”,不是“接受/拒绝怎么算”;因此拒绝采样仍然负责保持目标分布。论文报告 LLaMA2-Chat 70B 上的 latency 加速为 2.7–3.5×(§1),具体条件见 06。

Chen et al. 与 Leviathan et al. 是同期独立工作,核心接受规则相同;Chen 的重点是 Chinchilla 70B 的分布式 serving,Leviathan 的重点是算法分析、T5-XXL 实测和 walltime 模型。两者都强调:draft 模型不需要和 target 同架构,但必须能给出可比较的分布。

03

接受概率取 min(1,p/q),拒绝时从 p−q 的正部重采样

读完这节你应该知道:Algorithm 1 的每一步在做什么,以及为什么这个接受/拒绝规则能把 q 的样本修正成 p 的样本。

记目标模型分布为 p(x),draft 模型分布为 q(x),前缀固定。Algorithm 1 先让 draft 自回归采样 γ 个 token,再让目标模型对 prefix、prefix+x1、…、prefix+x1…xγ 做并行 forward,得到 p1 … pγ+1。然后从左到右逐个判断是否接受。

  1. Draft:对 i=1…γ,计算 qi ← Mq(prefix + x1…x(i−1)),采样 xi ~ qi。
  2. Verify:目标模型一次并行计算 p1 … pγ+1;每个位置都是一个完整的分布,不是一个 argmax。
  3. Accept:对每个位置抽 ri ~ U(0,1);若 ri ≤ pi(xi)/qi(xi) 则接受,否则停在第一个拒绝处。接受数为 n。
  4. Recover / Bonus:若 n < γ,从 norm(max(0, p(n+1) − q(n+1))) 采样一个修正 token;若全部接受,则从 p(γ+1) 采样 bonus token。输出 x1…xn, t。
ui ≤ pi(xi) / qi(xi) → 接受 xi
ui > pi(xi) / qi(xi) → 拒绝,x ~ norm(max(0, pi − qi))

Algorithm 1 的接受/拒绝规则;第一个拒绝之后的位置全部丢弃。

为什么这样就得到 p?把“输出的 token 是 x”拆成两条互斥路径:draft 抽到 x 且被接受,或者 draft 被拒绝、修正分布抽到 x。第一条路径的概率是 min(q(x), p(x));第二条路径的概率是“总拒绝质量”乘以修正分布在 x 上的质量。把 Σ max(0, p−q) = 1 − Σ min(p,q) 代入,第二项正好化简为 max(0, p(x) − q(x)),两项相加就是 p(x)。

P(output = x) = q(x)·min(1, p(x)/q(x)) + [1 − Σz min(p(z),q(z))] · max(0, p(x)−q(x)) / Σz max(0, p(z)−q(z))
= min(p(x), q(x)) + max(0, p(x) − q(x))
= p(x)

按 Algorithm 1 的接受/拒绝路径展开;论文在 Appendix A.1 给出同一结论。浮点实现是数值等价,不是逐位相同。

接受率本身也有闭式。对固定的前缀,draft 样本被接受的概率是 β = Σx min(p(x), q(x));把不同前缀上的 β 取期望,就得到论文的 α = E[β](Theorem 3.5、Corollary 3.6)。当 p=q 时 α=1;当两个分布支撑不交时 α=0;一般情况下 α 衡量 draft 与 target 的重叠程度,而不是“draft 的 argmax 命中率”。

β = Σx min(p(x), q(x)),α = E[β] = E[min(p,q)]

编号按 arXiv v2:Definition 3.1 定义 β;Theorem 3.5 给出 β 的表达式;Corollary 3.6 给出 α;Eq. (1) 给出期望 token 数;Theorem 3.8 给出 walltime。v2 没有 Theorem 3.1。

这个规则对 draft 模型没有限制:q 可以是小 Transformer、n-gram、suffix 树,或 EAGLE 的特征级 draft;只要 q 能给出每个候选 token 的概率,接受/拒绝就能把分布修正回 p(§3.6)。代价是 q 的概率必须在目标模型验证时可用;如果只保留 draft 的 argmax,接受规则会退化成一种特殊情形,而不是一般形式。

04

期望 token 数是 capped geometric;加速比被 c 和 γ 夹住

读完这节你应该知道:α、γ、c 三个量如何决定 E 和 S,以及为什么“更多 draft token”可能让加速比变负。

先看一次目标模型调用能产出多少 token。设 γ 个 draft 位置中接受的数量为 n,输出 token 数为 N = n + 1(修正 token 或 bonus token 至少有一个)。在“各位置独立、接受率同为 α”的简化下,P(n ≥ k) = αk,所以 E[N] = Σk=0γ αk,即论文的 Eq. (1)。

E[N] = 1 + α + α2 + … + αγ = (1 − αγ+1) / (1 − α)

Eq. (1):一次 Algorithm 1 调用生成的期望 token 数;α=1 时取极限 γ+1。

再看成本。设目标模型一次 forward 的耗时是 T,draft 模型一次 forward 的耗时是 cT;一次 speculative step 要跑 γ 次 draft 和 1 次目标模型(并行验证假设 γ+1 个目标位置不增加 walltime),总成本是 (γc + 1)T,产出 E[N] 个 token。因此理论 walltime 加速比是:

S(α, γ, c) = (1 − αγ+1) / [(1 − α)(1 + γc)]

Theorem 3.8;c = 单次 draft forward / 单次 target forward。§3.3 称,在 Mq 比 Mp 小约两个数量级的设置下 c 始终 <0.05、通常接近 0;但 Table 4 中 T5-large 的 c=0.11。

这个式子给出三条边界。第一,c ≈ 0 时 S = E[N],上界是 1/(1−α);n-gram 的 c 接近 0,所以即使 α 只有 0.2,也能有约 1.25× 的加速(§3.6)。第二,S > 1 等价于 c < α(1+α+…+αγ−1)/γ;右边不超过 α,所以 α < c 时所有 γ 都不划算。第三,即使 α 略高于 c,γ 太大也会让分母 1+γc 线性增长,把 S 拉回 1 以下;最优 γ 需要按 Figure 3 数值搜索。

预设的 α、γ 逐字取自 Table 2;c=0.05 取自 §3.3 的典型上界(Table 4 个别设置为 0.11)。模拟器只算 Eq. (1) 与 Theorem 3.8 的闭式值,Table 2 是实测 walltime,二者不必相等。

draft γ
7
accept α
0.75
E[N]
3.5995
期望输出 token 数
3.5995 tokens / 次目标 forward
理论 walltime 加速比
2.6663×
c < 盈亏平衡:S > 1

这是简化模型:α 被视为各位置相同的常数,c 被视为常数,且假设 γ+1 个目标位置可以并行而不增加 walltime。未覆盖分支见下面的手算块。

手算两组:α、γ 变化下的 E 与 S 固定 c=0.05,结果保留 4 位小数

简化模型:假设 γ 个 draft 位置的接受率都是 α 且相互独立,draft 单步成本 c 为常数,目标模型的 γ+1 个位置可以完全并行。这个模型只对应 Eq. (1) 与 Theorem 3.8 的闭式部分,不是对 vLLM 或论文实现的逐行还原。

算例 A(Table 2:EnDe / T5-small / temp=0):α=0.75,γ=7,c=0.05。

0.75^8 = 0.100112915
E = (1 − 0.100112915) / (1 − 0.75) = 3.59954834
S = 3.59954834 / (1 + 7×0.05) = 3.59954834 / 1.35 = 2.66633210
页面显示:E = 3.5995,S = 2.6663。

算例 B(Table 2:CNNDM / T5-small / temp=1):α=0.53,γ=5,c=0.05。

0.53^6 = 0.0221643611
E = (1 − 0.0221643611) / (1 − 0.53) = 2.08050136
S = 2.08050136 / (1 + 5×0.05) = 2.08050136 / 1.25 = 1.66440109
页面显示:E = 2.0805,S = 1.6644。

JS 与手算同口径:E 用 (1 − α^(γ+1)) / (1 − α),S 用 E / (1 + γc),只在显示时用 toFixed(4) 四舍五入;中间值不取整。

未覆盖:采样温度、top-k/top-p、batch 交互、KV 开销;也未建模 draft 与 target 的逐位置分布差异、硬件并行效率、实现固定开销和拒绝后的重采样方差。α 在论文里是特定模型/数据集上的期望,不是通用常数(§4.2)。

05

验证是一次并行 forward,KV 只回滚被拒绝的尾部位

读完这节你应该知道:draft→verify→accept 的数据流,vLLM 的 n-gram / draft model / EAGLE 路径,以及 speculative token 在 KV Cache 里怎么记账。

1 · Draft:小模型先猜 γ 个 token prompt prefix Mq × γ 自回归采样 x1 … xγ γ 个草稿 token 2 · Verify:target 一次并行 forward prefix + drafts Mp(prefix + x1…xγ) γ+1 个位置的 logits 一次算完 p1 … p(γ+1) 目标分布 3 · Accept / Recover:从左到右 u ≤ p(x)/q(x) ? 逐个接受 n 个接受 首个拒绝停止 n + 1 tokens 至少 1,至多 γ+1 拒绝位置:从 norm(max(0, p − q)) 重采样;全接受:从 p(γ+1) 取 bonus
图 1 · 按 Algorithm 1 重绘的 draft→verify→accept 数据流。不是论文原图;重点是 target 只做一次 forward,接受/拒绝在采样器里逐位置进行。

vLLM 的验证路径与这个数据流对应:scheduler 把每个请求的 draft token 放进 scheduled_spec_decode_tokens;SpecDecodeMetadata 把 batch 内的 draft token 拉平,target_logits_indices 和 bonus_logits_indices 指向需要验证的位置与 bonus 位置(vllm/v1/spec_decode/metadata.py:10-24)。rejection sampler 按 cu_num_draft_tokens 逐请求扫描,输出 accepted + recovered + bonus(vllm/v1/sample/rejection_sampler.py:50-69,432-440,525-535)。

源码对照:vLLM e740bad;vllm/v1/spec_decode/ 的 V1 proposer,以及 docs/features/speculative_decoding/ 的 §Method Selection。V2 的 speculator 在 vllm/v1/worker/gpu/spec_decode/,配置面相同。

method草稿来源关键配置成本与限制
ngram 在上下文里找最长的后缀 n-gram,复制匹配位置之后的 k 个 token(vllm/v1/spec_decode/ngram_proposer.py:210) num_speculative_tokens;prompt_lookup_min/max 默认 5/5(vllm/v1/worker/gpu/spec_decode/ngram/config.py:21-26) 无额外模型权重,c 接近 0;依赖重复文本,α 随任务变化
suffix 后缀树 / 全局缓存匹配;SuffixDecodingProposer(vllm/v1/spec_decode/suffix_decoding.py:10) suffix_decoding_max_tree_depth=24、suffix_decoding_max_cached_requests=10000(vllm/config/speculative.py:592,596) 无额外模型权重;树越深,draft 越长,但拒绝后浪费也越多
draft_model 独立小模型;默认要求与 target 同 vocab model、draft_tensor_parallel_size、num_speculative_tokens;use_heterogeneous_vocab 可选(要求 greedy draft) 多一份权重和 KV;DraftModelProposer 当前要求 draft TP 与 target TP 相同(vllm/v1/spec_decode/draft_model.py:64-78)
eagle / eagle3 EAGLE head 读 target hidden states;EagleProposer(pass_hidden_states_to_model=True)(vllm/v1/spec_decode/eagle.py:10-21) model 是 EAGLE checkpoint;num_speculative_tokens;parallel_drafting 仅 V1,V2 已弃用 多一份 draft head / KV;num_lookahead_tokens = num_speculative_tokens(vllm/v1/worker/gpu/spec_decode/eagle/config.py:56-58)
配置默认值

SpeculativeConfig 在 e740bad 的默认面

  • num_speculative_tokens 默认 None,必须 >0;若仍为 None 且 draft config 没有 n_predict,启动时报错(vllm/config/speculative.py:451,1634-1637)。
  • model 默认 None;method 默认 None,vLLM 会从 checkpoint 推断(vllm/config/speculative.py:454,457,1338-1397)。
  • draft_tensor_parallel_size 默认 None;配置注释允许 1 或与 target TP 相同,但当前 DraftModelProposer 直接要求两者相同(vllm/config/speculative.py:477;draft_model.py:64-78)。
  • rejection_sample_method="standard";draft_sample_method="greedy";parallel_drafting=False(vllm/config/speculative.py:622,694,561)。页面只展开 standard;synthetic/block/fly 是 vLLM 的扩展验证器,不在此展开。

命令行入口是 --speculative-config(别名 -sc),或三个快捷参数 --spec-method、--spec-model、--spec-tokens;快捷参数与 JSON 里的同名 key 互斥(vllm/engine/arg_utils.py:1895-1899,2142-2148)。旧写法 --speculative-model / --num-speculative-tokens 已弃用(docs/features/speculative_decoding/draft_model.md)。

vllm serve <target-model> \
  --speculative-config '{
    "method": "draft_model",
    "model": "<draft-model>",
    "num_speculative_tokens": 5
  }'

KV Cache:为 γ 个 draft 位置预留,拒绝后逻辑回滚

目标模型验证时,一次 forward 会为 prefix + x1…xγ 的所有位置写 KV;因果注意力保证每个位置只看到前面的 token,所以 γ+1 个位置的 logits 可以在同一次 forward 里算出。scheduler 为 spec decode 预留 1 + num_spec_tokens + num_lookahead_tokens 个 token 的 block,并按 block size 取整(vllm/v1/core/sched/scheduler.py:376-382)。

采样完成后,scheduler 从 num_computed_tokens 和 num_output_placeholders 中减去 num_rejected(vllm/v1/core/sched/scheduler.py:2430-2447)。这意味着被拒绝的尾部位不再计入“已计算 token”;物理块仍由 KV cache manager 按 block 管理,页面不展开块级回收细节。draft 模型也有自己的 KV Cache;EAGLE 的 lookahead 数等于 num_speculative_tokens,所以它的 KV 预留会随 γ 增长(vllm/v1/worker/gpu/spec_decode/eagle/config.py:56-58)。

指标口径也要分清:vLLM 的 mean_acceptance_length = 1 + num_accepted / num_spec_steps 把 bonus token 算进去,和 Eq. (1) 的 E[N] 同量纲;draft_acceptance_rate = num_accepted / num_draft_tokens 是聚合接受比例,不是论文的 per-position α(vllm/v1/metrics/stats.py:407-409)。

06

T5-XXL 上实测 1.4–3.4×(T5-small 2.3–3.4×);draft 更大不一定更快

读完这节你应该知道:论文在哪些模型/数据集上测到了什么,以及 Table 2 里最反直觉的两三行。

论文在 T5-XXL 11B 上比较了三种 draft 规模、两个任务和两种采样方式。Table 2 的完整 12 行转录如下;Speed 是相对 T5X 自回归实现的 walltime,条件是单 TPU-v4、batch size 1。

Table 2(Leviathan et al. v2,全表转录):目标模型 T5-XXL 11B;Mq 为 T5-small 77M / T5-base 250M / T5-large 800M;EnDe = WMT EnDe,CNNDM = CNN/DM;Temp=0 为 argmax,Temp=1 为标准采样。

TaskMqTempγαSpeed
EnDeT5-small070.753.4×
EnDeT5-base070.82.8×
EnDeT5-large070.821.7×
EnDeT5-small170.622.6×
EnDeT5-base150.682.4×
EnDeT5-large130.711.4×
CNNDMT5-small050.653.1×
CNNDMT5-base050.733.0×
CNNDMT5-large030.742.2×
CNNDMT5-small150.532.3×
CNNDMT5-base130.552.2×
CNNDMT5-large130.561.7×

三个反直觉点:

并列证据:Chen et al. 在 Chinchilla 70B 的分布式 serving 上报告 2–2.5× 解码加速(§1);EAGLE 在 LLaMA2-Chat 70B 上报告 2.7–3.5× latency 加速、吞吐约 2×(§1)。这些数字的模型、硬件、batch size 和数据集与 Table 2 不同,不能直接横向比较;它们说明的是同一条机制在不同 draft 侧实现上都能成立,而不是某个固定倍率。

07

α 低于 c 时,验证越认真越慢

读完这节你应该知道:speculative decoding 在哪些条件下会失效,以及部署时最先撞到的是哪几个约束。

负加速:α 低、c 高或 γ 过大

由 Theorem 3.8,S > 1 等价于 c < α(1+α+…+αγ−1)/γ。右边不超过 α,所以 α < c 时任何 γ 都无法把 S 拉过 1;即使 α 略高于 c,γ 太大也会让分母 1+γc 线性增长,S 先升后降。论文用 Figure 3 数值搜索最优 γ;Corollary 3.9 给出 α>c 时至少存在 γ=1 的改进,下界是 (1+α)/(1+c)。

Chen et al. 的 Figure 1 也显示:lookahead K 增大后,平均加速比会平台化甚至回退;XSum 在 K=3 时最优,而且延迟方差随 K 增大(§6.3)。这和 Eq. (1) 的饱和、Theorem 3.8 的线性分母是同一件事的两个视角:更多 draft token 带来更高的 E,但也带来更高的验证成本和更大的方差。

draft 模型显存:多一份权重和一份 KV

draft_model 和 EAGLE 都不是“免费”的:它们要多加载一份权重,还要为 draft 的 γ 个位置维护 KV。EAGLE 论文在 FP16 下报告:Vicuna 7B 的最大 batch size 从 8 降到 7(24GB RTX 3090);LLaMA2-Chat 70B 从 5 降到 4(4×A100 40GB)。论文把这归因于 EAGLE 略高的显存占用和 tree attention 的额外计算(§4.4)。

vLLM 里,draft_tensor_parallel_size 控制 draft 的 TP;DraftModelProposer 当前要求它和 target TP 相同(draft_model.py:64-78)。max_model_len 可以单独限制 draft 的上下文长度(vllm/config/speculative.py:502)。EAGLE 的 num_lookahead_tokens = num_speculative_tokens 还会让 scheduler 为每个请求预留更多 KV block(eagle/config.py:56-58;scheduler.py:376-382)。

batch 变大,收益通常变小

Leviathan 的 Table 2 是 batch size 1、单 TPU-v4 的 latency 数据;它不代表高 QPS 的吞吐。EAGLE 论文在 MT-bench、temp=0 下报告 LLaMA2-Chat 70B 的加速比随 batch size 从 3.01×(bs=1)降到 2.40×(bs=4),吞吐约 1.99×(Table 7)。原因是 batch 越大,目标模型越接近 compute-bound,验证阶段多出来的 γ 个位置不再“免费”。

vLLM 文档的定性表也把 EAGLE 标为“低 QPS 高收益、高 QPS 中高收益”,把 n-gram 标为“低 QPS 低到中收益、高 QPS 中收益”(docs/features/speculative_decoding/README.md §Method Selection)。具体拐点取决于模型、流量、硬件和 draft 质量,不能只看一个倍率。

KV 与采样:两条容易被忽略的口径

KV 方面,验证阶段要为 γ 个 draft 位置写 KV;scheduler 预留 1 + γ + lookahead 个 token 的 block,采样后从 num_computed_tokens 和 num_output_placeholders 中减去 num_rejected(scheduler.py:376-382,2430-2447)。逻辑回滚不等于物理块立刻释放;块仍按 block 管理。draft 模型自己的 KV 也要算进显存预算。

采样方面,论文 §2.2 把 argmax、temperature、top-k、top-p 都折算成“调整后的 p、q”,再套用同一拒绝采样;Table 2 的 temp=0 > temp=1 是 T5-XXL 上的实验观察(§4.1)。vLLM 的实现是另一层:rejection_sample_method 默认 standard,draft_sample_method 默认 greedy;greedy 把 draft 分布当成 one-hot,probabilistic 才保留完整 q 做概率比检验(vllm/config/speculative.py:622,694;docs/features/speculative_decoding/README.md)。不要把“vLLM 默认 greedy draft”直接等同于“target 用 temperature=0”。

适合

memory-bound、低到中 QPS、延迟敏感

目标模型有闲置算术资源,draft 的 α 足够高,且显存能放下 draft 权重和 KV。此时 speculative decoding 把 memory bandwidth 的瓶颈转成一次并行验证,收益来自“一次目标 forward 出多个 token”。

不适合

已经 compute-bound、batch 很大、没有好 draft

推断(依据:§3.3 的并行假设、EAGLE §4.4 的 batch 结果、vLLM 文档的 QPS 定位):当目标模型已经被 batch 填满、验证的 γ 个位置不再免费,或 α 低到覆盖不了 c 时,speculative decoding 会增加延迟而不是降低延迟;没有合适 draft 或显存紧张时,也应优先考虑其他优化。