Skip to content

推理优化:量化、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{精度字节}$$

变量含义
2K 和 V 各一份
L层数
T上下文长度
h×d注意力维度

这解释了两件事:为什么长上下文极耗显存;为什么并发数受显存限制而不是算力。

三、量化:最直接的省钱手段 ​

精度每参数字节7B 模型权重质量损失
FP32428 GB无
FP16/BF16214 GB几乎无
INT817 GB很小
INT4 (GPTQ/AWQ)0.53.5 GB小,可接受
2–3 bit0.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 8000
python
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 数
--quantizationawq / gptq / fp8按权重格式选
--enable-prefix-caching复用相同前缀的 KVRAG/固定 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 应用工程。

AI 学习路线 · 从入门到专家