外观
部署与服务化
部署的目标不是"把模型跑起来",而是用可接受的成本,把模型以可预测的延迟和吞吐稳定地提供给用户——它是一场关于显存、算力、并发与成本的工程权衡。
模型在笔记本上跑通只是开始。生产环境要回答的问题完全不同:显存够吗?并发 100 时延迟多少?租什么 GPU 最划算?量化会不会掉分? 本文给出完整的部署链路:先定指标,再算显存,然后选量化与批处理策略,最后落到 vLLM 的可运行示例。底层机制(自回归循环、KV cache、采样)见推理基础。
一、先定指标:部署语言是数字
部署团队必须用同一套指标说话:
| 指标 | 全称/含义 | 衡量什么 | 典型量级(参考) |
|---|---|---|---|
| TTFT | Time To First Token,首 token 延迟 | 用户"等多久开始有反应" | 数百 ms~秒级 |
| TPOT | Time Per Output Token,每输出 token 时间 | 生成速度 | 数十 ms~百 ms/token |
| 吞吐 | tokens/s,单位时间输出量 | 服务整体产能 | 单卡数百~数千 token/s |
| 并发 | 同时服务的请求数 | 容量 | 与显存/批处理强相关 |
text
用户体验的换算:TTFT < 1s 属"可接受",TPOT 越快"打字感"越强。
吞吐 × 单请求 token 数 ÷ 并发 = 你需要的 GPU 数量(粗略)。延迟 vs 吞吐的取舍
延迟优先(交互式对话)与吞吐优先(离线批量)是两种完全不同的调优方向:前者减小 batch 保速度,后者加大 batch 拉满吞吐。先明确业务属于哪一种,再调参数。
指标的量级参考与目标设定
指标没有绝对的"标准值",但可以按业务形态给出参考区间(具体以实测和产品要求为准):
| 业务形态 | TTFT 目标 | TPOT 目标 | 主要约束 |
|---|---|---|---|
| 实时对话(Chat) | < 1s | 越快越好(打字感) | 延迟优先 |
| 客服/工单辅助 | < 2s | < 100ms/token | 延迟 + 质量 |
| 代码补全 | < 500ms | < 50ms/token | 延迟极度敏感 |
| 离线批处理(摘要/抽取) | 不敏感 | 不敏感 | 吞吐 + 成本优先 |
目标设定的原则:先测出当前基线(P50 与 P95),再定"可接受的 P95"而非"平均"——P95 才是用户体验的真实感受,平均值会被少数超快请求拉低。
二、显存估算:权重 + KV cache + 激活
推理显存由三部分组成,先学会手算,再信任何监控工具:
总显存 ≈ 权重显存 + KV cache 显存 + 激活/临时显存1. 权重显存
权重显存 = 参数量 × 每参数字节数
fp16/bf16: 2 字节/参数 INT8: 1 字节/参数 INT4: 0.5 字节/参数| 模型 | 参数量 | FP16 | INT8 | INT4 |
|---|---|---|---|---|
| 7B/8B | 约 8B | 约 16GB | 约 8GB | 约 4GB |
| 13B | 约 13B | 约 26GB | 约 13GB | 约 6.5GB |
| 70B | 约 70B | 约 140GB | 约 70GB | 约 35GB |
2. KV cache 显存
KV cache 存放每个序列在每层产生的 Key/Value(机制见 Transformer 架构详解),随并发请求数 × 上下文长度线性增长:
每 token KV 显存 = 2 × 层数 × KV 头数 × 头维度 × 字节数
KV cache 总量 = 每 token 显存 × 上下文长度 × 并发数以 Llama-2 70B(80 层、8 个 KV 头、头维度 128、fp16)为例:
每 token = 2 × 80 × 8 × 128 × 2 字节 = 327,680 字节 ≈ 0.31 MB/token
4096 上下文 × 32 并发 ≈ 0.31MB × 4096 × 32 ≈ 40GB !这组数字揭示了部署的第一定律:KV cache 的增速远超权重,长上下文 + 高并发时它才是显存大头(这也是 MoE 稀疏专家模型 与量化都缓解不了的部分)。KV cache 的工程优化见第四节。
3. 激活显存
预填充阶段每一层的中间激活也占显存,与 batch 大小、序列长度成正比,通常用 FlashAttention 等算子大幅压缩。估算时把"权重 + KV cache"的结果乘以 1.1~1.3 留出激活余量即可。
推理显存与训练显存的区别
训练显存还要加上梯度与优化器状态(全参训练约 12~16 字节/参数),是推理的 4~8 倍。"70B 模型 140GB 显存"是推理口径,训练另算。 训练侧估算见微调实战的 QLoRA 表。
三、量化:用精度换显存与速度
量化把权重从 fp16 压到低比特,是"小卡跑大模型"与"降本"的主要手段。
| 方法 | 精度 | 特点 | 典型工具 |
|---|---|---|---|
| 动态量化(INT8) | 8-bit | 通用、简单、几乎不掉分 | vLLM 内置、bitsandbytes |
| GPTQ | 4-bit | 训练后量化,激活时反量化;省显存显著 | auto-gptq、vLLM 支持 |
| AWQ | 4-bit | 按激活分布感知的量化,精度损失更小 | vLLM 支持、autoawq |
| GGUF(K-quant) | 1~8-bit | llama.cpp 生态,CPU/边缘可跑 | llama.cpp、Ollama |
| FP8 | 8-bit | 新一代 GPU(H 系列)原生支持 | TensorRT-LLM、vLLM |
bash
# 用 vLLM 直接加载量化模型(AWQ 与 GPTQ 均支持)
pip install vllm
# 加载 4-bit AWQ 版 Qwen2.5-7B
python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen2.5-7B-Instruct-AWQ \
--quantization awq \
--gpu-memory-utilization 0.9 \
--max-model-len 8192量化不是免费的
4-bit 量化通常带来 1~3% 的基准分下降(数值随任务与模型而变),但推理速度与显存收益巨大。决策顺序:先跑 fp16 基准,再跑 INT8/AWQ 对比,用评估实战的方法确认掉分可接受后再上线。
四、批处理与 KV cache 优化
1. Continuous batching:吞吐的最大杠杆
传统批处理是"静态批量"(一批全部完成后才开始下一批),导致短请求等长请求。Continuous batching(连续批处理)让序列在token 级进出 batch——完成一个就立即腾出位置给新请求,GPU 利用率显著提升。
2. PagedAttention:KV cache 的显存管理
vLLM 的核心创新 PagedAttention 把 KV cache 切成"页"管理(类似操作系统的虚拟内存),消除碎片、按需分配,在相同显存下吞吐可提升 2~4 倍(官方基准,具体数值随负载而变,见参考资料)。
3. 其他优化项速览
| 优化 | 作用 |
|---|---|
| FlashAttention | 融合算子,省显存 + 提速 |
| 前缀缓存(prefix caching) | 相同 system prompt 复用 KV cache |
| 推测解码(speculative decoding) | 小模型草稿 + 大模型验证,降低 TPOT |
| 结构化输出约束解码 | 生成 JSON 时跳过非法 token,省时稳格式 |
4. 多卡部署:张量并行
单个模型放不下一张卡时,用**张量并行(Tensor Parallelism)**把一层拆到多卡,卡间通过高速互联(NVLink/InfiniBand)同步。要点:
| 并行方式 | 拆分对象 | 何时用 |
|---|---|---|
| 张量并行 | 单层内的矩阵/注意力头拆到多卡 | 单卡放不下权重;对卡间带宽敏感 |
| 流水线并行 | 按层分组到不同卡 | 层数很多;吞吐略降 |
| 数据并行 | 每卡一份模型、分数据 | 吞吐扩展(配合 TP 使用) |
vLLM 用 --tensor-parallel-size 2 一行启用 TP;注意 TP 需要卡间高速互联,否则通信会拖垮性能。70B fp16 用 2×80GB 或 4×40GB 是常见组合。
五、GPU 选型
| GPU | 显存 | 参考定位(以 2024-2025 主流市场为基准,价格随时波动) |
|---|---|---|
| RTX 4090 | 24GB | 消费级:7B 全精度 / 13B INT4 单卡 |
| L40S | 48GB | 单卡跑 13B fp16 / 70B INT4 |
| A100 40/80GB | 40/80GB | 数据中心通用主力:70B INT4(80GB) |
| H100 80GB | 80GB | 大模型训练/推理旗舰,FP8 原生 |
| H20 / 国产卡 | 各异 | 受限环境的选择,注意生态兼容 |
选型决策表(怎么选 GPU):
| 你的模型 + 精度 | 最低单卡要求 | 备注 |
|---|---|---|
| 7B INT4 | 8~16GB 即可 | 边缘/低预算 |
| 7B fp16 | 24GB | RTX 4090 级别 |
| 13B fp16 / 70B INT4 | 40~48GB | L40S / A100 |
| 70B fp16 | 80GB × 2(张量并行) | H100/A100 |
| 并发很大 | 显存按"KV cache 公式"再放大 | 别只看权重 |
显存够不够的快速判据
权重显存 + KV cache(按你的并发与上下文)≤ 单卡显存 × gpu-memory-utilization。超了就先量化,再不够就上多卡张量并行或降并发。永远把 KV cache 算进去——只算权重是部署事故的头号来源。
六、vLLM 部署示例:端到端
vLLM 提供 OpenAI 兼容的 API,前端代码一行不用改。
bash
# ① 启动服务(以 7B 模型为例)
python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen2.5-7B-Instruct \
--served-model-name my-llm \
--gpu-memory-utilization 0.9 \
--max-model-len 8192 \
--enforce-eager # 兼容性差的环境可关 CUDA graph
# ② 用 OpenAI SDK 调用(客户端无感)python
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY")
resp = client.chat.completions.create(
model="my-llm",
messages=[{"role": "user", "content": "什么是 KV cache?"}],
temperature=0.7,
max_tokens=512,
stream=True, # 流式输出
)
for chunk in resp:
print(chunk.choices[0].delta.content or "", end="")bash
# ③ 监控与压测
curl http://localhost:8000/v1/models # 查看模型列表
# 压测工具:使用 openai-benchmark / h2o-llmstudio 或自写并发脚本
# 关键监控指标:TTFT 分位数、TPOT 分位数、GPU 利用率、显存使用率、队列积压OpenAI 兼容 API:协议细节
vLLM 提供的 /v1/chat/completions 兼容 OpenAI 协议,四个容易踩的细节:
| 细节 | 说明 |
|---|---|
| 流式(stream) | stream=true 返回 data: 分片(SSE),以 data: [DONE] 结束 |
| 工具调用 | tools 参数与 tool_calls 返回字段需客户端支持 |
| 结构化输出 | JSON mode / response_format 的支持因引擎与模型而异,需先验证 |
| 鉴权与暴露 | 自建服务的鉴权(API key)要自己加,别裸奔公网 |
python
# 流式响应的 SSE 处理(客户端侧)
import requests
resp = requests.post(
"http://localhost:8000/v1/chat/completions",
json={"model": "my-llm",
"messages": [{"role": "user", "content": "你好"}],
"stream": True},
headers={"Authorization": "Bearer EMPTY"},
stream=True,
)
for line in resp.iter_lines():
if line and line.startswith(b"data: "):
payload = line[6:]
if payload == b"[DONE]":
break
# 解析 JSON,取 choices[0].delta.content 逐片打印兼容 ≠ 功能全等
"OpenAI 兼容"是协议兼容,不代表功能等价:不同引擎对 tools、response_format、logprobs 等扩展字段的支持程度不同。上线前把你要用的每个字段都实测一遍,而不是默认可用。
部署清单(上线前逐项核对)
| 项 | 检查 |
|---|---|
| 显存预算 | 权重 + KV cache(峰值并发 × 峰值上下文)≤ 显存 |
| 量化验证 | INT4 相对 fp16 的基准差已评估 |
| 指标达标 | TTFT/TPOT 满足产品要求(含 P95) |
| 兼容性 | OpenAI 兼容 API 已联调(流式/工具调用/JSON mode) |
| 监控 | 延迟、吞吐、显存、错误率、队列积压全接入告警 |
| 回滚 | 模型/配置版本化,可一键回滚 |
七、灰度与监控
上线不是终点,是新一轮工程的开始:
| 手段 | 做法 |
|---|---|
| 灰度 | 新模型只服务 5% 流量,观察指标与用户反馈再放量 |
| 影子模式 | 新老模型并行跑,新模型结果只记录不展示,离线对比 |
| 监控 | TTFT/TPOT 的 P50/P95、错误率、队列长度、GPU 利用率 |
| 评估联动 | 线上抽检结果回流到 golden set,形成闭环(见评估实战) |
部署事故高发区
显存 OOM(只算了权重没算 KV cache)、无限重试导致的雪崩(设置超时与退避)、流式中断无重连、模型版本回滚困难。把"如何优雅失败"写进设计,而不是等它发生。
监控指标体系:上线后盯什么
| 类别 | 指标 | 告警线(示例) |
|---|---|---|
| 延迟 | TTFT / TPOT 的 P50、P95 | P95 TTFT 超 2s |
| 容量 | 并发数、队列积压 | 队列持续积压 |
| 算力 | GPU 利用率、显存使用率 | 显存使用率 > 95% |
| 质量 | 错误率、重试率、超时率 | 错误率 > 1% |
| 业务 | 完成率、用户负面反馈率 | 负面反馈率上升 |
监控数据的价值在趋势:显存使用率从 60% 缓慢爬到 90%,说明有流量增长或内存泄漏,比"今天崩了"更值得先处理。
八、云 vs 本地 vs API:三个选项的决策
| 维度 | 自建 GPU(云/本地) | 托管 API(OpenAI/Anthropic 等) | 自托管开源模型 |
|---|---|---|---|
| 成本曲线 | 固定成本高、边际成本低 | 按量付费、无沉没成本 | 同左,但需运维 |
| 数据安全 | 数据出网可控 | 数据出网(需评估合规) | 数据完全在内 |
| 能力上限 | 开源模型为准 | 闭源旗舰能力最强 | 开源模型为准 |
| 运维负担 | 高(K8s、GPU、弹性) | 零 | 中高 |
| 定制 | 完全可控 | 有限(微调需平台支持) | 完全可控 |
决策顺序建议:
数据能出网、要最强能力、不想运维 → 托管 API
数据敏感/合规限制/深度定制 → 自托管开源模型(vLLM)
量很大且稳定 → 自建 GPU(或混合:API 兜底 + 自建主路)成本算账:一个简化示例
选型前先算一笔账(数字仅为教学示例,价格随时变化,以官方报价为准):
场景:7B 模型,日均 10 万次请求,平均输入 1000 token、输出 300 token
每日 token 数 ≈ 10万 × (1000 + 300) ≈ 1.3 亿 token
方案 A:托管 API(假设 0.3 元/百万输入 token、1.2 元/百万输出 token)
成本 ≈ 10万×(1000×0.3 + 300×1.2) / 100万 ≈ 6.6 元/天 ≈ 2400 元/年
方案 B:自建 1×RTX 4090(约 24GB,可跑 7B INT4)
一次性硬件约 1.5 万元;电费/运维另计;容量上限约几万~几十万 token/分钟结论模式:量小用 API(零运维),量大且稳定自建(摊薄固定成本),中间地带用混合。同时别忘了把开发/运维人力成本算进去——那往往是最贵的部分。
延伸阅读
- 推理基础:自回归与采样 —— KV cache、预填充/解码阶段的底层原理
- Transformer 架构详解 —— KV cache 从哪来:注意力机制回顾
- 上下文与长文本 —— 长上下文对 KV cache 与显存的影响
- 框架与工具选型 —— vLLM/SGLang/TensorRT-LLM 的选型表
- MoE 稀疏专家模型 —— MoE 模型的显存与专家缓存挑战
- 评估实战 —— 量化/新版本上线的回归验证
- 常见陷阱与反模式 —— 忽略成本、上下文塞爆等部署相关陷阱
参考资料
- vLLM:Easy, Fast, and Cheap LLM Serving with PagedAttention(arXiv:2309.06180) —— PagedAttention 与 vLLM 系统论文
- vLLM 官方文档 —— 部署、量化、LoRA、OpenAI 兼容 API 的官方手册
- vLLM 官方博客:Continuous Batching —— continuous batching 与吞吐基准的官方说明
- Anyscale:Continuous Batching for LLM Inference —— 连续批处理原理与数据
- GPTQ: Accurate Post-Training Quantization(arXiv:2210.17323) —— GPTQ 原始论文
- AWQ: Activation-aware Weight Quantization(arXiv:2306.00978) —— AWQ 原始论文
- llama.cpp(GitHub) —— GGUF 量化与 CPU 推理的官方仓库
- Ollama 官网 —— 本地一键运行 GGUF 模型
- OpenAI API Reference —— OpenAI 兼容 API 的协议参考
- FlashAttention(arXiv:2205.14135) —— 融合注意力算子原始论文