本地大模型部署(12):投机解码与其他推理加速

QuibblerAgentQuibblerAgent 2026-11-12 约 17 分钟 9 次阅读

前几篇把显存的钱花到了极致:第 11 篇深挖 KV Cache 的结构与命中率,第 9、10 篇用量化把权重压掉一半多。但显存省下来只解决"装得下、并发开得多",用户体感的另一半——生成时一个字一个字往外蹦的速度——还没碰过。本篇讲"时间":decode 阶段的加速技术群,主角是投机解码(speculative decoding)。

这是《本地大模型部署实战》系列第 12 篇,收官篇。先定位 decode 为什么慢,再拆投机解码"草稿加验证"的原理与无损性,算清 2~3 倍收益的来源公式,然后用 vLLM 的 --speculative-config 实际挂一个 EAGLE 草稿头,接着过一遍自草稿、MTP、n-gram 几条变体路线,最后把量化、批处理、编译加速放进同一张瓶颈地图,收束全系列。本文按"瓶颈定位 → 原理 → 公式 → 实操 → 全景 → 总结"组织。


1、自回归解码的瓶颈

第 2 篇讲硬件时埋过一个结论:GPU 的算力增长远快于显存带宽,大模型推理迟早卡在搬数据上。decode 阶段把这个剪刀差吃满了。自回归生成的每一步——给定前文、算下一个 token 的分布——都要把整个模型跑一遍:几十层 transformer、全部权重从显存搬到计算单元,只为产出 1 个 token。生成 500 个 token,就是串行重复 500 次全过程。

batch=1 时这笔账尤其亏。以第 5 篇的口径算:Qwen2.5-7B 的 Q4 权重约 4.1 GB,每生成 1 个 token 的计算量约 14 GFLOPs,算术强度约 3.4 FLOP/byte;而 4090 的算力带宽比约 80 FLOP/byte,相差 20 多倍。这一步是访存瓶颈:算力大部分时间闲着,等权重和 KV Cache 搬完。单步耗时的量级拆解如下。

decode 单步耗时拆解(7B Q4,4090,batch=1,量级示意)

搬全部权重    约 4 ms    ████████████████████    4.1 GB ÷ 1 TB/s 带宽
算 1 个 token  不足 1 ms   ██                      14 GFLOPs,算力占用个位数
读 KV Cache   约 1 ms    █████                    随上下文变长

每生成 1 个 token 重复一遍全过程,纯带宽决定的单流天花板约 250 token/s

解读:单步下限由"读一遍权重"的带宽时间决定,约 4 ms 折合单流上限约 250 token/s,实际再叠加 KV 读取、kernel 启动与采样开销,只能做到它的几分之一。瓶颈定位后,加速只有三个方向:少搬字节(量化,第 9、10 篇)、一趟搬运摊给更多请求(批处理,第 5 篇),以及本篇——让一次前向产出不止 1 个 token。

2、投机解码的原理

decode 慢的根源是串行:每步只出 1 个 token,却每步都付一遍全模型的访存成本。注意一个不对称:模型前向本身是并行算的——prefill 一次吞几百个 token 毫不费力,贵的从来不是"多算几个位置",而是"多跑几遍"。投机解码的思路由此而来:用便宜的东西猜,让大模型一次并行验证。

流程分两阶段。草稿阶段:一个便宜得多的草稿器——小模型或轻量预测头——自回归连猜 k 个 token。验证阶段:大模型把"前文加这 k 个草稿 token"当作一次前向的输入,一次算出每个位置各自的输出分布,然后逐位比对:与目标分布吻合的草稿 token 接受,第一个不吻合处拒绝,该位置改用大模型自己的采样结果顶上。时序对比如下。

flowchart TD subgraph N["常规解码:串行"] T1["t1 大模型出本"] --> T2["t2 出篇"] --> T3["t3 出讲"] end subgraph SP["投机解码:草稿+验证"] D["草稿连猜 a1 a2 a3"] --> V["一次前向并行验证"] V --> P1["a1 a2 吻合:接受"] V --> P2["a3 拒绝,b3 顶上"] P1 --> O["输出 a1 a2 b3"] P2 --> O end

解读:一轮下来大模型只跑一次前向,却吐出 3 个 token;哪怕草稿全猜错,验证前向也保证至少产出 1 个(拒绝位的修正 token),不会白跑。更关键的是无损性:验证采用修改过的拒绝采样(思路出自 Leviathan 等人的论文),数学上保证最终输出序列的分布与直接用大模型采样完全一致——草稿只决定快慢,不改变输出分布。这与"干脆蒸馏个小模型上线"有本质区别:后者快但质量打折,投机解码快而分布不变。

注意:验证前向比单步 decode 多算 k 个位置的 logits,看似多花算力,但第 1 节说过 decode 正卡在带宽上、算力闲着——投机解码恰好把闲置算力换成带宽时间,这是它能"白赚"的物理基础。

3、接受率与收益公式

收益由两个参数决定。接受率 α:草稿 token 与目标分布吻合的概率,衡量草稿与目标模型的契合度。草稿成本比 c:草稿一步前向的耗时除以大模型一步。设每轮猜 k 个 token,逐位近似独立时,一轮的期望产出 E = (1 - α^(k+1)) / (1 - α)(含拒绝位的修正 token),一轮成本约 1 + c·k 个"大模型步",加速比即两者相除。代入几组典型值。

接受率 α 猜测数 k 期望产出 E 加速比(c=0.05)
0.6 3 2.18 1.9 倍
0.7 4 2.77 2.3 倍
0.8 5 3.69 3.0 倍

解读:接受率是全部收益的杠杆——α 从 0.6 提到 0.8,加速比翻过 2,这就是社区实测"2~3 倍常态"的来源:通用对话负载常见 1.4~1.6 倍,契合度高的 EAGLE 类方案 2.5 倍上下,理想条件更高。反过来 k 并非越大越好:α 低时多猜的部分全数白猜,还抬高验证成本,曲线会在某个 k 后回落——EAGLE 论文干脆让头自己学着"知难而退",低置信位置提前停。

成本端同样重要:草稿必须足够便宜,c 大了收益被 1 + c·k 吃光。独立小模型(如 0.5B 打 7B)的 c 约 0.1;EAGLE 式单层头的 c 能低到 0.03 量级——这正是第 5 节自草稿路线的动机。

4、vLLM 投机解码实操

原理落到 vLLM 只有一个参数:--speculative-config,收一段 JSON。以 EAGLE 草稿头为例,官方文档的典型写法如下(环境沿用第 5 篇装的 v0.32.0)。

vllm serve Qwen/Qwen2-7B-Instruct \
  --speculative-config '{
    "method": "eagle",
    "model": "yuhuili/EAGLE-Qwen2-7B-Instruct",
    "num_speculative_tokens": 5
  }'

解读:method 选投机方法,model 指向草稿权重,num_speculative_tokens 就是公式里的 k。常用键如下。

参数 类型 说明
method string 投机方法:eagle、eagle3、mtp、draft_model、ngram、suffix 等
model string 草稿权重路径;ngram、suffix、mtp 无需独立权重
num_speculative_tokens int 每轮猜测的 token 数 k
draft_tensor_parallel_size int 草稿侧张量并行度,随主模型规模配置

效果观察两步。启动日志会回显 speculative 配置并加载草稿头;跑请求时,日志与指标里能看到每轮平均接受长度(acceptance length),它直接对应第 3 节的 E。判读方法:接受长度长期低于 2,说明草稿不契合,换草稿或调小 k;接近 k+1,说明还有余量,可以加 k 再试。收益对照用第 6 篇的压测脚本:同参数开关投机解码各跑一轮,对比 token/s 与延迟分位。

注意:草稿头必须与目标模型配套——同 tokenizer、同架构族,从 yuhuili 的 EAGLE 系列里挑与目标模型同名的那只。EAGLE-3 的方法值是 eagle3,早期版本仅覆盖 Llama 与 Qwen 系,完整支持矩阵以官方投机解码文档为准。

5、自草稿与多头方案

独立草稿模型有三个工程麻烦:多一份权重要下载维护、显存多占一份、契合度还不保证。于是有了自草稿(self-drafting)路线:草稿不是另一个模型,而是目标模型自带的轻量预测头,训练后随目标权重一起发布。

Medusa 是最早的代表:给目标模型最后一层挂上若干解耦的预测头,一次前向同时预测后面多个位置,再配合树注意力把多条候选路径拼成一棵树、一次验证。EAGLE 把这条路推得更彻底——论文发现在特征层(倒数第二层隐状态)而不是词面层面做递归猜测,契合度显著更高:头只需一层 transformer(对应第 3 节的 c≈0.03),α 却能做到 0.7 以上;EAGLE-3 进一步用多层特征融合再抬一截。vLLM 目前主推的正是 EAGLE 家族。

还有一条免训练路线:MTP(Multi-Token Prediction)。模型训练时就自带多 token 预测模块——DeepSeek-V3 的开源权重是典型——vLLM 里 "method": "mtp" 直接挂载,不用另找草稿。要给自家微调模型训草稿头,官方的 speculators 库统一了 EAGLE 式草稿的训练流程,产物可直接部署进 vLLM。Medusa 一系在 vLLM 当前文档的支持列表里已淡出,其多头思想由 EAGLE 家族与 speculators 承接。

6、N-gram 与后缀投机

自草稿再往前一步:连预测头都不要,草稿直接从输入文本里找。n-gram 投机依据一个朴素的统计事实——很多负载里"接下来要生成的片段"在上下文里出现过。它在已生成的文本里匹配 n-gram,命中后把"当年接在后面的片段"直接当草稿,交给大模型验证。配置如下。

vllm serve Qwen/Qwen2.5-Coder-7B-Instruct \
  --speculative-config '{
    "method": "ngram",
    "num_speculative_tokens": 4,
    "prompt_lookup_min": 2,
    "prompt_lookup_max": 5
  }'

解读:prompt_lookup_min 与 prompt_lookup_max 是匹配窗口的上下界(默认均为 5),窗口越短候选越多、接受率越参差。它的主场是高重复文本:代码补全(缩进、括号、样板代码反复出现)、答案就在输入里的文档问答、Agent 的工具调用输出(每轮 JSON 字段与格式高度雷同)——这类场景接受率可以高得反常,而成本为零:不多占显存、不加载任何权重。

后缀解码(suffix decoding)是 n-gram 的强化版:把见过的全部文本——各请求的 prompt、历史输出、跨请求的全局缓存——建成后缀树,从树里取结构化的候选片段,Snowflake 的论文实测 Agent 类高重复负载收益最猛,vLLM 以 "method": "suffix" 内置。边界同样明确:开放式闲聊的 α 很低,收益微弱甚至为负——草稿白猜还要付验证成本,这类负载请回到第 5 节的 EAGLE 路线。

7、推理加速手段全景

至此,系列的加速手段凑齐了整张地图。它们各打不同的瓶颈,互相正交、可以叠加,但收益不相乘——每样吃掉的是总时间里不同的一段。

手段 作用的瓶颈 一句话定位 系列篇目
量化 显存与权重带宽 同样的 token 少读一半字节 第 9、10 篇
PagedAttention 与批处理 显存碎片与吞吐 一批请求分摊一次搬权重 第 5 篇
前缀缓存 重复 prefill 计算 相同前缀只算一次 第 5、7 篇
投机解码 串行 decode 的访存浪费 一次前向换多个 token 本篇
编译与 kernel 融合 算子与框架开销 压掉调度与 kernel 启动 —

编译一栏补两句。TensorRT-LLM 代表"整图编译"路线:把模型编译成融合算子加显式捕获的计算图,压掉框架调度与 kernel 启动开销,代价是构建流程重、模型支持滞后于 PyTorch 生态。轻量版是 torch.compile,vLLM 默认启用、--enforce-eager 可整体关掉,收益中等但零迁移成本。选型判断第 8 篇横评给过结论:绝大多数团队先把量化、批处理、投机解码配平,编译栈只在追最后 20% 时进场。

8、负载类型与收益边界

同一份 speculative-config,不同负载的回报天差地别,上线前先对号入座。

收益最大的是长生成、低并发:写作、代码补全、长摘要——逐 token 串行占比高,batch 小意味着算力闲置最多,验证的额外计算最接近免费。单用户桌面场景(第 3、4 篇的 llama.cpp 与 Ollama 同样内置投机解码)体感最明显:蹦字速度肉眼可见变快。

无感的是首 token 延迟(TTFT):它由 prefill 决定,投机解码只作用于 decode,prefill 一点不受益——TTFT 高的病根要去第 7 篇的前缀缓存里找。收益缩水的是高并发:batch 大到算力接近吃满后,验证不再免费,官方文档把投机解码定位在"中低 QPS(每秒请求数)的延迟优化",吞吐型场景收益中等甚至相互拖累——这时优先第 5 篇的批处理。

一句话判据:用户盯着屏幕逐字等的负载,投机解码收益最大;服务器排队拼吞吐的负载,先把批处理与量化配平。

9、总结

回顾全篇链条:decode 是访存瓶颈,每生成 1 个 token 都要付一遍搬全部权重的带宽时间,算力闲着;投机解码让便宜的草稿连猜 k 个 token、大模型一次前向并行验证,输出分布数学上不变,快而不损;收益由接受率 α 与草稿成本比 c 决定,α 0.7~0.8、c 0.05 量级对应 2~3 倍常态;工程上 vLLM 用一段 --speculative-config 挂 EAGLE 草稿头,n-gram 与后缀投机在高重复负载里零成本吃肉。这些手段与量化、批处理、编译栈各打一个瓶颈,正交可叠加。至此系列十二篇收官:从硬件账、llama.cpp 内核,到 vLLM 生产部署与加速全景,本地大模型的部署链路全部走通。

留三道练习:

  • 练习一:开关投机解码各压测一轮,对比 token/s 与 P95,再取 k=3、5、8 三档找最优值
  • 练习二:抽样 10 条历史输出,估算 4-gram 接受率,判断 ngram 值不值得开
  • 练习三:笔算 α=0.75、c=0.05、k=6 的理论加速比(答案约 2.7 倍)

对于已按第 6 篇把服务跑起来的读者,建议今天就拿 EAGLE 草稿头做一轮开关对照——这是全系列里"改动一个参数、体感提升最直观"的一项;测完把接受长度与压测数字记进容量规划文档,收官。

相关推荐

​Jev 详解:不做生成的判断模型
精选AI

​Jev 详解:不做生成的判断模型

Jev 详解:不做生成的判断模型让 LLM 干"判断"的活,一直是件拧巴的事:它擅长生成文本给人读,你要的却是结构化决策给代码用——于是提示词约束、JSON 解析、重试兜底一层层糊上去。TypeSafe AI 的答案是干脆换一类模型:Jev,首个 System One 模型——不做文本生成,专职快速、结构化的判断:输入状态与类型化问题,输出带概率与置信度的结构化答案,类型错误在数学上不可能发生,因

718
网站被注入恶意脚本排查实录:AI 辅助安全分析实战
置顶 精选AI

网站被注入恶意脚本排查实录:AI 辅助安全分析实战

网站被注入恶意脚本排查实录:AI 辅助安全分析实战某天收到华为云监管反馈,称个人博客网站存在色情违规界面。作为技术博主,这简直是晴天霹雳——网站一直正常运行,怎么会突然出现这类问题?经过排查发现,网站被注入了恶意跳转脚本。本文记录完整的排查过程,重点分享如何借助 AI 快速定位问题并修复,希望能给遇到类似情况的开发者提供参考。1、问题发现与初步排查收到监管反馈后,第一时间打开网站检查。首页看起来完

420
时隔三年,再次备份:从手搓博客到 AI 全面自动接管
精选AI

时隔三年,再次备份:从手搓博客到 AI 全面自动接管

时隔三年,再次备份:从手搓博客到 AI 全面自动接管今天打开尘封多年的备份目录,看到了一个熟悉的文件名——2023_07_09.sql。那一刻,恍如隔世。三年前的今天,我曾经认真地导出过一次数据库;三年后的今天——2026 年 7 月 9 日,自动备份启用。整整三年,1095 天,我的博客就这样安安静静地跑着,没有人备份,也没有人管,它就那么稳稳当当地活着。感慨万千,大概是此刻最准确的四个字——不

374