跳转至

Speculative Decoding

投机解码由小草稿模型先提出多个 token,再由目标模型并行验证,以减少目标模型的串行步数。它把「逐个 token 串行生成」改造成「草稿批量提议 + 目标模型一次验证」,在单步内确认多个 token。

投机解码出自 Fast Inference from Transformers via Speculative Decoding(Leviathan、Kalman、Matias,ICML 2023)。它的由来是自回归生成的内存带宽瓶颈:decode 每步只产生一个 token,单步计算量小,GPU 大部分时间花在读取权重与 KV 而非计算上,因此吞吐受带宽限制而难以靠加大 batch 提升。既然单步前向本来就能处理一个 batch,投机解码就让一个更小、更快的草稿模型 (draft model) 先批量猜出后续若干个 token,再由目标模型对这批候选做一次并行前向验证;验证通过多少就前进多少,从而用一次目标模型前向「兑现」多个 token。后续发展出无需独立草稿模型的变体,如基于目标模型隐藏状态接草稿头的 Medusa、用 n-gram 匹配的投机方法等,但「草稿提议 + 目标验证 + 拒绝采样保证同分布」的框架保持一致。

快速开始

先测草稿接受率、端到端延迟和额外显存;接受率低时不要盲目启用。

  1. 选草稿模型:选择与目标模型分布接近、成本更低的草稿模型,或使用基于目标模型隐藏状态的派生草稿头。
  2. 配置与启用:在支持投机解码的运行时启用,设置草稿长度等参数,例如 vLLM 的 --speculative-model 与 --num-speculative-tokens。
  3. 基准测试:分别记录接受率、端到端延迟与新增显存占用,与不启用时的基线对比。

验证成功的关键是接受率高到足以抵消草稿与验证的额外计算,净延迟下降。若接受率低,先调整草稿模型或草稿长度,而非强行启用。

机制

目标模型保证采样分布正确地接受或修正草稿 token。验证阶段,目标模型一次前向得到草稿序列每个位置的概率,从前往后按概率接受草稿 token,一旦某处不匹配,就在该位置用目标模型分布重新采样并丢弃后续草稿,从而保证最终输出与目标模型直接采样同分布(拒绝采样)。

拒绝采样保证了「正确性」不因投机而降低。设目标模型在当前位置的真实分布为 \(p\),草稿模型提出 token \(x\) 时依据的分布为 \(q\),则接受 \(x\) 的概率为:

\[ p_{\text{accept}}(x)=\min\left(1,\ \frac{p(x)}{q(x)}\right) \]

若草稿 token 被拒绝,则从修正分布重新采样:

\[ p_{\text{resample}}(x)=\operatorname{norm}\Big(\max\big(0,\ p(x)-q(x)\big)\Big) \]

这一「接受或修正」两步在数学上等价于直接从 \(p\) 采样,因此投机解码的输出分布与目标模型直接自回归采样完全一致,不损失质量,这也是它区别于「草稿直接输出」等有损加速方法的关键。

加速依赖草稿模型与目标模型的预测一致性。草稿模型单步成本低但质量差,只有它猜中的 token 被目标模型接受时才能省下目标模型的串行步数;猜错的越多,验证被截断得越早,收益越低。在简化假设下,若每个位置以概率 \(\alpha\) 被接受、草稿长度为 \(\gamma\),则一次目标模型前向平均推进的 token 数约为:

\[ \mathbb{E}[\text{accepted tokens}]=\frac{1-\alpha^{\gamma+1}}{1-\alpha} \]

当 \(\alpha\to 1\) 时该值趋于 \(\gamma+1\),即一次前向最多推进 \(\gamma+1\) 个 token;\(\alpha\) 越低,收益越接近 1,甚至因额外开销而净亏损。

代价是额外的草稿计算与显存。草稿模型本身要占用显存与算力,验证还要并行处理多个候选位置,若接受率不足,总成本反而高于直接生成。

案例

代码补全任务草稿接受率高而加速明显;开放式创作接受率低,草稿计算反而增加成本。

以代码补全为例,代码的语法与命名规律性强,草稿模型容易猜中后续 token,接受率高,目标模型的串行步数被大幅压缩,端到端延迟明显下降。

开放式创作则不同,后续 token 分支多、可预测性低,草稿频繁猜错,验证被反复截断,草稿与验证的额外计算成为纯开销,反而比直接生成更慢。因此应按任务类型实测接受率与净收益。

常见失败点是接受率虚高(例如只看草稿与最终文本的表面重叠)或显存超限,排查时以「目标模型实际接受的 token 数 / 提议总数」为准,并监控新增显存。

相关主题