投机解码。
让生成变快的其他每一种办法都会改变模型说出的内容——量化它、换一个更小的、削掉上下文——而每一种都要求你在上线前重跑评测。投机解码是唯一的例外:一个廉价的草稿模型往前猜若干个令牌,真正的模型用一次前向把这份猜测核验掉,活下来的令牌可被证明就是真模型本来也会吐出的那些。不过它并不免费。它是拿闲置算力去换延迟,这意味着它在空闲的 GPU 上回报丰厚,却会在繁忙的 GPU 上悄悄吃掉你的吞吐。
起草、核验、保留前缀。
生成之所以慢,原因很无趣。在解码阶段,产出一个令牌需要把模型的每一个权重从显存里读一遍,而现代 GPU 读内存的速度远慢于它做乘法的速度。把一次 700 亿参数的完整扫描只换来一个令牌,是对硬件极其糟糕的使用:算术单元干等着带宽。
投机解码正是要榨出这份闲置算力。每一轮分两半:
- 起草。某个廉价的东西提出接下来的 k 个令牌——通常是 3 到 8 个。这个"廉价的东西"可以是同族的一个小模型,可以是嫁接在大模型自身隐藏状态上的轻量草稿头(EAGLE、Medusa),最省的情况下,也可以只是一次 n-gram 查表,直接抄一段提示词里已经出现过的短语。
- 核验。完整模型对整段提议只跑一次,并行地给全部 k 个位置打分。因为真正的开销是那一趟权重扫描,所以给八个位置打分几乎不比给一个位置打分更贵。
接着是接受规则:从左往右走过草稿,只要某个令牌与完整模型本会采样出的结果一致就保留,在第一处分歧断开——并用完整模型自己的选择替换掉那一个令牌。一轮里六个草稿令牌接受了五个,就等于用大约一次前向的价格产出了六个令牌。
无损性是接受规则的一条性质,而不是关于质量的一句宣传。这条规则是一次拒绝采样,它被构造成让输出分布与直接从完整模型采样完全相同——同样的温度、同样的 top-p、样样相同。任何单次运行仍会像普通采样一样次次不同,所以这不是通往确定性的路。它保证的是:打开投机不可能移动你的质量指标——这和"我们测了一下,看着还行"是非常不同、也有用得多的承诺。
接受率就是全部的方程。
这件事有没有用,由两个数字决定:草稿多久对一次,以及这份草稿花了多少钱。其余都是细节。
- 接受率是草稿令牌中通过核验的比例。它不是这项技术的属性——它是草稿/目标这一对模型在你的流量上的属性。匹配良好的现代草稿头在代码与指令跟随上大约报出 0.8;一对不匹配的模型碰上非常规文本,可能腰斩。
- 起草成本是你为一份可能被丢掉的提议所花的钱。一个体量为十分之一的独立小模型,每个草稿令牌就实打实地花掉十分之一次前向;而针对大模型自身隐藏状态训练出的草稿头便宜得多——这正是那种设计后来占了上风的原因。
两者的结合有一处显然、一处不显然。显然的是,抬高 k 就抬高了每轮令牌数的天花板。不显然的是,接受是会连乘的:一个令牌只有在同一轮里排在它前面的每个令牌也都被接受时才用得上,所以期望接受长度是次线性增长的,每多一个投机令牌都比前一个更不值钱。超过某个 k,你就是在花钱起草那些几乎永远走不到的令牌。
由此落下两条实践推论:
- 接受率依赖内容,而且依赖得可预测。高度模式化的输出——代码、结构化 JSON、正在被小幅编辑的文档,任何下一段几乎可以从上下文抄过来的场景——接受率远好过开放式散文。这正是那个便宜到近乎儿戏的 n-gram 草稿器在结构化输出和改文件类工作负载上真有竞争力的原因。
- 接受率值得记进日志。它是唯一能告诉你:在换过模型、重写过提示词、或用户诉求发生迁移之后,这个特性是否还在挣回自己饭钱的数字。接受率下滑是草稿已经不再匹配流量的先行指标。
这笔交易是拿算力换延迟——所以负载说了算。
这是最出人意料的一段,也是一项号称 2–3× 加速的技术有时反而让生产系统变慢的原因。
投机的原理是把闲置的算术换成更少的串行步数。在一块只服务一个请求的 GPU 上,闲置算术多得惊人,这笔交易近乎白送。可是服务引擎对付空闲 GPU 的办法是批处理:让许多请求共享同一趟权重扫描。大批量本就已经把算术单元填满,此时机器是算力受限而非带宽受限——再没有闲置容量可花,而每一个被拒绝的草稿令牌,都是本可以服务另一位用户的算力。
- 低并发——交互式对话、单个智能体逐步推进任务,任何有人盯着令牌一个个冒出来的场景。投机几乎是纯赚:那份算力你本来就在浪费。
- 高并发——一个被打满的端点在服务大量请求。投机可能拉低总吞吐,连带拉低你的每美元令牌数。服务引擎正是为此暴露了一个负载阈值;vLLM 的
--speculative-disable-by-batch-size会在并发请求数超过你设定的上限时关掉投机。 - 长上下文会挪动这条线,因为庞大的 KV 缓存会把系统在更高批量下重新推回带宽受限。你自己的临界点落在哪里,是一次测量,不是一个可以查表得到的数。
它在其他提速手段中的位置。
把投机摆到它的替代方案旁边,决策的形状就清楚了。这几栏里只有一栏不带质量争议。
- 量化把权重变小,于是每趟扫描读的内存更少。它是更大的单流收益,而且能与投机叠加——但它会改变输出,所以它要花掉你一个评测周期。见蒸馏与量化。
- 换更小的模型是收益最大的一招,也是质量风险最大的一招;把容易的请求路由给它,通常比让它承担全部要聪明。
- 提示词缓存攻的是预填充而不是解码,而在长上下文的智能体轮次里,被感知到的延迟往往正是待在那儿。
- 流式输出不让任何东西变快,它让等待变得可读——在多数界面上,这比 20% 的解码提升更值钱。见流式与部分输出。
如果你是从 API 买推理而不是自己跑,以上都不是你能拧的旋钮——它已经被默默地替你用上了,这也是同一个模型在不同供应商处延迟不同的原因之一。它要到你自托管那一刻,才成为你的决定。
如果你自托管、而且流量是交互式的,就配一个匹配良好的草稿头把投机解码打开,然后做两件没人做的事:在流量尖峰到来之前先把 disable-by-batch-size 阈值设好,并把接受率与延迟并列,当作一等指标记录下来。前者能防止你的延迟优化在最忙的那天变成一次容量事故;后者会告诉你它是从哪个月起不灵了。而且与清单上其他每一项不同,你不欠你的评测集一次重跑——这正是该优先伸手拿这一项的全部理由。
相关:预填充与解码讲这项优化所处的两个阶段,成本、质量与延迟讲它身处的三方权衡,批量与异步推理讲那些根本不以延迟为指标的工作负载。