09. 推理与部署

返回目录

模型训练完之后的事:怎么把 token 一个个吐出来(解码策略),以及怎么让它吐得又快又省(推理系统优化)。

架构层面的 KV Cache、MHA/MQA/GQA 见 06. LLM 基础,这里讲它们在服务侧怎么被用起来。

1. Prefill 与 Decode:两个完全不同的阶段

自回归推理天然分成两段,它们的性格截然相反:

  • Prefill(预填充):把整个 prompt 一次性喂进去,并行算出所有位置的 K/V,最后产出第一个 token。几百上千个 token 同时参与矩阵乘法,GPU 算力被打满,属于计算密集(compute-bound)。
  • Decode(解码):之后每一步只输入一个新 token,算它的 \(Q_t\),与缓存里的全部 K/V 做注意力,吐出下一个 token。计算量只有一个 token,却要把整份模型权重和整份 KV Cache从显存搬进计算核心,属于访存密集(memory-bound),算力大量闲置。
阶段 一次处理的 token 数 瓶颈 对应指标
Prefill 整个 prompt 算力 FLOPs TTFT(首 token 延迟)
Decode 1 显存带宽 TPOT(每 token 延迟)、吞吐

这个分裂是后面几乎所有优化的根源:

  • decode 要搬的数据太多 → 想办法把数据变小:GQA/MQA、KV 量化、权重量化。
  • decode 的算力在闲置 → 想办法把闲置算力用起来:continuous batching、speculative decoding。
  • 两个阶段性格差太远 → 大规模服务干脆把它们拆到不同机器上跑(PD 分离)。

顺带澄清一个常见混淆:延迟和吞吐不是一回事,而且经常互换。批开得越大,吞吐越高,但排队和批内等待会让单个请求的延迟变差。选型时要先问清楚优化的是 TTFT、TPOT 还是总吞吐。


2. 解码策略

模型每一步输出的是词表上的一个概率分布,解码策略决定怎么从这个分布里挑出一个 token。

\[ y_t=\arg\max_y p(y\mid y_{<t},x) \]

每步都挑当前概率最大的那个。快、确定、可复现;缺点是每一步只顾眼前,容易陷进重复循环,开放生成时也偏平庸。

一个容易忽略的点:贪心不等于最优。整句概率最高的序列,未必每一步都取最大值——第一步让一点,后面可能赚回来更多。这正是 beam search 存在的理由。

适合抽取式任务、分类式输出、部分代码补全。

Temperature

在 softmax 之前先把 logits 除以一个温度 \(T\):

\[ p_i=\frac{\exp(z_i/T)}{\sum_j\exp(z_j/T)} \]

  • \(T<1\):分数差被放大,分布更尖,模型更保守(\(T\to0\) 就退化成 greedy)。
  • \(T=1\):原始分布。
  • \(T>1\):分数被压平,长尾 token 更容易被采到,更有创意,也更容易胡说。

注意它调的是分布形状,不是「随机不随机」这个开关——\(T=0.7\) 依然是随机采样,只是采到冷门 token 的机会小了。

Top-K Sampling

只保留概率最高的 \(K\) 个 token,重新归一化后采样。

  • 优点:直接砍掉长尾里的低质量 token。
  • 缺点:\(K\) 是固定的,不适应分布的尖锐程度。某一步前 5 个 token 已覆盖 99% 概率(此时 \(K=50\) 会放进 45 个垃圾),另一步前 5 个只覆盖 40%(此时 \(K=5\) 又砍掉了合理选项)。

Top-P / Nucleus Sampling

选取累计概率刚好达到 \(p\) 的最小 token 集合,再归一化采样。

  • 优点:候选集大小动态变化——分布尖的时候只留几个,分布平的时候自动放宽,正好补上 Top-K 的短板。
  • 缺点:仍有随机性,也不解决事实错误。

实践中 top-p(0.9 左右)配 temperature 是最常见的组合,两者叠加使用。

每一步保留累计 log probability 最高的 \(B\) 条候选序列,最后从中选一条。相比 greedy 更接近「整句概率最高」的序列。

  • 优点:翻译、语音识别这类目标相对确定的任务上常有效。
  • 缺点:成本随 beam size 线性增长;开放生成里偏好安全、短、重复的文本(高概率 ≠ 高质量);需要配长度归一化,否则天然偏好短句。
  • 现代 LLM 的对话/推理场景基本不用 beam,多样性和质量的性价比不如采样。

怎么选

场景 建议
数学、代码、抽取、评测 \(T=0\)(greedy),要可复现
翻译、摘要 beam,或低温采样
对话、创意写作 \(T\approx0.7\sim1.0\) 配 top-p \(\approx0.9\)
要多样候选再筛 高温采样 + Best-of-N / self-consistency

其他常见参数

  • repetition / frequency penalty:对已出现的 token 降权,抑制复读;调过头会破坏术语和格式的一致性(代码、专有名词尤其敏感)。
  • constrained decoding:用 grammar、JSON Schema、正则或状态机在解码时屏蔽非法 token,保证输出结构合法。比「求模型输出 JSON」可靠得多。
  • logit bias:直接对特定 token 加减分,用来强制或禁止某些词。

3. 用算力换正确率:Best-of-N 与 Self-Consistency

这两个都属于 test-time scaling:不改模型权重,只在推理时多花算力换正确率。

Best-of-N(Rejection Sampling)

采样 \(N\) 条候选回答,用一个打分器(reward model 或 verifier)挑出得分最高的一条。

  • 需要一个靠谱的打分器,效果上限由它决定。
  • \(N\) 越大收益越明显,但会开始钻打分器的漏洞——RM 分数继续涨而人工质量下降,就是过优化信号(见 08. 对齐与 RLHF)。
  • 成本随 \(N\) 线性增长。

Self-Consistency(多数投票)

针对有唯一答案的推理题:用较高温度采样 \(N\) 条不同的思维链,只对最终答案做多数投票,中间过程不管。

它成立的直觉是:错法千奇百怪,对法殊途同归——错误答案会散落在各处,正确答案会反复出现。

  • 不需要额外的 RM,这是它相对 Best-of-N 最大的优势。
  • 只适用于答案能归一化比较的任务(数值、选项、可执行代码),开放式写作用不了。
  • 温度不能太低,否则 \(N\) 条轨迹几乎一样,投票失去意义。

对比

方法 需要打分器 适用范围 主要风险
Best-of-N 需要 RM/verifier 开放生成、对齐 reward hacking
Self-Consistency 不需要 答案唯一的推理题 答案不可比就没法用

这两者和 RL 训练是一条线的两端:推理时多花算力,还是训练时把能力压进权重。实践中它们常被串起来用——先用 Best-of-N / self-consistency 造出高质量轨迹,再蒸馏回模型(见 07. 训练与系统),把「推理时的贵」变成「权重里的便宜」。


4. KV Cache 的显存账与优化

先算账

\[ \text{KV Cache}=2\times n_{\text{layers}}\times H_{KV}\times d_{\text{head}}\times L\times \text{bytes} \]

逐项读:开头的 2 是 K 和 V 各一份;每层都要存;\(H_{KV}\) 是 KV 头数(GQA 就是在这一项上省钱);\(L\) 是当前序列长度;bytes 是精度(fp16 = 2)。最后还要乘以并发请求数。

代入 Llama-3-8B(32 层、8 个 KV 头、\(d_{\text{head}}=128\)、fp16):

\[ 2\times32\times8\times128\times2\ \text{B}=128\ \text{KB / token} \]

8k 上下文就是 1 GB——而这只是一条请求。并发 32 条就要 32 GB,比模型权重本身(约 16 GB)还大。这就是为什么长上下文和高并发一起上就 OOM。

于是优化沿三条路走:

① 让每个 token 的 KV 更小 —— MQA/GQA/MLA,直接砍 \(H_{KV}\)(见 06. LLM 基础)。

② 降精度 —— 把 KV Cache 量化到 int8/fp8,显存和带宽同时减半。KV 对量化比权重更敏感一些,通常保留 head 维度上的分组 scale。

③ 别浪费已分配的空间 —— PagedAttention。

PagedAttention

问题出在内存碎片:传统实现要为每条请求预留一整块连续显存,而请求最终生成多长事先不知道,只能按最大长度(比如 4096)预留。实际只生成了 200 个 token,剩下的全浪费。实测有效利用率经常只有 20–40%。

做法直接借了操作系统虚拟内存的思路:把 KV Cache 切成固定大小的 block(比如 16 个 token 一块),用一张 block table 把逻辑位置映射到物理块。物理上不必连续,用多少分多少。

额外的好处比省碎片更值钱:同一个 prompt 的多条采样(Best-of-N、beam、多轮对话共享的系统提示)可以共享前缀的物理块,写时复制。这也是 prefix caching 的基础——相同前缀只 prefill 一次。


5. Continuous Batching

静态批处理的毛病:一批请求要等最长的那条生成完才整批返回,先结束的槽位一直空转。而 LLM 请求的输出长度差异极大——10 个 token 的「好的」和 2000 个 token 的长文混在一批里,前者要陪跑到底。

Continuous batching(也叫 in-flight batching):以迭代为调度粒度,任何一条请求生成完就立刻退出,把槽位让给排队中的新请求,不必等整批对齐。

它为什么这么有效,回到第 1 节的结论:decode 阶段是访存密集、算力闲置的。多塞几条序列进来,权重只需要搬一次却服务了更多请求,几乎是白赚的吞吐,通常有数倍提升。

代价和注意事项:

  • 批越大,KV Cache 压力越大。吞吐的天花板最终由显存决定,要和 PagedAttention 一起看。
  • prefill 和 decode 混在同一批里会互相拖累:一条长 prompt 的 prefill 会卡住其他请求的 decode,表现为 TTFT 和 TPOT 抖动。缓解办法是 chunked prefill(把长 prefill 切块,插进 decode 的空隙)或PD 分离(prefill 和 decode 跑在不同实例上)。

6. Speculative Decoding

思路:decode 阶段算力大量闲置,那就让一个小模型先猜,大模型批量验。

流程三步:

  1. 草稿:用一个便宜的 draft 模型(或 n-gram、模型自身的浅层)一口气猜出 \(k\) 个 token。
  2. 验证:把这 \(k\) 个 token 拼成序列送进大模型,一次前向同时算出这 \(k+1\) 个位置的分布。注意这是 prefill 式的并行计算,耗时和只算 1 个 token 几乎一样——因为瓶颈本来就在搬权重,不在算。
  3. 接受/回退:从头逐个比对,接受与大模型分布一致的前缀;第一个不一致的位置用大模型的分布重采一个,后面的草稿全丢掉。

最关键的一点:经过拒绝采样的修正,输出分布与直接用大模型采样完全一致。它是无损加速,不是近似。

  • 收益取决于接受率:draft 和目标模型越像、内容越「好猜」(代码、格式化文本、有大量复制的改写任务),加速越明显,通常 2–3×。
  • 代价是要多维护一个 draft 模型,且 batch 很大时算力已经打满,收益会缩水——它花的正是那部分闲置算力。
  • 常见变体:Medusa(给模型加几个头,一次预测多个后续位置,省掉独立 draft 模型)、EAGLE(在特征层而非 token 层做草稿,接受率更高)、prompt lookup / n-gram(直接从上下文里抄一段当草稿,摘要、改写、RAG 场景特别有效,零额外模型)。

7. Quantization

把权重(有时还有激活和 KV Cache)从 fp16 降到 int8/int4。目的有两个:省显存,以及——因为 decode 是访存密集的——要搬的字节少了就是快。

常见路线

  • PTQ(Post-Training Quantization):拿训练好的模型直接量化,只需少量校准数据,几分钟到几小时搞定。GPTQ、AWQ 属于这一类,是部署主流。
  • QAT(Quantization-Aware Training):训练时就模拟量化误差,质量更好,但要重训,成本高,通常只在小模型或极低比特时才值得。

按量化对象还分:W4A16 / W8A16(只量化权重,激活保持高精度)最常见,直接治 decode 的带宽瓶颈;W8A8 / FP8(权重激活都量化)在支持的新硬件上还能同时吃到算力红利,对 prefill 也有效。

为什么压到 4 bit 还能用?

关键在离群值(outlier):LLM 的激活里,少数几个维度的数值远大于其他维度。如果按整体最大最小值统一量化,绝大多数正常值会被挤进极少数几个格子里,信息全丢。

所以现代方法都在处理这件事:

  • 分组量化:每 64/128 个权重共享一组 scale,而不是整层一个。
  • 离群通道保高精度:LLM.int8() 把少数异常通道单独用 fp16 算。
  • 按重要性调缩放:AWQ 观察到「重要权重」由激活分布决定,量化前先按激活尺度缩放,保护那部分权重。

代价

  • 质量下降先暴露在长尾上:平均 benchmark 可能看不出差别,但长上下文、多步数学推理、罕见语言会先掉。评估不能只看一个平均分。
  • int4 以下(2–3 bit)目前一般需要额外训练或特殊结构才可用。
  • 量化格式必须有配套的高效 kernel,否则显存省下来了、速度反而更慢——量化只有在 kernel 支持时才是加速。

8. 一张表串起来

每个优化手段治的都是第 1 节里那两个瓶颈之一:

手段 治的是什么
GQA / MQA / MLA decode 的带宽(KV 搬太多)
KV 量化、PagedAttention 显存容量与碎片
权重量化(W4A16) 权重搬运带宽 + 显存
Continuous batching decode 的算力闲置
Speculative decoding decode 的算力闲置
Chunked prefill / PD 分离 prefill 与 decode 互相干扰
FlashAttention prefill 的 \(O(n^2)\) 显存与访存
Best-of-N / self-consistency 反过来——主动多花算力换正确率

目录

章节
00. 机器学习核心概念
01. 基础与神经网络机制
02. 模型评估与指标
04. 经典机器学习
05. NLP、RNN 与词向量
06. LLM 基础
07. 训练与系统
08. 对齐与 RLHF
09. 推理与部署
11. ML Coding 基础
12. ML Coding 专题
参考资料