在一家需要同时接待几百桌客人的高级餐厅里,最贵的不是食材,而是主厨的时间。主厨手艺惊人,但每出一道菜都要亲自掌勺,一小时只能出三道菜。某天来了位手脚麻利的学徒——他的手艺虽不如主厨精细,出菜却出奇的快。主厨发现一个好办法:让学徒先一口气把他能想到的几道菜快速端上来,主厨只需扫一眼整排菜,认可的菜直接放行上桌,不认可的菜亲自改一笔。只要学徒的品味和主厨接近,主厨往往一次就能放行三四道菜,出菜速度立刻翻了几倍,而每一道端上桌的菜,依然还是主厨完全认可的味道。这就是投机解码(Speculative Decoding):让一个小模型快速写出候选 token 序列,再由大模型用一次前向传播并行验证,无损地加速大模型解码。

岗位关联

掌握投机解码,你能在不改动大模型权重的前提下,用 2~3 倍速度部署推理服务,在延迟与吞吐上获得立竿见影的收益,是 LLM 服务端优化从业者的必修课。

目录
  1. 可视化演示
  2. 动机与原理:主厨的瓶颈
  3. 草稿模型选型:如何挑选学徒
  4. 自回归快速生成:学徒连写草稿
  5. 并行验证机制:主厨一次审完全部候选
  6. 数学等价性证明:为什么无损
  7. 加速收益与工程:餐厅账本的秘密
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示零知识证明的三步交互协议(承诺-挑战-响应),通过多轮交互降低欺骗概率,验证者无需获取额外信息即可确信声明真实性。

本节配有交互动效(进入视口后自动加载,约 3KB)。

三步交互协议 承诺-挑战-响应三步 每轮欺骗概率减半 P=2^-n

动机与原理:主厨的瓶颈

让我们回到那家餐厅观察主厨的工作节奏。主厨(大模型)炖一锅高汤要三个小时,更让人头疼的是,每往汤里加一种香料(生成一个 token),主厨都必须重新品一遍整锅汤——因为他的配方规则是:每加一味料,都要回顾前面所有料的味道。这个规则叫自回归:第 t 个字依赖于它之前的所有 t-1 个字。在 Transformer 大模型里,每生成一个 token 都要做一次完整的前向传播(forward pass),把整段上下文重新“读”一遍。

虽然 KV 缓存优化了部分重复计算,但单步开销依然与模型参数量成正比。一个 70B 参数的模型,生成第 100 个 token 和第 1 个 token 的前向计算量几乎一样大。也就是说,生成 T 个 token 就要做 T 次大前向,“出一字、算一遍”是解码延迟的根本瓶颈。而更扎心的事实是:每一次前向都要把全部参数读一遍,但最终只用到了一个 token 的预测结果,大部分算力都花在了“搬家”而不是“思考”上。

投机解码的朴素洞察由此诞生:既然每一步都绕不开一次前向,而前向的核心产出是给词表里每个词打分,那能不能让一个便宜的小模型先把多步的候选都猜出来,再用大模型的一次前向同时给这些候选打分?这就把 T 次大前向压缩成 1 次大前向加若干次小前向,从根本上绕开了强制串行的路径。

主厨依然保留最终决定权——每一道端出去的菜都必须经过他的认可,只是他不再需要亲手切每一片葱姜蒜了。接下来的章节,我们来看看这位学徒(草稿模型)该怎么挑、怎么写草稿、主厨又该怎么审。

p(x1,x2,…,xT)=∏t=1Tp(xt∣x1,…,xt−1)\displaystyle p(x_1, x_2, \dots, x_T) = \prod_{t=1}^{T} p(x_t \mid x_1, \dots, x_{t-1})
这个公式把“整句概率”拆成 T 个条件概率的乘积,也直观展示了串行代价:每多一个 token,就要多一次条件概率计算(大模型前向)。
  1. 整句概率左边 p(x₁,…,x_T) 表示模型认为整句话出现的概率,是所有 token 联合起来的分布。
    p(x1,x2,…,xT)p(x_1, x_2, \dots, x_T)
  2. 连乘符号$\prod$ 表示从 t=1 到 T 把每一步乘起来,每一步都是“在已知前文的前提下,下一个字是谁”的条件概率。
    ∏t=1T\prod_{t=1}^{T}
  3. 条件概率$p(x_t \mid x_1, \dots, x_{t-1})$ 读作“给定前 t-1 个字,第 t 个字是 x_t 的概率”,这就是自回归生成的每一步。
    p(xt∣x1,…,xt−1)p(x_t \mid x_1, \dots, x_{t-1})
  4. 串行代价T 个 token 意味着 T 次条件概率计算,也就需要 T 次大模型前向传播——这就是要绕开的瓶颈。
    t=1,2,…,Tt = 1, 2, \dots, T

草稿模型选型:如何挑选学徒

选学徒有个铁律:不是越聪明越好,而是要“手快”且“口味贴近主厨”。如果学徒做一道菜比主厨还慢,请他来反而拖慢节奏;如果学徒的口味与主厨南辕北辙,主厨得把每一道菜都推翻重做,等于白请。草稿模型的选型标准对应两个量化指标:一是单步延迟 $t_{\text{small}}$ 必须远小于大模型的 $t_{\text{large}}$;二是草稿分布 q 与目标分布 p 的“口味贴合度”要高,即平均接受率 α 要尽量大。

工程上,草稿模型常见方案有三:直接选用一个小参数的开源模型(如 1B 级别对 70B 级别);用目标模型蒸馏出的迷你版本;或用 n-gram 等轻量统计模型。它们都不需要改动目标模型——大模型保持完全黑盒,只是在它前面增加一个“预检”环节。草稿模型的训练往往是离线完成的,语料与目标模型越接近,接受率就越高。

选好了学徒,接下来看看学徒如何飞快地写出一长串推荐菜单。

α=Ex∼q[min⁡(1,p(x)q(x))]\displaystyle \alpha = \mathbb{E}_{x \sim q}\left[\min\left(1, \frac{p(x)}{q(x)}\right)\right]
α 是平均接受率的数学定义:它衡量草稿分布 q 与目标分布 p 的平均贴合度,α 越高,一次验证能放行的 token 越多。
  1. 平均接受率 α一个介于 0 到 1 之间的数,代表“主厨平均放行学徒几道菜”的比例;α=0.8 意味着每 5 个候选期望放行 4 个。
    α\alpha
  2. 期望符号$\mathbb{E}_{x \sim q}$ 表示“让 x 按草稿分布 q 随机出现”,对每一种可能出现的情况求平均。
    Ex∼q\mathbb{E}_{x \sim q}
  3. 比值 p/qp(x)/q(x) 反映大模型与草稿模型对同一个 token 的分歧——大模型更看好时比值大于 1,没那么看好时小于 1。
    p(x)q(x)\frac{p(x)}{q(x)}
  4. min 截断接受概率取 min(1, ·),保证概率不超过 1;大模型比草稿更看好时也只能接受(信任),不会“超额接受”。
    min⁡(1,p(x)q(x))\min\left(1, \frac{p(x)}{q(x)}\right)

自回归快速生成:学徒连写草稿

学徒写菜单的方式和主厨一样,也是一道菜一道菜地想:“昨天主厨做过烤鸭,那今天他多半会配荷叶饼……”只不过他每想一道菜只需要主厨十分之一的时间。因为他的知识结构(参数量)小得多,每一步的“想”(前向传播)都便宜得多。草稿模型生成 γ 个候选 token 时,会以自己刚生成的前缀为条件逐字采样,这个过程完全复用 KV 缓存——每多生成一个候选,只需要做一次轻量的增量计算,而不需要把前面所有内容重算一遍。

这个阶段的目标不是“正确”,而是“快而合理”:学徒可以大胆地按自己的风格往下编,编错了也没关系,反正后面有主厨把关。γ 通常取 4~5:太小则一次验证的收益有限,太大则一旦草稿写偏,后面大量候选全部作废,浪费写作时间。

学徒洋洋洒洒写下一串菜单:“烤鸭、荷叶饼、甜面酱……”,主厨要开始审了。

q(x^1,…,x^γ)=∏i=1γq(x^i∣x^1,…,x^i−1)\displaystyle q(\hat{x}_1, \dots, \hat{x}_\gamma) = \prod_{i=1}^{\gamma} q(\hat{x}_i \mid \hat{x}_1, \dots, \hat{x}_{i-1})
草稿模型生成候选序列的概率,等于它自己每一步条件概率的连乘——它完全按照自己的“小脑瓜”串行往下编。
  1. 候选记号带帽子的 $\hat{x}_i$ 表示“候选 token”,即学徒写出来的草稿,不是最终答案。
    x^i\hat{x}_i
  2. 草稿长度 γgamma 是一轮生成多少个候选的超参数,工程上常取 4~6。
    γ\gamma
  3. 草稿条件概率$q(\hat{x}_i \mid \hat{x}_{<i})$ 表示草稿模型在看过自己刚写的前 i-1 个候选后,下一个候选是谁的概率。
    q(x^i∣x^1,…,x^i−1)q(\hat{x}_i \mid \hat{x}_1, \dots, \hat{x}_{i-1})
  4. KV 缓存加速因为每个新候选只依赖前文增量,复用历史 key-value,每次生成只需一次轻量增量计算,代价远小于大模型单步。
    cost≪tlarge\text{cost} \ll t_{\text{large}}

并行验证机制:主厨一次审完全部候选

主厨审菜单的方式令人惊喜:他不需要一道菜一道菜地尝——他直接把整份菜单拿在手里,用一秒钟把五道菜的搭配在脑子里完整体验一遍。Transformer 的注意力机制恰好支持这种“一次前向看完整行”:输入是一整串候选 token,输出是每个位置上对词表的打分。这就是并行验证的核心:目标模型把 γ 个候选作为同一个序列喂进去,一次前向传播就能得到 γ 个位置的 logits,再配合 softmax 得到每个位置的分布 p。

得到分布后开始逐位置验证。对第 i 个候选 $\hat{x}_i$,我们同时知道草稿模型给它的概率 $q(\hat{x}_i)$ 和目标模型给它的概率 $p(\hat{x}_i)$:如果大模型比草稿模型更看好这个 token(p ≥ q),就完全信任地接受;如果大模型没那么看好(p < q),则以 p/q 的概率接受。一旦某个候选被拒绝,后面所有候选全部作废,被拒绝的位置从修正分布重新采样,下一轮从该位置重新开始。

主厨的审阅几乎瞬间完成:三道菜盖“过”章,第四道菜画了个“叉”并亲自改写,然后把菜单还给学徒,让他从改写处继续往下编。整个过程,主厨只“出手”一次。

pacc(x^i)=min⁡(1,p(x^i∣x^<i)q(x^i∣x^<i))\displaystyle p_{\text{acc}}(\hat{x}_i) = \min\left(1, \frac{p(\hat{x}_i \mid \hat{x}_{<i})}{q(\hat{x}_i \mid \hat{x}_{<i})}\right)
这是投机解码最核心的“盖章规则”:大模型越认可草稿,接受概率越高;一旦大模型没那么认可,就按比例降低放行概率,把决定权交还给大模型。
  1. 接受概率 p_acc第 i 个候选最终被主厨盖章放行的概率,是一个 0 到 1 之间的数。
    pacc(x^i)p_{\text{acc}}(\hat{x}_i)
  2. 大模型概率$p(\hat{x}_i \mid \hat{x}_{<i})$ 是大模型“内心”对第 i 个位置真实分布给出的打分。
    p(x^i∣x^<i)p(\hat{x}_i \mid \hat{x}_{<i})
  3. 草稿模型概率$q(\hat{x}_i \mid \hat{x}_{<i})$ 是草稿模型生成该候选时自己的打分,两个概率的比值就是“分歧度”。
    p(x^i∣x^<i)q(x^i∣x^<i)\frac{p(\hat{x}_i \mid \hat{x}_{<i})}{q(\hat{x}_i \mid \hat{x}_{<i})}
  4. min 截断与随机比较实际执行时均匀采样 $u \sim U[0,1]$,若 $u \le p_{\text{acc}}$ 则接受;否则拒绝并进入修正分支。
    u≤min⁡(1,pq)u \le \min\left(1, \frac{p}{q}\right)

数学等价性证明:为什么无损

细心的人会问:学徒写的菜和主厨改过的菜混在一起,最后的菜单味道还能是“纯正主厨风格”吗?答案是:能。这背后是统计学里著名的拒绝采样(rejection sampling)的妙用。对每个被验证的位置,输出只有两种可能:要么接受草稿模型的 x(概率为 $q(x) \cdot \min(1, p(x)/q(x))$);要么进入拒绝分支,从修正分布 r 里重新采样。把两条路加起来,恰好得到 p(x)——也就是大模型自己采样的分布,分毫不差。

直观理解:当 p(x) > q(x) 时,说明大模型比草稿模型更喜欢 x,草稿“漏掉”的概率质量全部由拒绝分支里的 r(x) 补回来;当 p(x) < q(x) 时,草稿“多给”的概率质量又恰好被 min(1, p/q) 的接受率削掉。一加一减,误差被精确抵消。这就是“无损”二字的数学保证——修正拒绝采样让整体输出分布严格等于目标模型分布。

主厨盖章的每一道菜,和主厨亲手做的每一道菜,在“风味分布”上完全等价。加速归加速,品质不打折——这比任何近似加速方法都更让人安心。

Pout(x)=q(x)min⁡ ⁣(1,p(x)q(x))+(1−α) r(x)=p(x)\displaystyle P_{\text{out}}(x) = q(x)\min\!\left(1,\frac{p(x)}{q(x)}\right) + (1-\alpha)\, r(x) = p(x)
输出分布 = 接受分支贡献 + 拒绝分支贡献 = 目标分布 p。两条支路的偏差恰好互补,所以投机解码在统计上与直接用大模型采样完全等价。
  1. 第一项(接受直接输出)若大模型对 x 的认可度不低于草稿(p≥q),接受概率为 1;否则为 p/q。这一项贡献了草稿分布乘以接受概率。
    q(x)min⁡ ⁣(1,p(x)q(x))q(x)\min\!\left(1,\frac{p(x)}{q(x)}\right)
  2. 拒绝总权重 1-αα 是所有候选的平均接受率(前文公式),1-α 就是进入修正分支的总概率,它是第二项的权重。
    α=∑yq(y)min⁡ ⁣(1,p(y)q(y))\alpha = \sum_{y} q(y)\min\!\left(1,\frac{p(y)}{q(y)}\right)
  3. 修正分布 r(x)r(x) 正比于 max(0, p(x)-q(x)),专门把草稿“少给”的概率质量补回来,补多少由真实差距决定。
    r(x)=max⁡(0,p(x)−q(x))1−αr(x) = \frac{\max(0, p(x)-q(x))}{1-\alpha}
  4. 结果恒等于 p(x)把上述两项代入并化简:当 p≤q 时第一项得 p(x)、第二项为 0;当 p>q 时第一项得 q(x)、第二项恰好补上 p(x)-q(x)。两种情况结果都是 p(x)。
    Pout(x)=p(x)P_{\text{out}}(x) = p(x)

加速收益与工程:餐厅账本的秘密

请了学徒后,餐厅到底快了多少?这取决于两个数:学徒每轮能写出 γ 道菜(草稿长度),以及主厨平均能放行几道(期望接受长度 E[N])。如果主厨平均放行 2.5 道,主厨此前每出一道菜要 1 分钟、学徒写一道菜只要 0.1 分钟、主厨审一次只要 1 分钟,那么一轮的耗时是 $0.1 \times \gamma + 1$ 分钟,产出却是 2.5 道菜——等效每道菜耗时从 1 分钟大幅下降。当草稿模型足够小、接受率足够高时,整体解码速度就能稳定提升 2~3 倍。

E[N] 的公式体现两个规律:α 越大,期望接受长度越长;γ 无限增大时,E[N] 趋近上界 $\alpha/(1-\alpha)$。但 γ 不能无限大——草稿写太多,一旦中途被拒,前面的写作时间就白花了。工程实践要点:一是把验证前向纳入系统调度,利用 GPU 的批量能力把多个请求的验证合并执行;二是监控每轮接受率,动态调整 γ 或切换草稿模型;三是与 vLLM、TGI 等服务框架的 paged attention、continuous batching 配合,进一步放大吞吐收益。

餐厅账本上,出菜速度提升了两三倍,而顾客吃到的依然是主厨亲手把关的风味。投机解码不改变模型的任何参数,只改变“谁先写、谁再审”的协作方式——这是工程上极其优雅的一类加速。

E[N]=∑k=1γαk,Speedup=E[N]×tlargeγ×tsmall+tlarge\displaystyle \mathbb{E}[N] = \sum_{k=1}^{\gamma}\alpha^{k}, \qquad \text{Speedup} = \frac{\mathbb{E}[N] \times t_{\text{large}}}{\gamma \times t_{\text{small}} + t_{\text{large}}}
期望接受长度是每一轮平均“白赚”的 token 数;加速比则是“串行生成同样多 token 的耗时”除以“投机解码一轮的耗时”。
  1. α 的 k 次方假设各位置接受相互独立,前 k 个候选都被接受的概率是 $\alpha^k$。
    αk\alpha^{k}
  2. 期望接受长度 E[N]把“至少接受 1 个”到“至少接受 γ 个”的概率全部累加,得到平均每轮放行的 token 数。
    E[N]=∑k=1γαk\mathbb{E}[N] = \sum_{k=1}^{\gamma}\alpha^{k}
  3. 分母一轮总耗时:草稿模型逐个生成 γ 个候选($\gamma \times t_{\text{small}}$),加上大模型一次验证前向($t_{\text{large}}$)。
    γ×tsmall+tlarge\gamma \times t_{\text{small}} + t_{\text{large}}
  4. 分子串行对照:如果不加速,串行生成 E[N] 个 token 需要 $\mathbb{E}[N] \times t_{\text{large}}$。
    E[N]×tlarge\mathbb{E}[N] \times t_{\text{large}}
  5. 加速比分子除以分母。当 $t_{\text{small}} \ll t_{\text{large}}$ 且 α 较高时,加速比可达 2~3 倍;这就是“2~3 倍无损加速”的出处。
    Speedup≈2∼3\text{Speedup} \approx 2 \sim 3

AI 应用场景

大模型在线推理服务加速

阶段:推理部署与性能优化

解决的问题:在传统自回归解码中,每次只生成一个 token 都要跑一次大模型前向,GPU 算力大量浪费在参数搬运上,单用户延迟高、吞吐上不去。投机解码让便宜的草稿模型先“探路”,大模型一次验证放行多个 token,瓶颈瞬间松开。

具体案例:某聊天机器人服务部署 70B 模型,接入投机解码(1B 草稿模型 + γ=5)后,平均每轮接受约 2.8 个 token,单请求首 token 后的平均吞吐提升约 2.2 倍,同时保持完全相同的生成质量分布。vLLM、TGI 等框架已原生支持该能力。

端云协同与边缘设备部署

阶段:模型压缩与端侧推理

解决的问题:手机、边缘盒子等设备的显存和算力有限,跑不动大模型;全部请求上云又面临网络延迟。端侧放一个轻量草稿模型,云端跑大模型验证,既享受大模型的质量,又大幅减少端云往返次数。

具体案例:某移动端输入法接入端侧 100M 参数草稿模型与云端 13B 目标模型,端侧连续生成 5 个候选后一次性上传验证,网络请求频率降低约 4 倍,候选词上屏延迟显著下降。

实时翻译与代码补全等低延迟交互

阶段:应用层延迟优化

解决的问题:同声传译、代码自动补全对单次解码延迟极其敏感,用户等待超过几百毫秒就会感到卡顿。投机解码把“逐字出结果”变成“一批批出结果”,在保持文本质量的前提下显著缩短感知延迟。

具体案例:某 IDE 插件对代码补全模型启用投机解码后,单次补全的平均生成时延从 480ms 降至 210ms,用户连续输入的跟手度大幅提升,而补全结果与原模型逐句对比无统计差异。

Python 演示

python70 行
import numpy as np VOCAB = ['a', 'b', 'c', 'd', 'e']N = len(VOCAB)rng = np.random.default_rng(42) # 目标大模型:一阶马尔可夫转移矩阵 P("主厨的风味")P = rng.dirichlet(np.ones(N), size=N) # 草稿小模型:与 P 相近但有偏差("学徒的口味")Q = 0.6 * P + 0.4 * np.ones((N, N)) / NQ = Q / Q.sum(axis=1, keepdims=True)  def sample(dist):    return int(rng.choice(N, p=dist))  def speculative_decode(steps, gamma):    """草稿模型生成 gamma 个候选,目标模型一次前向并行验证"""    tokens = [int(rng.choice(N))]    while len(tokens) < steps:        prefix = tokens[-1]        # 阶段1:草稿模型自回归生成候选        cand, ctx = [], prefix        for _ in range(gamma):            x = sample(Q[ctx])            cand.append(x)            ctx = x        # 阶段2:目标模型一次前向得到所有位置分布        p_list = [P[prefix]] + [P[cand[i]] for i in range(gamma - 1)]        # 逐位置验证        idx = 0        while idx < gamma and len(tokens) < steps:            x = cand[idx]            prev = cand[idx - 1] if idx > 0 else prefix            p_x = p_list[idx][x]            q_x = Q[prev][x]            if rng.random() < min(1.0, p_x / q_x):                tokens.append(x)                idx += 1            else:                residual = np.maximum(0.0, p_list[idx] - Q[prev])                residual = residual / residual.sum()                tokens.append(sample(residual))                break    return tokens  def trans_freq(seq):    """统计序列中相邻 token 的转移频率"""    T = np.zeros((N, N))    for a, b in zip(seq, seq[1:]):        T[a, b] += 1    T = T / T.sum(axis=1, keepdims=True)    return T  if __name__ == '__main__':    steps, gamma = 20000, 5    seq = speculative_decode(steps, gamma)    baseline = [int(rng.choice(N))]    for _ in range(steps - 1):        baseline.append(sample(P[baseline[-1]]))     err_sd = np.abs(trans_freq(seq) - P).mean()    err_bl = np.abs(trans_freq(baseline) - P).mean()    print('投机解码 转移偏差:', round(err_sd, 5))    print('直接采样 转移偏差:', round(err_bl, 5))    print('两者同分布(无损):', err_sd <= err_bl * 3)
目的:用一个小型马尔可夫世界完整复现投机解码的“草稿生成 + 一次前向验证 + 修正采样”,并统计验证输出分布与目标模型直接采样一致,即无损加速。
  1. P = rng.dirichlet(np.ones(N), size=N)用 Dirichlet 分布随机生成目标大模型的转移矩阵,模拟“主厨风味”。
  2. Q = 0.6 * P + 0.4 * np.ones((N, N)) / N构造草稿小模型:与目标分布相近但有明显偏差,模拟“学徒口味”。
  3. x = sample(Q[ctx])草稿模型以自己刚写的前缀为条件,逐个采样生成候选 token。
  4. p_list = [P[prefix]] + [P[cand[i]] for i in range(gamma - 1)]模拟目标模型一次前向传播同时得到所有位置的分布,这是并行的关键。
  5. if rng.random() < min(1.0, p_x / q_x):核心接受判断:大模型更看好就接受,否则按 p/q 比例接受。
  6. residual = np.maximum(0.0, p_list[idx] - Q[prev])拒绝后从 max(0, p-q) 归一化分布重采样,把草稿漏掉的概率补回来。
  7. err_sd <= err_bl * 3比较投机解码与直接采样的转移矩阵误差,验证两者同分布、无质量损失。

常见误区

误区我是不是得把草稿模型训练成和目标模型几乎一模一样,否则被拒绝的 token 太多,加速就没意义了?或者至少架构上得保持一致才能“配合”?

正解不需要。草稿模型只要平均接受率 α 足够高(工程上 0.6~0.8 已很理想)就能稳定获得 2 倍左右加速。它与目标模型可以有明显的分布偏差,偏差部分由拒绝分支的修正采样自动校正,不会影响最终质量,只会略微影响加速倍数。架构更是完全随意,大模型只把它当“一串输入”来验证。

为何常见:人们直觉认为“越像越好”,且担心偏差会引入质量损失,忽略了拒绝采样天然兜底——偏差的代价只是收益变小,而不是品质变差。

误区验证的时候不还是一个 token 一个 token 地检查吗?只不过把检查结果放在一起看,这和串行生成有什么本质区别?

正解判断本身的开销微乎其微——只做一次概率比较和均匀采样,可以忽略不计。真正的开销在于“模型前向传播”,而投机解码把所有位置的 logits 用一次前向就全部算出来了。把 γ 次大模型前向压缩成 1 次,这正是 2~3 倍加速的来源。“逐个判断”是廉价算术,“逐个前向”才是昂贵计算,两者不能混为一谈。

为何常见:把“逐个判断”误解为“逐个前向”,混淆了验证层的轻量操作与模型层的重量计算两个层级。

误区用草稿模型猜再加验证,生成结果应该跟直接用大模型不一样吧?这属于“用质量换速度”的近似手段。

正解数学上可以严格证明输出分布与目标模型完全一致(修正拒绝采样,见“数学等价性证明”一节)。注意“分布一致”不等于“逐句相同”——每次采样本来就有随机性,但整体统计特性、质量评分分布与直接用大模型采样不可区分。它是真正意义上的无损加速。

为何常见:直觉上“预猜+修错”像是近似手段,只有推导过拒绝采样的等价性才能打消这个疑虑;很多人没有意识到两个分布可以在不逐句相同的情况下统计等价。

误区既然接受率越高加速越多,那我直接把草稿模型换成更大的模型,γ 也设到 20,加速肯定更猛。

正解加速比是 $\mathbb{E}[N] \times t_{\text{large}} / (\gamma \times t_{\text{small}} + t_{\text{large}})$。草稿模型变大 → $t_{\text{small}}$ 增大 → 分母变大;γ 增大 → 超过期望接受长度后收益递减,且一旦中途被拒,前面草稿时间全部浪费。正确的做法是按实际延迟数据实测,找到让整体加速比最大的配置,而不是盲目增大任何单一变量。

为何常见:只盯着接受率 α 这一个变量,忽略了加速比是多个变量的比值关系,容易出现“边际收益递减”的认知盲区。

面试题

Q1. 投机解码中,候选 token 序列由哪个模型生成?

单选题 · easy

  • A.目标大模型自回归逐个生成
  • B.草稿小模型自回归逐个生成← 正确答案
  • C.两个模型共同并行生成
  • D.从词表均匀随机采样获得

答案:B。草稿模型是“学徒”,负责低成本的逐字生成;大模型只负责验证。若由大模型生成候选就回到串行解码,失去了加速意义;均匀随机采样与目标分布无关,接受率过低,不切实际。

Q2. 目标大模型在验证阶段的工作方式是?

单选题 · mid

  • A.对每个候选 token 分别做一次前向
  • B.一次前向并行计算全部候选位置← 正确答案
  • C.直接采信草稿模型的全部概率
  • D.用小模型回传的 logits 加权平均

答案:B。Transformer 的注意力机制可一次前向同时得到序列所有位置的 logits,这是并行验证的物理基础。逐个前向等于没加速;直接采信或加权平均都不符合“大模型把关”的设计。

Q3. 已知草稿概率 q(x)、目标概率 p(x),单 token 接受概率是?

单选题 · mid

  • A.无条件接受,概率恒为 1
  • B.min(1, p(x)/q(x)) 决定接受与否← 正确答案
  • C.max(0, p(x)-q(x)) 决定接受与否
  • D.当 q(x) 大于 p(x) 时一定接受

答案:B。p≥q 时大模型完全信任草稿,接受概率为 1;p<q 时大模型“没那么看好”,以 p/q 的概率接受。max(0,p-q) 是拒绝后的修正分布,不是接受概率;“q 大就接受”方向反了。

Q4. 首个被拒绝的 token 应如何修正?

单选题 · mid

  • A.从草稿分布 q 重新采样一次
  • B.从 max(0, p-q) 归一化分布采样← 正确答案
  • C.跳过该位置,继续验证后续候选
  • D.从均匀分布随机挑一个 token

答案:B。修正分布正比于 max(0, p-q),专门把草稿“少给”的概率质量补回来,才能保证输出分布无偏。从 q 重采样会保留草稿偏差;跳过或均匀采样都会改变最终分布,破坏无损保证。

Q5. 草稿模型选型时需要考虑哪些因素?

多选题 · mid

  • A.参数量小,单步延迟显著低于大模型← 正确答案
  • B.输出分布与大模型分布尽量接近← 正确答案
  • C.架构必须与大模型完全一致
  • D.可离线训练蒸馏或直接用现成小模型← 正确答案

答案:ABD。草稿模型的两个硬指标是“单步快”(t_small 小)和“口味贴近”(接受率高),方案可以离线蒸馏或直接用现成小模型。C 错误——草稿模型不参与最终质量决策,架构可以完全不同,大模型始终是黑盒。

Q6. “投机解码无损”指的是什么?

单选题 · easy

  • A.输出分布与目标大模型原生采样一致← 正确答案
  • B.输出结果与草稿模型采样结果一致
  • C.每一轮所有候选 token 都被接受
  • D.生成的 token 数量与串行完全相同

答案:A。修正拒绝采样保证 P_out(x)=p(x),即统计上等价于大模型自己采样。注意不是逐句相同——采样本身有随机性,但整体分布和质量统计不可区分;B、C、D 都不是“无损”的定义。

Q7. 关于期望接受长度 E[N] 的说法,正确的是?

多选题 · hard

  • A.平均接受率 α 越高,E[N] 越大← 正确答案
  • B.草稿与目标分布越接近,E[N] 越大← 正确答案
  • C.γ 无限增大时 E[N] 趋近 α/(1-α)← 正确答案
  • D.E[N] 完全由候选数 γ 单独决定

答案:ABC。E[N]=Σα^k,α 越大则 E[N] 越大;α 由草稿与目标分布的贴合度决定,所以 A、B 正确。γ 增大时 E[N] 收敛到几何级数上界 α/(1-α),C 正确。D 错误——E[N] 同时由 α 和 γ 决定,γ 只提供上限。

Q8. 一轮投机解码总耗时估算最接近?

单选题 · hard

  • A.期望接受长度乘大模型时延再加草稿时延
  • B.草稿生成 γ 个候选时延加上大模型一次验证时延← 正确答案
  • C.大模型时延加上草稿模型全部参数传输时延
  • D.候选数 γ 乘以大模型单步时延再除以接受率

答案:B。一轮耗时 = γ×t_small + t_large:草稿逐个生成 γ 个候选(每个都很便宜),大模型只做一次验证前向。A 把分子(串行对照)当成总耗时;D 又回到“每步都跑大模型”的串行思维。

Q9. KV 缓存在投机解码中主要服务于?

单选题 · mid

  • A.草稿模型增量生成候选时复用历史键值← 正确答案
  • B.缓存目标模型验证时的所有 logits
  • C.缓存每轮候选 token 的接受标记
  • D.替代目标模型的注意力矩阵计算

答案:A。草稿模型逐字生成候选时,每多写一个 token 只需增量计算,KV 缓存让这一步开销进一步降低。验证阶段的 logits 来自本次前向,不需要缓存历史;接受标记只是布尔值,与 KV 无关。

Q10. 关于投机解码的工程实践,正确的说法是?

多选题 · hard

  • A.验证前向的额外开销也应计入加速收益评估← 正确答案
  • B.多个请求的验证可合并成批次执行以提升 GPU 利用← 正确答案
  • C.接受率过低时就应无限加大草稿模型参数量
  • D.目标大模型的权重需要根据草稿模型反向微调

答案:AB。A 正确,加速比公式的分母就包含 t_large;B 正确,验证前向可以像普通推理一样做连续批处理(continuous batching)。C 错误——草稿模型变大会同步增大 t_small,需要权衡整体加速比;D 错误——大模型保持黑盒,权重完全不动。