用小模型"猜测"来换取大模型的推理加速
前言
上一篇我介绍了 vLLM 的 LoRA 动态加载,很多读者私信问:72B 这样的大模型推理太慢了,除了加显卡还有什么办法?
今天聊的投机解码(Speculative Decoding)就是答案。这是一个让我第一次看到就拍大腿的技术——不改模型、不加硬件、不损精度,推理速度直接翻倍。
💡 什么是投机解码?
投机解码是一种推理加速技术,核心思想是让一个轻量的”草稿模型”快速生成多个候选 token,然后让目标大模型并行验证。由于验证比逐个生成快得多,整体推理速度可以提升 1.5-2.5 倍,且输出质量与原模型数学等价。
为什么大模型推理慢
要理解投机解码为什么有效,得先搞清楚大模型推理的瓶颈在哪。
Memory Bound:显存带宽才是真凶
很多人以为大模型慢是因为计算量大。错了。
现代 GPU 的算力其实是过剩的。以 A100 为例:
- 算力:312 TFLOPS(FP16)
- 显存带宽:2 TB/s
一个 70B 模型(假设 FP16)的权重是 140GB。生成每个 token 都需要把这 140GB 权重从显存读到计算单元。按 2TB/s 的带宽算:
140GB ÷ 2TB/s = 70ms/token ≈ 14 tokens/s
而实际的矩阵运算只需要几毫秒。90% 以上的时间都在等数据搬运,GPU 大部分时候是闲着的。
📊 Memory Bound(显存带宽受限):指 GPU 的计算单元等待数据从显存传输过来的状态。此时算力是过剩的,瓶颈在于数据搬运速度。
自回归生成的原罪
雪上加霜的是,Transformer 的自回归生成机制要求一个一个 token 地生成。
输入:今天天气
第1步:今天天气 → 真(需要读取140GB)
第2步:今天天气真 → 不(需要读取140GB)
第3步:今天天气真不 → 错(需要读取140GB)
...
每生成一个 token,就要完整读取一遍模型权重。如果能一次生成多个 token,就能大幅减少权重读取次数。
问题是:怎么在不知道前一个 token 的情况下,预测下一个 token?
投机解码原理
投机解码的核心思想惊人地简单:让一个小模型先猜几个 token,再让大模型并行验证。
具体流程
假设我们要用 72B 大模型生成文本,配一个 1.5B 的小模型:
Step 1:小模型快速生成5个草稿 token
输入:"今天天气"
小模型输出:"真不错啊!" (5个token)
耗时:10ms
Step 2:大模型并行验证这5个 token
输入:"今天天气" + "真不错啊!"
大模型判断:
- "真" ✅ 接受(和大模型自己生成的一致)
- "不" ✅ 接受
- "错" ❌ 拒绝(大模型想生成"好")
- 后续 token 作废
耗时:70ms(只读取一次权重!)
Step 3:采纳被接受的 token,从拒绝点重新开始
已生成:"今天天气真不"
大模型同时输出正确的第3个token:"好"
继续下一轮...
关键洞察:大模型验证 5 个 token 和生成 1 个 token 的时间几乎一样(都是读取一次权重)!
如果小模型猜对了 3 个,我们就用 80ms 生成了 4 个 token(3 个猜对的 + 1 个大模型修正的),而传统方式需要 280ms。
为什么能保证输出质量
这是投机解码最精妙的地方:数学上可以证明,投机解码的输出分布与纯大模型完全一致。
验证阶段用的是一个叫”投机采样”的算法。简单说:
- 如果小模型和大模型对某个 token 的概率分布一致,直接接受
- 如果不一致,按一定概率接受或拒绝,保证最终分布正确
🔬 数学等价:这不是近似,是数学等价。你可以把投机解码的输出和纯大模型对比,统计上是完全一致的。
在 vLLM 中配置投机解码
vLLM 从 0.4.0 开始原生支持投机解码,配置非常简单。
基础配置
python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen2.5-72B-Instruct \
--speculative-model Qwen/Qwen2.5-1.5B-Instruct \
--num-speculative-tokens 5 \
--tensor-parallel-size 4
| 参数 | 说明 |
|---|---|
--speculative-model |
草稿模型,越小越好 |
--num-speculative-tokens |
每轮猜测的 token 数,通常 3-7 |
使用 ngram 作为草稿模型
如果你不想额外加载一个小模型,vLLM 还支持用 ngram 作为草稿模型:
python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen2.5-72B-Instruct \
--speculative-model "[ngram]" \
--num-speculative-tokens 5 \
--ngram-prompt-lookup-max 4
ngram 模式会从输入 prompt 中查找可能的续写。对于有重复模式的文本(如代码、模板),效果出奇地好。
MLPSpeculator:专门训练的投机头
这是最先进的方案。不用完整的小模型,而是训练一个轻量的 MLP 头来预测 token:
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3.1-70B-Instruct \
--speculative-model ibm-fms/llama3-70b-accelerator \
--num-speculative-tokens 5
💡 IBM 为 Llama 系列开源了一些 accelerator 模型,专门用于投机解码。
草稿模型选择指南
草稿模型的选择直接决定加速效果。
黄金法则
| 原则 | 说明 |
|---|---|
| Tokenizer 必须一致 | 最重要!词表不同会导致验证失败 |
| 架构相似 | 同系列模型效果最好(Qwen配Qwen,Llama配Llama) |
| 越小越好 | 草稿模型越小,生成越快,整体加速比越高 |
| 不要太笨 | 太小的模型猜测准确率太低,反而拖累性能 |
推荐配置
| 目标模型 | 推荐草稿模型 | 预期加速 |
|---|---|---|
| Qwen2.5-72B | Qwen2.5-1.5B 或 0.5B | 1.8-2.2x |
| Llama-3.1-70B | Llama-3.2-1B | 1.6-2.0x |
| Mistral-Large | Mistral-7B | 1.5-1.8x |
| DeepSeek-V2-236B | DeepSeek-V2-Lite-16B | 1.4-1.7x |
如何判断草稿模型是否合适
vLLM 会输出接受率(Acceptance Rate)的统计:
[Speculative Decoding] Acceptance rate: 0.72, Efficiency: 1.86x
- 接受率 > 0.6:草稿模型选得不错
- 接受率 < 0.4:考虑换一个更大/更准的草稿模型
-
接受率 > 0.9:可以尝试增加
num-speculative-tokens
性能调优实战
num-speculative-tokens 的选择
这个参数很关键,不是越大越好:
| 猜测数 | 效果 |
|---|---|
| 2-3 | 加速效果有限 |
| 4-6 | 通常最优 |
| 8+ | 验证开销增加,接受率下降 |
我的经验是从 5 开始,根据接受率调整:
- 接受率高(>0.7):可以增加到 6-7
- 接受率低(<0.5):减少到 3-4
显存规划
投机解码需要同时加载大模型和草稿模型:
总显存 ≈ 大模型显存 + 草稿模型显存 + KV Cache 开销
例如 72B + 1.5B:
- 72B(FP16):~144GB
- 1.5B(FP16):~3GB
- KV Cache:取决于并发和上下文长度
💾 显存紧张时的方案:
- 使用量化版大模型(AWQ/GPTQ)
- 选择更小的草稿模型(0.5B)
- 减少
gpu-memory-utilization
批处理场景的权衡
投机解码在低并发场景效果最好。高并发时:
- 大模型本身已经能充分利用 GPU
- 草稿模型的开销变得不可忽略
- 接受率通常会下降
| 并发数 | 建议 |
|---|---|
| < 4 | 投机解码效果显著 |
| 4-16 | 效果一般,需要测试 |
| > 16 | 可能不如关闭投机解码 |
实测数据
测试环境:
- GPU:4×A100 80GB
- 目标模型:Qwen2.5-72B-Instruct
- 草稿模型:Qwen2.5-1.5B-Instruct
- 输入长度:约 500 tokens
- 输出长度:约 200 tokens
| 配置 | 吞吐量 | 首 token 延迟 | 加速比 |
|---|---|---|---|
| 纯 72B | 16 tok/s | 180ms | 1.0x |
| + 投机解码 (k=5) | 28 tok/s | 220ms | 1.75x |
| + 投机解码 (k=7) | 31 tok/s | 250ms | 1.94x |
| ngram 模式 | 22 tok/s | 185ms | 1.38x |
📊 几个发现:
- 首 token 延迟会略增:因为要同时加载草稿模型
- 代码生成场景效果更好:接受率能到 0.8+,加速比接近 2.5x
- 创意写作场景效果一般:大模型和小模型的分布差异大,接受率只有 0.5 左右
常见问题
Q1: 开启投机解码后反而变慢了?
检查这几点:
- 草稿模型是否太大?试试更小的版本
- 并发是否太高?投机解码适合低并发
- 接受率是否太低?查看日志中的 acceptance rate
Q2: 报错 “Tokenizer mismatch”?
草稿模型和目标模型的 tokenizer 不兼容。必须使用同系列模型。
Q3: 可以用不同架构的模型吗?
技术上可以(只要 tokenizer 一致),但效果通常很差。小模型”理解”大模型的能力有限。
Q4: 投机解码和量化可以同时用吗?
可以!而且推荐这样做:
python -m vllm.entrypoints.openai.api_server \
--model TheBloke/Qwen2.5-72B-Instruct-AWQ \
--speculative-model Qwen/Qwen2.5-1.5B-Instruct \
--quantization awq \
--num-speculative-tokens 5
量化减少显存占用,投机解码提升速度,双重优化。
与其他优化技术的组合
投机解码可以和其他优化技术组合使用,效果叠加。
投机解码 + 量化
python -m vllm.entrypoints.openai.api_server \
--model TheBloke/Qwen2.5-72B-Instruct-AWQ \
--speculative-model Qwen/Qwen2.5-1.5B-Instruct \
--quantization awq \
--num-speculative-tokens 5 \
--tensor-parallel-size 2
量化减少显存占用,投机解码提升速度,双重优化。实测 AWQ 量化 + 投机解码可以在 2×4090 上跑 72B 模型,吞吐量约 25 tok/s。
投机解码 + Chunked Prefill
python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen2.5-72B-Instruct \
--speculative-model Qwen/Qwen2.5-1.5B-Instruct \
--num-speculative-tokens 5 \
--enable-chunked-prefill \
--max-num-batched-tokens 8192
💡 Chunked Prefill 改善长 prompt 场景的公平性,投机解码加速生成阶段。两者互不冲突。
结语
投机解码是我见过最”聪明”的推理优化技术:
| 特性 | 说明 |
|---|---|
| 原理简单 | 让小模型猜,大模型验 |
| 效果显著 | 1.5-2.5 倍加速,取决于场景 |
| 零精度损失 | 数学上等价于原始输出 |
| 配置简单 | vLLM 加两个参数就行 |
适用场景:
- ✅ 低并发、高延迟敏感的交互式应用
- ✅ 代码生成、模板填充等有规律的任务
- ⚠️ 高并发场景需要测试
- ❌ 超短输出(<10 tokens)效果有限
如果你在用 70B 级别的大模型,强烈建议试试投机解码。这可能是性价比最高的优化手段——不加显卡、不换模型,推理速度直接翻倍。
下一篇我会介绍 vLLM 的多模态模型部署,敬请期待。
参考资料
- Fast Inference from Transformers via Speculative Decoding
- vLLM Speculative Decoding 官方文档
- Medusa: Simple Framework for Accelerating LLM Generation with Multiple Decoding Heads
版权声明: 如无特别声明,本文版权归 sshipanoo 所有,转载请注明本文链接。
(采用 CC BY-NC-SA 4.0 许可协议进行授权)
本文标题:《 vLLM 高性能推理系列——投机解码加速 》
本文链接:http://localhost:3015/ai/vLLM-speculative-decoding.html
本文最后一次更新为 天前,文章中的某些内容可能已过时!
目录
- 前言
- 为什么大模型推理慢
- Memory Bound:显存带宽才是真凶
- 自回归生成的原罪
- 投机解码原理
- 具体流程
- 为什么能保证输出质量
- 在 vLLM 中配置投机解码
- 基础配置
- 使用 ngram 作为草稿模型
- MLPSpeculator:专门训练的投机头
- 草稿模型选择指南
- 黄金法则
- 推荐配置
- 如何判断草稿模型是否合适
- 性能调优实战
- num-speculative-tokens 的选择
- 显存规划
- 批处理场景的权衡
- 实测数据
- 常见问题
- Q1: 开启投机解码后反而变慢了?
- Q2: 报错 “Tokenizer mismatch”?
- Q3: 可以用不同架构的模型吗?
- Q4: 投机解码和量化可以同时用吗?
- 与其他优化技术的组合
- 投机解码 + 量化
- 投机解码 + Chunked Prefill
- 结语
- 参考资料