模型推理与部署
模型推理与部署
模型训练完之后,剩下的问题全在推理:用户点击发送,到看见第一个字,中间发生了什么?为什么长文生成那么慢?为什么"1 万并发"那么贵?本文从机制讲起——两阶段推理、KV Cache、批处理、量化、MoE,最后落到产品视角的延迟-成本-吞吐三角。本文讲机制;怎么选供应商、怎么算账,分别见 LLM API 与供应商 与 LLM 成本测算。
推理的两阶段
大模型生成一段回答,内部经历两个计算特征完全不同的阶段:
预填充(Prefill)
- 一次并行处理完整输入(prompt),算出每个位置的隐藏状态,建立初始 KV Cache;
- 计算量大(输入越长越大),但并行度高,GPU 利用率好;
- 决定首 token 延迟(TTFT,Time To First Token)——用户"看到第一个字"要等多久。
解码(Decode)
- 逐 token生成:每步只处理一个新 token,串行进行;
- 每步矩阵很小但要读取全部历史 KV Cache 和模型权重,瓶颈常在显存带宽而非算力;
- 决定生成速率(TPOT,相邻 token 间隔)——用户"看到后面的字"多快。
两阶段的计算特征对比
| 阶段 | 并行度 | 计算特征 | 主要瓶颈 | 体验指标 |
|---|---|---|---|---|
| Prefill | 高(序列维度并行) | 大矩阵乘,随输入长度近似平方增长 | 计算量、长序列注意力 | TTFT |
| Decode | 低(时间上串行) | 每步小矩阵,读取权重 + 全部 KV Cache | 显存带宽、调度 | TPOT / TPS |
为什么解码慢
训练时 GPU 可以并行处理整段文本;推理时下一个 token 依赖前一个 token 的输出,时间上无法并行。单请求的生成延迟 ≈ 输出长度 × 每 token 耗时,所以"写 2000 字"天然比"写 50 字"慢一个量级——这不是网络问题,是自回归机制决定的。
工程应对
- Chunked Prefill:把超长输入切块处理,避免一个长 prompt 独占 GPU 太久、阻塞其他请求的 Decode;
- Prefill/Decode 分离部署:两种阶段的资源需求完全不同(一个要算力、一个要带宽),生产上可以拆成两套服务分别扩缩容;
- TTFT 与生成速率分开优化:TTFT 高 → 压 prompt 长度、上提示词缓存;生成慢 → 量化、批处理、投机解码。先定位是哪个指标差,再对症下药,不要"换更大的模型"一刀切。
从请求到响应:完整链路
一次 API 调用在推理服务内部经历的完整链路,按顺序排查问题就靠它:
1 2 3 4 | |
对应地,用户感知的延迟可以拆成四段:排队时间 + Prefill 时间(决定 TTFT)+ 生成时间(输出长度 × TPOT)+ 网络传输。产品侧排查延迟问题,先问"哪一段慢"——排队慢是容量问题(加卡/错峰),TTFT 慢是输入太长(压 prompt/缓存),生成慢是模型太大或带宽受限(量化/小模型),只盯着总延迟数字会误判。
KV Cache
缓存已算的 Key/Value
生成第 t 个 token 时,注意力机制需要让新 token 的 Query 与所有历史 token 的 Key/Value 做计算。历史 token 的 K/V 一旦算过就不会变——所以推理引擎把每层的 K/V 存下来(KV Cache),每步只算新 token 的 K/V 并追加,避免重复计算整个前缀。没有 KV Cache 的话,生成 1000 token 要重算约 50 万次前向,完全不可用。
为什么只缓存 K/V 不缓存 Q?因为历史的 Q 只服务于它自己当时的查询;未来步骤需要的是"历史位置可以被匹配的 K"和"可以被取回的 V",Q 用不上了。
显存估算
KV Cache 的显存 ≈ 2(K 和 V)× 层数 × KV 头数 × 单头维度 × 已缓存 token 数 × 每元素字节数。举个算例:32 层、8 个 KV 头、单头维度 128、BF16(2 字节)的模型,每个 token 的缓存 ≈ 32 × 2 × 8 × 128 × 2 ≈ 128 KiB:
- 单请求上下文 8K token → 约 1 GB;
- 并发 100 请求 → 约 100 GB——显存瞬间吃光。
为什么显存吃紧
KV Cache 是长上下文 × 大模型 × 高并发的三重乘积:
- 上下文 100 万 token 的模型,单条请求的 KV Cache 可达数 GB;
- 并发 100 个请求就要 100 份缓存;
- 权重占显存是固定的,KV Cache 却随请求动态增长——并发上限往往不是算力决定的,而是 KV Cache 显存决定的。
相关优化
| 优化 | 思路 | 效果 |
|---|---|---|
| MQA/GQA | 多个 Query 头共享同一组 K/V 头 | 缓存缩到原来的几分之一(架构层面) |
| PagedAttention | KV Cache 分页管理,按需分配 | 消除预留浪费与碎片,提并发(见「吞吐优化」) |
| KV Cache 量化 | 缓存用 INT8 存储 | 显存再砍一半,质量影响通常小 |
| 窗口/淘汰 | 只保留最近 N 个 token 的缓存 | 适合流式对话,放弃超远历史 |
| MLA | 把 K/V 压缩到低维潜空间再投影 | DeepSeek 系架构,缓存大幅缩水 |
采样与解码策略
大模型基础 讲过 temperature/top-p/top-k,这里补全解码侧的完整图景:
| 策略 | 机制 | 场景 |
|---|---|---|
| 贪心 | 每步取概率最高 token | 结构化抽取、要求可复现 |
| 温度采样 | 按缩放后的概率随机抽取 | 对话、创作(配合 top-p/top-k) |
| 束搜索(beam search) | 同时保留若干高分候选序列 | 翻译、摘要等追求整体序列质量 |
| 结构化约束解码 | 限制每步候选必须符合语法(如 JSON Schema) | 工具调用、结构化输出 |
停止条件
生成何时停?常见停止条件:生成结束符(EOS)、命中停止序列(stop strings)、达到 max_tokens 上限、结构化解码器确认语法已闭合、业务主动中止(超时、用户打断)。模型"说完一句话"不会天然停止——话痨、截断、停不下来这些问题,一半是停止条件没设好。
参数推荐与常见坑
- 默认值只是起点:temperature 0.7 + top_p 0.9 是通用起点,事实类任务直接降到 0-0.3,不要怕"单调";
- 重复惩罚别乱加:frequency penalty 对"车轱辘话"有效,但加多了会让模型回避常用词,输出变得怪异;
- max_tokens 设大不如设对:按业务真实输出长度分布设上限,防止话痨烧钱,也防止长回答被硬截断;
- 种子 ≠ 完全可复现:固定 seed 减少随机性,但并行服务与浮点实现的差异仍可能造成不一致,别把它当测试铁律;
- 结构化任务用约束解码:需要合法 JSON/SQL/函数参数时,用结构化约束解码而不是祈祷模型自觉。
结构化约束解码
结构化约束解码(constrained decoding)值得单独强调:它不是"提示词要求输出 JSON"(那只是请求,模型可能不听话),而是在解码每一步时屏蔽不符合语法的 token,从机制上保证输出一定合法。工具调用、Agent 流程、数据管线里这是标配,能消灭一整类"解析失败"的 bug(见 Agent 与工作流)。
吞吐优化
三种批处理方式
| 方式 | 组批时机 | 优点 | 局限 |
|---|---|---|---|
| 静态批处理 | 运行前固定 | 实现简单 | 被最长请求拖死,padding 浪费多 |
| 动态批处理 | 等短窗口凑一批 | 矩阵规模大 | 等待窗口增加排队延迟 |
| 连续批处理 | token 粒度动态调度 | 空槽即插即走,吞吐最高 | 状态与 KV Cache 管理复杂 |
早期推理是静态批处理:一批请求等最慢的生成完才整体退出,短的被长的拖死。连续批处理(Continuous Batching)在 token 粒度上调度:谁生成完了谁退出,新请求随时补进来,GPU 空槽被填满。这是 vLLM 等生产引擎吞吐碾压早期方案的核心原因。批越大吞吐越高,但单个请求的排队延迟(TPOT)会变差——引擎要在吞吐与延迟之间设调度阈值(如限制最大 batch token 数)。
PagedAttention
PagedAttention 解决的是 KV Cache 的显存管理问题(不是把注意力算得更好):借鉴操作系统虚拟内存,把 KV Cache 切成固定大小的"页",每个请求按需分配、物理上不要求连续,生成完了释放。收益:消除"按最大长度预留"的浪费与外部碎片,支持请求动态增长,显著提高单卡可容纳的并发数。FlashAttention 优化算子 IO、PagedAttention 优化缓存管理,两者可同时使用。
并发上限
并发能力由权重显存 + KV Cache 显存 + 批处理效率共同决定:单卡能同时服务多少请求,主要看 KV Cache 还剩多少显存。长上下文请求会剧烈压缩并发——这是"1M 上下文 API 很贵"的机制原因之一。
流式输出与体感
解码本来就是逐 token 的,服务端用 SSE(Server-Sent Events) 边生成边推送,用户首字等待 = TTFT 而非完整生成时间。对对话产品这是决定性的体验优化:长回答从"转圈 20 秒"变成"1 秒出字 + 逐字输出"。代价是内容安全审核要改为流式分段审核、中断续传要处理。
投机解码(Speculative Decoding)
投机解码的思路:让一个小模型先"打草稿"生成一串候选 token,大模型一次性并行校验——草稿对了就跳过计算,错了再从错处重来。数学上不改变输出分布(采样等价),实践中解码速度可提升约 2 倍(效果因模型与硬件而异)。生产引擎(vLLM/SGLang)已内置支持。
提示词缓存(Prompt Caching)
LLM 的输入里通常有大量重复前缀:系统提示词、知识库片段、多轮对话历史。提示词缓存把已计算过的前缀 K/V 存下来,相同前缀的请求直接复用,省掉重新 Prefill 的计算与时间。计费上命中缓存的部分按折扣价计费(各厂商折扣不同,缓存输入通常比原价低一个量级,以官方定价页为准)。产品侧的配合:把固定内容放在 prompt 前缀并保持字节级一致(别在开头插时间戳、随机串),持续吃折扣——这是成本优化的第一杠杆(见 LLM 成本测算)。
量化
量化:用更低精度的数字表示权重,换取显存下降与(部分场景)速度提升。
| 精度 | 每参数字节 | 相对 FP16 显存 | 质量影响 |
|---|---|---|---|
| FP16 / BF16 | 2 字节 | 1 倍(基线) | 无 |
| INT8 | 1 字节 | 约一半 | 通常可忽略,敏感任务需验证 |
| INT4 | 0.5 字节 | 约四分之一 | 小模型与数学/代码任务可能明显掉点 |
要点:
- 显存收益确定,速度收益看硬件:INT4 让 70B 模型从"要 140 GB"降到"约 35 GB",单卡可跑;但若硬件没有高效的低精度算子,量化后反而可能变慢(要反量化回高精度计算);
- 质量影响与模型大小反相关:模型越小、任务越敏感(数学、代码、长文档),量化掉点越明显——"能跑"不等于"能打";
- KV Cache 也可以量化(INT8 缓存),进一步省显存、提并发,与权重量化叠加;
- BF16 是训练与推理的现代基线:指数位多、数值稳定,与 FP16 同为 2 字节但不易溢出;
- 量化选型顺序:先 INT8 看质量,能接受再上 INT4;数学/代码/长文档场景务必用同一评测集做量化前后对比(评测方法见 评估与评测)。
量化格式与工具
实际部署中量化格式和工具链要匹配:GGUF 是 llama.cpp/Ollama 生态的格式(本地首选);AWQ、GPTQ 是 vLLM/SGLang 生产环境的常见格式(按模型与推理引擎的支持选);部分新引擎还支持 FP8(H 系列 GPU 原生加速)。注意:同一模型在不同格式下的质量与速度差异可能不小,别只看"多少 bit"就下结论——用你的任务实测。
稀疏化与 MoE
专家混合(MoE)
MoE(Mixture of Experts,专家混合) 把模型的 FFN 层替换成多个"专家"子网络,每次由路由网络(Router)为每个 token 挑选少量专家计算。代表:Mixtral 8x7B(总参数约 47B,每 token 激活约 13B)、Qwen3-MoE、DeepSeek V3。
激活参数 vs 总参数
- 总参数:所有专家 + 共享层,决定存储与显存;
- 激活参数:每个 token 实际经过的参数,决定单 token 算力与成本。
MoE 的价值是"用总参数买容量,用激活参数控制开销"——总参数 47B 的 Mixtral,推理成本接近 13B 的稠密模型,能力却强得多。比较模型时必须说明是总参数还是激活参数,否则"谁更大"没有意义。
对部署的意义
- 权重全部要存:MoE 模型总参数大,单卡放不下,需要多卡切分(专家并行),小显存设备跑不了;
- 低并发下优势不明显:请求太少时每个专家收到的 token 少,GPU 利用率低,稠密模型可能反而更稳;
- 路由带来跨卡通信(all-to-all)开销,部署比稠密模型复杂;
- 路由还可能负载不均:热门专家过载、冷门专家闲置,需要负载均衡策略(辅助损失、容量因子、共享专家)。
私有化部署
本地运行时与生产引擎
| 工具 | 定位 | 特点 |
|---|---|---|
| Ollama | 本地运行与管理(个人/原型) | 一条命令跑模型、HTTP API、跨平台,适合试用 |
| llama.cpp | 底层推理库(GGUF 格式) | 极省资源、CPU 也能跑,量化生态成熟 |
| vLLM | 生产级高吞吐引擎 | 连续批处理 + PagedAttention,OpenAI 兼容 API |
| SGLang | 生产级推理引擎 | 高吞吐、结构化生成优化,Agent/RL 场景友好 |
OpenAI 兼容 API 是事实标准:vLLM/SGLang 都提供 /v1/chat/completions 格式接口,业务代码可以"换底座不换代码"——先 Ollama 本地跑通原型,再 vLLM 上生产,这是开源模型落地的最短路径。
部署决策:API vs 本地
| 维度 | 云 API | 私有化部署 |
|---|---|---|
| 数据 | 数据出域(留存策略各异) | 数据不出域,合规可控 |
| 成本 | 按量付费,有缓存/错峰折扣 | 固定算力成本,规模化后边际成本低 |
| 能力 | 旗舰模型,持续更新 | 开源模型,自己跟进版本 |
| 运维 | 零运维 | 自担 GPU、监控、扩缩容、升级 |
GPU 显存估算公式
1 | |
权重是固定大头:7B 模型 FP16 约 14 GB,INT4 约 3.5 GB;70B 模型 FP16 约 140 GB,INT4 约 35 GB。
| 单卡显存 | 可跑规模(INT4 量级) | 典型卡 |
|---|---|---|
| 8 GB | 1-3B | RTX 3060/4060 |
| 16 GB | 7-8B | RTX 4080/4090 Laptop |
| 24 GB | 13-14B,或 32B 勉强 | RTX 4090 |
| 48 GB | 32B 舒适、70B 可跑 | A6000/L40S |
| 80 GB | 70B BF16 | A100/H100 |
上表为"权重放得下"的量级参考,不代表跑得快——要同时算 KV Cache 余量与解码带宽;具体以实测为准。开源权重各自的许可证也要查清楚(Qwen 系 Apache 2.0 可商用、Llama 系需 Meta 许可门控等),"能下载"不等于"能商用"。
私有化部署的典型演进路径
不追求一步到位,按业务阶段演进:
1 2 3 4 5 | |
关键决策点:接口抽象在第一天就做(OpenAI 兼容 API),否则后面每换一次引擎/模型都要改业务代码;量化与评测同步做,别先量化后才发现任务掉点。
规模化成本
1K 并发的量级推演:假设每请求平均 2000 输出 token、TPOT 20 ms(约 50 token/s/请求),需要服务端合计约 5 万 token/s 的输出吞吐;一张 H100 跑 7B 级模型的实测输出吞吐在万级 token/s,意味着需要数张到十余张卡(70B 级则要更多),再叠加冗余与弹性。真实数字受模型、量化、输入长度、批大小影响极大,上线前必须用真实流量压测。省钱思路:小模型路由扛常规量、量化 + 缓存 + 批处理压单价、错峰与批处理折扣(见 LLM API 与供应商 与 LLM 成本测算)。
压测口径
性能数字可比的前提是口径统一:硬件型号、模型与精度、输入长度分布、输出长度分布、并发数、流式与否、统计口径(平均还是 P95/P99)、冷启动还是稳态——脱离负载分布的"峰值 TPS"没有意义。报告性能时把这八项写清楚,否则数字不可比。
产品视角
延迟-成本-吞吐三角
推理侧存在一个不可能三角:延迟低、吞吐高、成本低,三者只能取其二。
- 要低延迟(对话、助手)→ 减少批大小、用更贵的大卡 → 单位成本上升;
- 要高吞吐(批处理、离线)→ 加大批、排队 → 延迟上升;
- 要低成本 → 小模型/量化/排队 → 能力或延迟让步。
产品经理的功课不是追求"每一项都最好",而是明确产品对三角的优先级:实时对话要延迟,离线分析要吞吐,创业期要成本。配套的监控指标:TTFT、TPOT、P95/P99 延迟、吞吐(token/s)、GPU 利用率、KV Cache 显存水位、单请求成本——上线后按月复盘,指标恶化先定位到阶段(排队?Prefill?Decode?显存?)再动刀。
常见故障排查表
| 症状 | 优先检查 | 常见根因 |
|---|---|---|
| 首 token 慢 | 排队、Prefill | prompt 过长、缓存未命中、并发超卖 |
| 首字快但后续慢 | Decode、KV 读取 | 批太大、带宽瓶颈、模型太大 |
| 吞吐低、GPU 空转 | 调度、批处理 | 静态批处理、小请求碎片、Tokenizer 在 CPU 上 |
| OOM 随并发增长 | KV Cache | 长上下文 + 高并发、未用 PagedAttention |
| 量化后变慢 | Kernel 支持 | 硬件无低精度算子、格式不匹配 |
| 输出乱码/角色串线 | Tokenizer、模板 | 模型与 tokenizer 不匹配、EOS 错误 |
| 结果变差 | 上下文、采样、模型 | 检索错误、温度过高、模型更新漂移 |
排查顺序口诀:先固定输入复现 → 区分质量问题还是性能问题 → 性能再拆排队/Prefill/Decode/显存 → 单请求基线 → 逐步加压 → 只改一个变量。
延迟预算分配:从 SLA 反推架构
产品要求"用户点击到首字 ≤ 1.5 秒,P95"。预算拆分:网络 200 ms + 排队 300 ms + Prefill 500 ms + Tokenizer 与调度 200 ms,还剩约 300 ms 余量。反推:输入 prompt 必须控制在 2K token 以内(否则 Prefill 超预算)→ 长文档走 RAG 摘要而非全文塞入 → 用提示词缓存把系统提示与历史前缀命中;并发峰值时排队超预算 → 加容量或降级到快模型。延迟是设计出来的,不是测出来的——从 SLA 反推每一段的预算,架构决策才有依据。
把"等模型"移出用户关键路径
模型生成是慢动作(秒级),用户等待是最贵的体验损耗。三个成熟手段:
- 异步化:生成不阻塞用户——任务进队列,完成后推送/轮询结果(如报告生成、批量分析),用户该干嘛干嘛;
- 预生成:高频问答、常见场景提前生成缓存结果,命中直接返回(应用层缓存,连模型都不用调);
- 分层:简单请求走快模型/低 effort,复杂请求才上旗舰/高 effort——把"重计算"留给真正需要的 10%(路由做法见 模型能力与选型)。
与相关页面的分工
| 页面 | 回答什么 |
|---|---|
| 本文(llm-inference) | 推理的机制:为什么慢、为什么贵、怎么优化 |
| LLM API 与供应商 | 怎么选:供应商、模型、API 参数、评测清单 |
| LLM 成本测算 | 怎么算账:单次成本、月预算、降本杠杆 |
| 模型训练与对齐 | 模型侧的另一半:训练、微调、对齐 |
来源说明
本文为原创整理,综合以下资料撰写,访问验证日期 2026-08-23;推理引擎与价格变动频繁,以官方页面为准。
- AIGC-Interview-Book「大模型基础(精华版)」:模型架构与工作原理(Prefill/Decode、KV Cache、批处理、量化、MoE 章节)(预取参考材料)
- vLLM:连续批处理、PagedAttention 与高吞吐推理
- SGLang:结构化生成与推理优化
- Ollama 与 llama.cpp:本地运行时
- DeepSeek-R1:MoE 与蒸馏部署案例
- 站内关联:大模型基础、模型训练与对齐、模型能力与选型、LLM API 与供应商、LLM 成本测算
发现错误?想一起完善? 在 GitHub 上编辑此页!
本页面贡献者:AI-PM Wiki Team
本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用