推理优化:量化、KV Cache 与 vLLM
目标:把「能跑」变成「跑得起」。
模型效果的最后一公里往往不在算法,而在延迟、吞吐、成本。这一篇是 AI 工程师最值钱的技能之一。
一、先量化你的问题
| 指标 | 定义 | 用户感受 |
|---|---|---|
| TTFT(首 token 延迟) | 请求到第一个 token 的时间 | 「点了之后多久有反应」 |
| TPOT(每 token 时间) | 之后每生成一个 token 的耗时 | 「出字速度」 |
| 吞吐 | tokens/秒(全局) | 决定成本 |
| QPS | 每秒完成请求数 | 决定需几张卡 |
优化目标不同,手段完全不同:对话要低 TTFT,批处理要高吞吐。
二、KV Cache:为什么推理能加速
自回归生成里,每生成一个新 token 都要对全部历史做注意力。若每步重算,复杂度爆炸。
KV Cache 把历史 token 的 K、V 缓存下来,每步只算新 token:
python
# 概念示意
past_k, past_v = None, None
for step in range(max_new_tokens):
k_new, v_new = project(x_last) # 只算 1 个 token
k = torch.cat([past_k, k_new], dim=1) if past_k is not None else k_new
v = torch.cat([past_v, v_new], dim=1) if past_v is not None else v_new
attn_out = attend(q_new, k, v) # 历史 K/V 直接复用
past_k, past_v = k, v代价:显存。KV Cache 大小:
$$\text{bytes} \approx 2 \times L \times h \times d \times T \times \text{batch} \times \text{精度字节}$$
| 变量 | 含义 |
|---|---|
| 2 | K 和 V 各一份 |
| L | 层数 |
| T | 上下文长度 |
| h×d | 注意力维度 |
这解释了两件事:为什么长上下文极耗显存;为什么并发数受显存限制而不是算力。
三、量化:最直接的省钱手段
| 精度 | 每参数字节 | 7B 模型权重 | 质量损失 |
|---|---|---|---|
| FP32 | 4 | 28 GB | 无 |
| FP16/BF16 | 2 | 14 GB | 几乎无 |
| INT8 | 1 | 7 GB | 很小 |
| INT4 (GPTQ/AWQ) | 0.5 | 3.5 GB | 小,可接受 |
| 2–3 bit | 0.25–0.4 | ~2 GB | 明显下降,慎用 |
python
# 用 bitsandbytes 直接 4bit 加载(最省事)
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
import torch
bnb = BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16)
model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-7B-Instruct",
quantization_config=bnb, device_map="auto")| 量化方法 | 特点 | 何时用 |
|---|---|---|
| bitsandbytes (NF4) | 加载即用、易上手 | 快速试验、显存紧张 |
| GPTQ | 训练后量化,推理快 | 部署 |
| AWQ | 激活感知,精度更好 | 推荐的部署选择 |
| GGUF(llama.cpp) | CPU/Mac 友好 | 本地/端侧 |
量化不是无损
数学推理、代码生成这类任务对量化更敏感。量化后必须重跑你的评测集,别只看「能说人话」。
四、vLLM:生产部署的默认选择
核心是 PagedAttention(把 KV Cache 像操作系统分页一样管理)+ 连续批处理(新请求随时插入)。
bash
pip install vllm
# 起一个 OpenAI 兼容服务
python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen2.5-7B-Instruct \
--dtype bfloat16 \
--max-model-len 8192 \
--gpu-memory-utilization 0.90 \
--tensor-parallel-size 1 \
--port 8000python
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1", api_key="dummy")
resp = client.chat.completions.create(
model="Qwen/Qwen2.5-7B-Instruct",
messages=[{"role": "user", "content": "用一句话解释 KV Cache"}],
temperature=0.2, max_tokens=128)
print(resp.choices[0].message.content)关键参数
| 参数 | 作用 | 建议 |
|---|---|---|
--max-model-len | 最大上下文 | 按需设小,省显存 |
--gpu-memory-utilization | 显存占用上限 | 0.85–0.92(留给激活值) |
--max-num-seqs | 最大并发 | 吞吐与延迟的平衡旋钮 |
--tensor-parallel-size | 张量并行数 | = GPU 数 |
--quantization | awq / gptq / fp8 | 按权重格式选 |
--enable-prefix-caching | 复用相同前缀的 KV | RAG/固定 system 提示必开 |
前缀缓存是白捡的性能
RAG 场景中,system prompt 与长文档前缀高度重复。开启前缀缓存后 TTFT 常降 30%–70%。
五、优化手段全景与收益排序
| 手段 | 收益 | 代价 | 优先级 |
|---|---|---|---|
| 前缀缓存 / KV 复用 | 高(TTFT↓) | 几乎零 | ★★★★★ |
| 连续批处理(vLLM) | 高(吞吐↑ 5–20×) | 需换推理框架 | ★★★★★ |
| 量化(AWQ/INT8) | 高(显存↓、速度↑) | 少量质量损失 | ★★★★★ |
| 流式输出 | 用户感知 TTFT↓ | 工程改造 | ★★★★★ |
| 投机解码(Speculative) | 中(速度↑ 1.5–2.5×) | 需草稿模型 | ★★★ |
| 换更小模型 / 蒸馏 | 高(成本↓ 10×) | 需重训与评测 | ★★★★ |
| 语义缓存(相似问题复用答案) | 高(命中时极省) | 相似度阈值风险 | ★★★ |
| 上下文裁剪 / 摘要 | 中(token↓) | 可能丢信息 | ★★★★ |
| FlashAttention | 中(长序列明显) | 框架支持即可 | ★★★ |
六、成本核算:把账算清楚
python
def cost_per_1k_requests(
prompt_tokens=1500, out_tokens=300,
price_in_per_m=2.0, price_out_per_m=8.0, # 元/百万 token(示意)
requests=1000):
in_cost = requests * prompt_tokens / 1e6 * price_in_per_m
out_cost = requests * out_tokens / 1e6 * price_out_per_m
return in_cost + out_cost
print(f"每千次成本 ≈ ¥{cost_per_1k_requests():.2f}")自建 vs API 的盈亏平衡点(粗略):
| 自建(单张 4090) | 云 API | |
|---|---|---|
| 固定成本 | 硬件或租卡(¥3–10/小时) | 0 |
| 边际成本 | 电费 + 运维 | 按 token |
| 盈亏平衡 | 日均请求 > 数千次时更划算 | 低频场景更划算 |
| 隐性成本 | 部署、监控、扩缩容、故障处理 | 无 |
别忽略隐性成本
自建的真实成本 = 卡钱 + 运维人力 + 出故障的夜里。大多数团队在请求量上来之前,用 API 更划算。
七、压测:拿数字说话
bash
# 用 vLLM 自带 benchmark
python -m vllm.entrypoints.cli.benchmark.serve \
--backend vllm --model Qwen/Qwen2.5-7B-Instruct \
--base-url http://localhost:8000 \
--dataset-name sharegpt --num-prompts 200 \
--request-rate 8必须记录的四个数:QPS、平均 TTFT、平均 TPOT、峰值显存。
然后调整并发再跑一遍 —— 你会看到吞吐上升但延迟变差,那个拐点就是你的容量上限。
八、常见陷阱
| 陷阱 | 后果 |
|---|---|
用 transformers 直接部署 | 无批处理,吞吐低 10 倍 |
max_tokens 不设上限 | 长尾请求拖垮并发 |
| 忽略输出长度分布 | 成本估算差 3 倍 |
| 量化后不重测 | 静默质量下降 |
| 未做超时与限流 | 一个坏请求堵住队列 |
| 全量日志 | 存储成本超过推理成本 |
通过标准
下一步
L4 完成。进入 L5 AI 应用工程。