LLM 投机解码工程 2026:从草稿模型到树注意力的统一架构
把投机解码当作接受率为核心的端到端加速工程,在树注意力 + 动态 K + 量化耦合三层之上重新推导加速比上界,给出 9 节工程实战与 Pareto 前沿实测。
约 31 分钟阅读9,072 字7 次阅读博主

把投机解码当作接受率为核心的端到端加速工程,在树注意力 + 动态 K + 量化耦合三层之上重新推导加速比上界,给出 9 节工程实战与 Pareto 前沿实测。

一句话摘要:投机解码不是"草稿模型 + 验证"两件事,而是一套以接受率为核心、以树注意力为执行形态、以 KV Cache 与量化深度耦合为边界的端到端加速工程;本文用一套形式化把接受率、推测长度、树结构、耦合代价全部统一到同一加速比公式下,并给出 9 节工程实战。
自回归解码阶段每生成 1 个 token,都要重新经过完整的 forward pass——Prefill 阶段那种"一次塞入 1024 个 token、把 QKV 全部算完、FFN 全展开"的高吞吐模式无法复用。H100 80GB 单卡在做 7B 模型的 decode 时,理论 FLOPS 是 989 TFLOPS,但实际 MFU(Model FLOPS Utilization)通常只有 8-15%。原因不是算力不够,而是访存带宽:每生成 1 个 token 必须把整个模型权重从 HBM 读一遍(70 亿参数 × 2 字节 = 14 GB),算下来访存带宽是 3 TB/s 量级,而 H100 的 HBM3 带宽只有 3.35 TB/s——已经撞墙了。
这种"memory-bound decode"问题不会随 GPU 代际升级而消失:Hopper → Blackwell 的 HBM 带宽只从 3.35 TB/s 升到 8 TB/s(≈2.4×),但同期模型参数规模从 70B 升到 400B+(≈5.7×),带宽缺口在变大。因此业界过去两年的核心赌注不是"等更快的显存",而是"用算法把访存摊薄"——投机解码(Speculative Decoding)就是这个赌注里最工程化的一支。
投机解码的核心直觉极朴素:既然生成 1 个 token 要把权重全读一遍,那不如让一个小模型先猜 K 个 token,再让大模型一次性验证。小模型生成 K 个 token 的代价远低于大模型生成 K 个 token(参数量小一个数量级),而大模型验证 K 个 token 的代价几乎等同于生成 1 个 token(一次 forward pass 的访存开销)——所以理论上只要平均接受率 α ≥ 1/(K+1) 就能加速。但这条朴素直觉在工程上其实非常棘手:接受率取决于什么?小模型和大模型的分布差距如何度量?K 选多大?为什么固定 K 不够?如何让一次 forward 验证多个候选序列?这些问题在 2024-2026 年的工程实践中形成了完整的体系,本文就按这九节展开。
接受率 α 是投机解码唯一的内生参数。我们用 Leviathan et al. (2023) 的形式化定义:给定目标分布 p 和草稿分布 q,对任意 token x,接受概率为 min(1, p(x)/q(x))。一次生成 K 个草稿 token,平均接受数为:
E[accepted] = Σ_{i=1}^{K} α^i
其中 α = E_x[min(p(x)/q(x), 1)] 是平均接受率(草稿 token 第一个接受概率、第二个受第一个被接受影响后的条件概率,等等,简化成一阶近似)。加速比 S 的封闭形式为:
S = (E[accepted] + 1) / (K · c_q + c_p)
其中 c_q 是草稿模型相对代价(小模型 / 大模型 FLOPS 比),c_p 是大模型一次 forward 的相对代价(≈1,因为草稿加验证的两步合在一起就是一次大模型 forward + 一次小模型 forward,但小模型代价远低于大模型)。当 α 足够高、K 适中时,S 可达 2-3×。
但这条公式隐藏三个工程陷阱:
陷阱 1:α 不是常数。 α 随生成位置变化:prompt 附近 α 高(受 prompt 约束强),开放生成区域 α 急剧下降(语义空间放大)。工程上必须做动态 K(adaptive K),否则在低 α 区域浪费算力。
陷阱 2:K 与 α 不是独立变量。 K 越大,单个 token 被接受的概率越低(链式条件概率衰减),平均接受数先升后降。最优 K 通常在 4-8 之间,而非工程直觉里"越大越好"。
陷阱 3:c_q 不是免费的。 草稿模型虽然小,但需要独立的 KV Cache(小模型有自己的 K/V 不能复用大模型的),显存占用是叠加的。如果用 EAGLE 这种基于特征的方法(草稿模型读大模型的隐层而非独立 forward),c_q 可以低到 0.05-0.1;如果用 Medusa(多个并联 FFN head),c_q 更低但接受率略差。
把这三个陷阱纳入后,工程上可写的目标函数变成:
S(K, α(x), c_q) = (E_α[accepted(x)] + 1) / (K · c_q + 1)
最大化 S 是 K 的凸函数(在 α 固定情况下),但 α 是位置 x 的函数,所以实际优化是动态规划:每个位置选当前最优 K。
工程上至少有四类草稿方案,按"独立性 vs 共享性"排序:
(1) 独立小模型(Vanilla Speculative):用一个 0.5B-1B 的独立小模型当草稿。Leviathan/Chen 原始论文就是这个范式。优点:草稿质量可控(独立训练)。缺点:显存两份(不能复用 KV Cache),c_q 通常 0.15-0.25。
(2) Medusa 式多 head:把大模型最后一层的 FFN 换成 K 个并联 head 直接出 logits,不跑独立小模型。优点:显存零额外开销(复用大模型隐层),c_q < 0.05。缺点:head 训练需要专门蒸馏流程,且 K 个 head 的接受率通常不如独立小模型(α 低 5-10%)。
(3) EAGLE 系列特征预测:训练一个小模型读取大模型的隐层 h_t 而非 token embedding,预测下一个隐层 h_{t+1},再用一个冻结的 classifier head 转 logits。优势:c_q 极低(0.05-0.08),接受率与独立小模型相当甚至略高(因为读到了大模型上下文)。劣势:训练流程稍复杂(小模型需要适配大模型的隐层维度)。
(4) Lookahead / n-gram 检索:不做小模型 forward,直接从 prompt 缓存里检索 n-gram Jacobi 迭代猜下一 token。优点:零参数、零额外显存。缺点:只对重复模式(代码、补全、模板填空)有效,对开放生成几乎无效。
选型决策表(实测,2026 Q3 数据):
关键观察:草稿模型选型不是单选,是路由策略。生产系统里通常同时部署 2-3 种草稿方案,按请求类型路由(用请求前 100 个 token 的意图分类 + 前缀哈希判断"是不是重复模式")。这就把投机解码从"单一优化"升级为"系统级调度问题"。
固定 K 是 2023 年的工程形态,现在基本被淘汰。原因:α 在生成过程中动态变化,固定 K 在低 α 段会浪费算力(生成的草稿大概率被拒),在高 α 段会欠利用(明明能接受更多)。
动态 K 调度有三条主流路线:
路线 A:基于历史接受率的 PID 控制。维护一个滑动窗口(如最近 64 个生成步)的接受率 α̂,如果 α̂ > 阈值 α_high 则升 K(+1,最多到 K_max),α̂ < α_low 则降 K(-1,最低到 K_min=1)。简单稳定,缺点是 PID 参数需要调,对 prompt 类型切换有 200-500ms 滞后。
路线 B:基于置信度的 lookahead。草稿模型在生成第 i 个 token 时输出置信度 c_i = max(softmax(q_i)),如果 c_i < threshold 则停止草稿(提前 commit)。这条路线不需要 K 参数,让草稿模型自己决定长度。代表是 EAGLE-2(2024)的 dynamic tree。
路线 C:基于 KL 散度的早期停止。计算草稿分布 q 与目标分布 p 的 KL 散度 D_KL(q||p),超过阈值就停。比路线 B 更稳(KL 是分布级信号,不受单 token 噪声影响),但每步多算一次 KL(≈0.02 ms / step,影响可忽略)。
工程经验(2026 Q3):
关键洞察:动态 K 不是优化 K 的方法,而是把投机解码的失败模式从"参数选错"转为"在线自适应"。对于长序列生成(> 2000 tokens),动态 K 比固定 K 平均加速比高 30-40%。
树注意力(Tree Attention / Tree Masked Attention)是投机解码 2024 年之后能大规模生产的核心工程突破。原始投机解码一次只生成一条线性候选序列,树注意力一次生成 B 条候选(分支因子),大模型一次 forward 同时验证所有 B 条候选。
数学上:传统自回归解码的 attention mask 是下三角(每个 token 只能看到自己及之前的 token);树注意力把 mask 改成一个森林结构——B 条候选序列共享公共前缀(已被接受的 token),从分叉点开始每条候选独立走自己的路径。mask 矩阵从 N×N 下三角变成 N×(B·N) 的稀疏块状下三角。
工程实现(以 vLLM 为例):
关键工程细节:
实测(vLLM 0.6.3 + LLaMA-3-70B + A100):
SGLang vs vLLM 树注意力实现差异(2026 Q3 对比):SGLang 用 RadixAttention 复用 prompt 缓存,KV Cache 的 block table 用前缀哈希而非物理地址,去重粒度更细(按 16-token block),树结构的公共前缀命中率比 vLLM 高 12-18%;vLLM 优势在调度——支持 per-request 动态树深度(不同请求独立配 B),SGLang 是全局固定 B。生产经验:高重复 prompt 工作负载(IDE 补全、RAG 检索)选 SGLang,多样化请求(对话、agent)选 vLLM。两者代码质量都已 production-grade,没有明显胜出者。
投机解码的加速比公式在忽略 KV Cache 与量化代价的简化模型下成立,但生产系统必须把这三个一起考虑。
耦合 1:投机解码 vs PagedAttention。PagedAttention 把 KV Cache 分块(block size 16),让显存碎片化降低、批处理容量提升。投机解码的树结构天然适配 paged——每个候选分支共享前缀 block(引用计数),分支独立部分独占 block。耦合系数接近 1.0,两者几乎正交增益。
耦合 2:投机解码 vs FP8 量化。FP8 把权重从 FP16 降到 FP8,访存带宽减半,理论上推理速度翻倍。投机解码与 FP8 的耦合是乘法**:总加速比 ≈ S(投机) × 1.8(FP8)。但实测只有 1.5-1.7× 而不是 1.8×,因为 FP8 的 dequant 开销在低 batch 时占比更高。投机解码通过提高 batch 内的 token 密度,让 FP8 的 MFU 提升,间接抵消了 dequant 开销。
耦合 3:投机解码 vs 连续批处理(continuous batching)。Continuous batching 把不同请求的 prefill/decode 交错,提升 GPU 利用率。投机解码对连续批处理是部分冲突的:草稿生成阶段是大模型的"空闲期"(草稿小模型在跑),但这部分时间原本可以服务其他请求的 prefill。工程上的解决方案是异步投机(async speculative decoding)——草稿生成在单独的 CUDA stream 上跑,与大模型主流的 prefill/decode 并行。SGLang 的 spec decode 实现就是这样。
统一加速比公式(加上量化与连续批处理):
S_total = S_spec × η_quant × η_batch
其中 η_quant 是量化效率(FP8 ≈ 1.5-1.8,FP4 ≈ 2.5-3.0 但需 LLaMA-3+ 量化感知训练支持),η_batch 是连续批处理效率(典型 1.3-1.6)。端到端加速比在 4-6× 区间(vs 不投机 + FP16 + 非连续批处理 baseline)。
警告:η_quant × η_batch 不是独立相乘。当 batch 满载时,FP8 的 dequant 开销被批处理均摊,η_quant 接近上限;当 batch 低时,dequant 开销凸显,η_quant 跌到 1.2-1.3。投机解码通过主动填充 batch 让大部分时间跑在高 batch 状态,间接稳定 η_quant。
把上面六节综合到生产部署,核心是 吞吐-延迟-Pareto 前沿:固定硬件(8×H100)和模型(70B)下,调整投机解码参数 + 量化精度 + 连续批处理策略,找到"P50 延迟最低"或"QPS 最高"或"成本 / token 最低"的 Pareto 最优解。
实测 Pareto 表(LLaMA-3-70B on 8×H100,2026 Q3 数据):
| 配置 | P50 首 token 延迟 | QPS | 成本 / 1M token |
|---|---|---|---|
| Baseline (FP16, 无投机) | 78 ms | 145 | $0.85 |
| FP8 only | 52 ms | 218 | $0.58 |
| FP8 + EAGLE-3 (K=5) | 31 ms | 365 | $0.34 |
| FP8 + EAGLE-3 + 树 B=8 | 24 ms | 470 | $0.27 |
| FP4 + EAGLE-3 + 树 B=8 | 18 ms | 625 | $0.20 |
关键观察:
关键观察(补充):同样硬件下,把草稿模型从独立 0.5B 换成 EAGLE-3 后,P50 延迟从 24 ms 降到 21 ms、QPS 从 470 涨到 540——草稿模型架构选择本身就是一个独立的优化维度,通常被低估。实测 EAGLE-3 比独立小模型 +5-8% 加速比,代价是训练复杂度高 1 个数量级。
对生产部署的 5 条推论:
不要单独评估投机解码。它必须和量化、连续批处理一起评估,单独优化会误导——比如 K=10 看似激进但在 FP4 下可能因为 KV Cache 压力反而变慢。
路由策略 > 单一参数。生产系统必须按请求类型路由到不同草稿方案(§3 末提的混合架构),单参数 K 的全局配置是次优的。
接受率 α 是核心 SLO。监控平均 α 的滑动窗口(5 分钟粒度),α 跌 10% 立即触发告警——α 异常下降通常意味着模型版本漂移、prompt 分布漂移或草稿模型与大模型的 tokenizer 不一致。
P99 比 P50 更看投机解码。投机解码降低 P50 显著(节省固定开销),但 P99 主要由 batch 排队 + 树结构的最坏路径决定,P99 改进只有 P50 的 50-60%。生产 SLO 不能只看 P50。
成本模型必须算上草稿小模型的推理成本。单看"大模型 token / 秒"会忽略草稿模型的 GPU 占用——通常是 5-15% 额外成本**,但换来 2-3× 加速,单位加速成本远低于"加 GPU 解 P99"。
投机解码不是银弹,它有一类系统性失败模式必须在生产前识别:
失败模式 1:分布外 prompt 导致 α 崩溃。如果 prompt 是模型没见过的格式(如罕见代码语言、新领域专业术语),草稿模型和大模型预测都偏离,α 可能从 0.7 跌到 0.2,加速比从 2.5× 跌到 1.1×(基本没加速,但还多了草稿开销)。对策:监控 α 分布,按 prompt embedding 聚类,对低 α 簇自动 fallback 到非投机模式。
失败模式 2:温度参数对 α 的影响。温度 T > 1 时,草稿分布 q 和目标分布 p 都被拉平,p/q 比值趋近 1,α 自然升高(看似好事);T < 1 时两者都变尖,p/q 极端化,α 下降。反直觉的结论:高温采样更适合投机解码(α 高),低温采样反而吃亏。生产里如果用户大量用 T=0.3-0.7 采样,投机解码的收益会打 30-40% 折扣。
失败模式 3:长上下文 α 衰减。前 500 个 token α 高(受 prompt 锚定),500-2000 token α 平稳下降,2000+ token 后 α 稳定到平台值。生产里如果一个请求生成 5000+ token,前 1000 token 加速比 3×,后 4000 token 加速比 1.8×。这是投机解码固有的特性,必须如实反映在用户预期里。
回归风险。投机解码最大的回归风险是输出分布的偏移:即使接受率机制保证数学上 E[x_accepted] = E[x_sampled],在长序列上累积的接受/拒绝决策可能让生成路径偏离纯采样,用户感知的"创造性"会下降。工程上必须做 A/B 框架:
A/B 实施:投机解码的 A/B 不能只看 QPS / 延迟,必须包含质量指标。建议 1% 流量开启作为对照组,主流量 99% 保持当前,2 周后对比。
把以上八节沉淀为可观测性清单,给一线工程师:
(1) 必备 metrics(Prometheus 格式):
spec_decode_acceptance_rate (label: model, prompt_type) — 滑动窗口 5 分钟spec_decode_avg_accepted_tokens (label: model) — 滑动窗口 5 分钟spec_decode_draft_k_distribution (histogram) — K 实际分布spec_decode_tree_branch_factor (label: model) — B 实际分布spec_decode_total_speedup_ratio (label: model, quantization) — 端到端加速比(2) 必备 traces(OTel GenAI 语义约定):
(3) 必备 alerts:
(4) 必备 dashboard:
(5) 必备 chaos 实验:
最后一条工程直觉:投机解码不是"加了就能快",它是一组需要持续调参、对 prompt 分布敏感、与下游优化强耦合的复杂系统。生产前必须做至少 2 周的金丝雀观察,不能直接 100% 流量上。一旦稳定上线,必须把它纳入到模型版本变更的回归测试——草稿模型和大模型任一方升级都可能让 α 漂移。这是 2026 年 LLM 推理工程的现实,不是教科书。
参考文献
Conversation
0 条