5.2 Draft 模型与 N-gram / Suffix 方案
独立小模型的选择与部署(大小、词表、TP 配置、TLI 跨词表),以及无需额外模型的 N-gram 与 Suffix Decoding 方案,理解接受率与收益的权衡
5.1 节建立了投机解码的框架:提案器 + 验证器 + Rejection Sampling。这一节回答最实际的问题——提案器从哪来? 两条路线:一是用独立的小模型(Draft Model),二是完全不用模型,用 N-gram 匹配或 Suffix Decoding 这类基于文本模式的算法。前者收益高但引入部署成本,后者几乎零成本但收益有限。选哪条,取决于你的场景对”接受率”的要求。
📑 目录
- 1. 独立 Draft 模型:最朴素也最通用的提案器
- 2. 模型选择:大小、词表与能力匹配
- 3. 部署要点:TP 配置与显存
- 4. 跨词表方案:TLI 算法
- 5. N-gram 方案:零模型的匹配式提案
- 6. Suffix Decoding:比 N-gram 更聪明的模式匹配
- 7. 三种方案怎么选
- 📝 总结
- 🎯 自我检验清单
- 📚 参考资料
1. 独立 Draft 模型:最朴素也最通用的提案器
思路一句话:拿一个同架构、同词表、小得多(通常 1/10 到 1/100 参数量)的模型当提案器。它在同一份 prompt 前缀上自回归生成 N 个候选 Token,每个候选附带草稿概率 ,交给 Target 验证。
为什么”同词表”重要?因为 Rejection Sampling 需要把草稿概率 和 Target 概率 在同一个 Token 上比较。词表不同(比如 tokenizer 切分方式不同),两边给出的 Token ID 无法对应,仲裁无从谈起。这也是 vLLM 的 draft_model 方法默认要求草稿与目标共享词表的原因。
实际中最经典的开箱组合(vLLM 官方文档示例):
Qwen/Qwen3-8B+Qwen/Qwen3-0.6B(同家族同词表,0.6B 是 8B 的 1/13)meta-llama/Llama-3.1-8B-Instruct+ 同家族小模型
💡 提示:同家族的小模型(Qwen3-0.6B 之于 Qwen3-8B、Llama-3.2-1B 之于 Llama-3.1-8B)是最省心的选择——词表、架构、训练分布天然对齐。跨家族的组合不是不行,但要么自己处理词表对齐(TLI,见第 4 节),要么接受接受率偏低。
1.1 收益曲线:为什么”小”是双刃剑
5.1 节的加速比公式 直接决定收益,而 和 都由草稿模型的大小决定:
- 草稿越大 → 分布 越接近 → 越高 → 越大;但同时 变大,每轮草稿成本 升高
- 草稿越小 → 草稿快但 低, 逼近 1,投机等于白干
最优草稿大小是”让每轮净收益最大”的平衡点,实践中靠扫参数(不同草稿大小 × 不同 N)找。经验区间:目标模型 8B 级配 0.5-1B 草稿,70B 级配 1-3B 草稿。
📌 关键点:草稿模型的大小不是”越小越好”也不是”越大越好”。 和 是同一个旋钮的两端——调参的本质是在”猜得准”和”猜得快”之间找平衡。这也催生了 5.3 节的 Self-Draft 方案:它们用目标模型自己的浅层当提案器,让”猜得准”和”猜得快”不再互斥。
2. 模型选择:大小、词表与能力匹配
选择独立草稿模型时,除了大小还有三个实操维度:
2.1 词表(Vocabulary)对齐
如第 1 节所述,默认要求草稿与目标共享 tokenizer。检查方法很简单:加载两个模型的 tokenizer,对同一段文本编码,看 token ID 序列是否一致。不一致时有两个选择:
- 换同家族的草稿模型(推荐,零成本)
- 启用 vLLM 的
use_heterogeneous_vocab: true(TLI 算法,第 4 节,有额外限制)
2.2 能力匹配:草稿只需”像”目标,不必”懂”任务
草稿模型的核心指标只有一个:在目标模型擅长的内容上,草稿分布的接受率。它不需要具备推理能力——需要”推理”的 Token 反正大概率会被拒绝,交给 Target 兜底。但要注意极端不匹配:
- 草稿是代码模型、目标是对话模型:代码场景接受率尚可,对话场景接受率很低
- 草稿的知识截止日期远早于目标:新知识相关 Token(人名、新术语)几乎必被拒绝,但这类 Token 占比通常不高
💡 提示:有个反直觉的点——草稿模型不需要做任何微调也能工作(LLM 生成文本的低熵特性决定的),但微调过的草稿(用目标模型的输出分布做蒸馏式训练)接受率显著更高。vLLM 官方维护的 Speculators 库就是干这个的:用 vLLM 离线生成训练数据,训练草稿模型,再无缝接入 vLLM。
2.3 采样配置要与目标一致
草稿模型的采样参数(temperature、top_p)需要和 Target 的采样配置协调。vLLM 中 draft_sample_method 支持 greedy(贪心草稿,默认)和 probabilistic(概率草稿)。贪心草稿通常接受率更稳定,也是 TLI 模式唯一支持的选项。
3. 部署要点:TP 配置与显存
独立草稿模型是第二个模型,部署时它占用一份独立显存,这直接影响你的显存规划:
3.1 Tensor Parallel 配置
vLLM 中草稿模型的 TP 由 draft_tensor_parallel_size 控制,只能取 1 或与 Target 相同:
draft_tensor_parallel_size=1:草稿模型完整放单卡,不参与 TP 通信。适合小草稿模型,省通信开销(默认)- 与 Target 相同(如
tp=4+draft_tp=4):草稿也切 4 份。适合草稿模型大到单卡装不下的场景,例如 70B 目标配 8B 草稿、8 卡部署时
📌 关键点:TP 的直觉是”模型越大越要切”,但草稿模型追求的是低延迟——TP 引入的 AllReduce 通信可能比草稿模型本身的推理还慢。所以除非显存放不下,草稿模型用
draft_tensor_parallel_size=1通常延迟最优。这和第 6 章”TP 不跨节点”的理由一脉相承。
3.2 显存规划
草稿模型、KV Cache、激活三者共享 GPU 显存。vLLM 的显存规划以 Target 模型为基准自动预留,但如果 gpu_memory_utilization 设得太高,草稿模型可能装不下——启动日志里会出现”draft model loading”阶段的 OOM。经验做法:给草稿模型留出与其参数量匹配的余量(如 8B 目标 + 0.6B 草稿,把利用率从 0.95 降到 0.9 左右),或使用 max_model_len 限制草稿的上下文长度。
3.3 与量化叠加
草稿模型可以独立量化(vLLM 的 speculative_config 里有 quantization 字段指定草稿的量化方式)。但注意第 5.4 节会详细讨论:量化 Target 会改变 ,草稿的 没变,接受率可能小幅下降。实践中常做的是”Target 量化、草稿保持 FP16/BF16”——草稿小,量化省下的显存有限,但保留精度对维持接受率更有利。
4. 跨词表方案:TLI 算法
现实中有时候你就是想用不同家族的草稿模型(比如手头只有某个现成的小模型)。vLLM 为此实现了 **Token-Level Intersection(TLI)**算法,通过 use_heterogeneous_vocab: true 开启:
- 初始化:对两个 tokenizer 的全部 Token 做字符串归一化,计算词表交集,建立草稿 Token ID → 目标 Token ID 的映射表
- 草稿采样:把草稿模型的 logits 约束到交集 Token 子集上,只在共享 Token 内采样
- 仲裁:草稿概率与 Target 概率在共享 Token 上做 Rejection Sampling;被接受的草稿 Token ID 映射回目标的词表
代价与限制:
- 交集之外的 Token(通常是生僻词、特殊符号)永远不会被草稿猜中,接受率上限被压缩
- 目前仅支持贪心草稿采样(
draft_sample_method='greedy'),概率草稿尚不支持 - 两个 tokenizer 差异越大,交集越小,收益越差
💡 提示:TLI 是”退而求其次”的方案。如果同词表的草稿模型不存在(比如目标模型很新、生态里没有配套小模型),TLI 能让你先用任意小模型顶上——但接受率大概率不如同家族组合。SGLang、TensorRT-LLM 也有类似机制,名字各有不同,思路一致。
5. N-gram 方案:零模型的匹配式提案
5.1 原理:从 prompt 里抄作业
N-gram 提案器的想法极其朴素:LLM 生成的内容经常在 prompt 里出现过(用户给了示例、few-shot 上下文、代码片段、重复的格式)。那么——直接在 prompt 的历史 Token 里找”最近 N 个 Token”的重复出现,把那次出现后面的 Token 抄下来当候选:
Prompt 历史: ... A B C D E F ... (某个位置出现过 "B C D")
当前上下文: A B C ← 以 "B C" 结尾
匹配: 找到历史中的 "B C",后面跟 "D E F"
候选: D, E, F ← 直接抄
vLLM 的实现由两个参数控制:
prompt_lookup_max:匹配窗口上限(默认 5)prompt_lookup_min:匹配窗口下限(默认与 max 相同)
每个位置在 范围内找最长可匹配的窗口,把后续 Token 作为候选。找不到匹配就停止本轮提案。
5.2 优劣:零成本,但天花板低
| 维度 | 评价 |
|---|---|
| 部署成本 | 零——不需要第二个模型,不需要额外显存,一条配置即可开启 |
| 接受率 | 中低。只在”内容确实重复”时有效:代码补全、JSON/XML 格式、log 解析、few-shot 模仿 |
| 适用场景 | 离线批处理、成本敏感、不想引入第二个模型的环境 |
| 天花板 | 无法猜”没出现过”的内容——对话、创意写作等场景基本无效 |
📌 关键点:N-gram 的适用性可以用一句话判断——“输出是否会大量复现输入中出现过的片段”。代码生成(函数体重复)、结构化输出(重复的字段模式)、Agent 循环(重复的工具调用格式)都是好场景;开放式对话不是。
5.3 vLLM 配置
vllm serve Qwen/Qwen3-8B \
--speculative-config '{
"method": "ngram",
"num_speculative_tokens": 5,
"prompt_lookup_max": 4,
"prompt_lookup_min": 2
}'
💡 提示:N-gram 提案器有一个隐藏优点——它完全不占显存。在显存已经吃紧(比如量化后 KV Cache 才是大头,见 4.4 节)的部署里,N-gram 是唯一”免费”的投机手段。这也是 vLLM 官方在”方法选型总览”里把它列为”peak traffic 下不增加负载”选项的原因。
6. Suffix Decoding:比 N-gram 更聪明的模式匹配
Suffix Decoding(arXiv:2411.04975,vLLM v0.26 支持)是 N-gram 的进化版,解决 N-gram 的两个痛点:
| 痛点 | N-gram | Suffix Decoding |
|---|---|---|
| 匹配范围 | 只在 prompt 里找 | prompt 和之前生成的历史都能匹配 |
| 匹配策略 | 找到”最长窗口”就抄 | 用频率计数提出最可能的延续(统计”B C”后面跟过哪些 Token、各多少次) |
| 草稿长度 | 固定 N | 每轮自适应——根据匹配质量和接受率动态决定草稿多长 |
Suffix Decoding 在 vLLM 中的关键参数:
| 参数 | 默认 | 含义 |
|---|---|---|
suffix_decoding_max_tree_depth | 24 | 前缀匹配 + 投机树的最大深度 |
suffix_decoding_max_cached_requests | 10000 | 全局后缀树缓存的请求数上限(0 = 关闭全局缓存) |
suffix_decoding_max_spec_factor | 1.0 | 投机长度上限 = 前缀匹配长度 × 该系数 |
suffix_decoding_min_token_prob | 0.1 | 提议一个 Token 的最低估计概率阈值 |
它的典型高收益场景:高重复性任务——代码编辑(diff 格式大量重复)、Agentic 循环(self-reflection、self-consistency 反复生成相似内容)、RL rollout(同一策略反复采样)。
💡 提示:Suffix Decoding 需要安装
arctic-inference包(pip install arctic-inference)。另外注意它的num_speculative_tokens是上限——实际每轮草稿长度自适应,官方建议设大(16 或 32),让算法自己决定。
7. 三种方案怎么选
| 方案 | 接受率 | 部署成本 | 显存开销 | 典型场景 |
|---|---|---|---|---|
| 独立 Draft 模型 | 高(同家族) | 中(第二个模型) | 中(0.5-3B 权重) | 追求最大加速、有配套小模型 |
| N-gram | 中低 | 零 | 零 | 内容重复度高、显存吃紧、不想加模型 |
| Suffix Decoding | 中(重复任务中高) | 低(一个 pip 包) | 低 | 代码编辑、Agent 循环、RL rollout |
决策直觉:
- 有同家族小模型 → Draft 模型,收益最大
- 没有小模型、但任务重复度高(代码/结构化/Agent)→ Suffix Decoding(> N-gram)
- 只想要个”免费的”兜底、或者显存已满 → N-gram
📌 关键点:三者的共同归宿是 5.1 的公式——加速比由 与 决定。Draft 模型用”更贵的 “换”更高的 “;N-gram/Suffix 用”零 “换”更低的 “。没有绝对最优,只有场景适配。
📝 总结
- 独立 Draft 模型:同家族同词表小模型,接受率最高;大小是”猜得准 vs 猜得快”的平衡点;TP 默认 1,显存需单独规划
- 词表对齐是硬约束:共享词表是默认前提;跨词表用 TLI(Token 级交集 + logits 约束),但有接受率上限和贪心限制
- N-gram:零成本零显存的匹配式提案,在内容重复场景有效,适合成本敏感/显存吃紧环境
- Suffix Decoding:N-gram 的进化版——匹配历史生成、频率计数、自适应草稿长度,适合代码编辑与 Agent 循环
- 选型直觉:有小模型选 Draft,重复任务选 Suffix,兜底选 N-gram
🎯 自我检验清单
- 为什么草稿模型必须和 Target 共享词表?TLI 是怎么绕过这个约束的?
-
draft_tensor_parallel_size为什么”只能取 1 或与 Target 相同”?为什么默认 1 通常更好? - 草稿模型大小为什么不是越大越好? 和 如何此消彼长?
- N-gram 的匹配窗口参数(min/max)分别控制什么?
- Suffix Decoding 相对 N-gram 的三个改进是什么?
- 显存吃紧、任务重复度高、追求最大加速——三种情况分别选什么方案?
📚 参考资料
- Leviathan et al., Fast Inference from Transformers via Speculative Decoding(https://arxiv.org/abs/2211.17192)
- vLLM 官方文档:Draft Model(https://docs.vllm.ai/en/latest/features/speculative_decoding/draft_model.html)
- vLLM 官方文档:N-Gram(https://docs.vllm.ai/en/latest/features/speculative_decoding/n_gram/)
- vLLM 官方文档:Suffix Decoding(https://docs.vllm.ai/en/latest/features/speculative_decoding/suffix.html)
- Zhang et al., Suffix Decoding: Accelerating Language Generation via Pattern Matching(https://arxiv.org/abs/2411.04975)
- vLLM-Project/Speculators:草稿模型训练库(https://github.com/vllm-project/speculators)