跳到主要内容

第18章:推理基础

训练一个大模型需要耗费大量算力,但训练完成之后,真正面向用户的环节是推理(Inference)——将一段输入文本转化为输出文本的过程。推理看起来比训练简单:没有梯度,没有参数更新,只有一次次前向传播。然而,当你真正需要在生产环境中为数千名用户实时提供服务时,你会发现推理的性能瓶颈远比想象中复杂。

本章从自回归解码的本质出发,分析 Prefill 与 Decode 两个阶段的计算特点,再系统介绍各种解码策略及其适用场景。


18.1 自回归解码的计算特点

逐 Token 生成的本质

GPT 系列模型生成文本的方式称为自回归解码(Autoregressive Decoding)。每一步,模型接收到目前为止生成的所有 token,预测下一个 token 的概率分布,从中选取(或采样)一个 token,然后将它追加到序列中,重复此过程,直到生成终止符或达到最大长度。

用公式表示,给定输入 x1,x2,,xnx_1, x_2, \dots, x_n,模型依次生成:

yt=argmaxvP(vx1,,xn,y1,,yt1)y_t = \arg\max_v P(v \mid x_1, \dots, x_n, y_1, \dots, y_{t-1})

关键约束yty_t 的生成依赖于 yt1y_{t-1},而 yt1y_{t-1} 依赖于 yt2y_{t-2},……这是一条严格的依赖链。每个 token 必须等上一个 token 生成完毕才能开始计算,无法并行

与训练时不同——训练时整个目标序列已知,可以用 Causal Mask 在一次前向传播中并行计算所有位置的损失——推理时只能一步一步地走。

每一步都是一次完整的前向传播

生成每个 token,模型都要执行一次完整的前向传播:

  1. 将当前序列的所有 token 经过 Embedding 层转化为向量
  2. 通过 LL 层 Transformer Block(每层包含 Multi-Head Attention + FFN)
  3. 经过 LM Head 输出词表大小的 logits,再 softmax 得到概率分布

对于一个拥有 70B 参数的模型,每次前向传播涉及的矩阵乘法量极为庞大。生成 NN 个 token 就意味着 NN 次这样的前向传播。

计算量分析

粗略地,一次前向传播的浮点运算量(FLOPs)约为:

FLOPs per token2P\text{FLOPs per token} \approx 2P

其中 PP 是模型参数量(每个参数参与一次乘加运算,乘加算两次浮点运算)。对于 GPT-3(P=175BP = 175 \text{B}),生成一个 token 约需 350 GFLOPs;生成 1000 个 token 就需要约 350 TFLOPs。

然而,FLOPs 并非唯一瓶颈

访存瓶颈:Memory-Bound

硬件性能受两个指标约束:

  • 计算吞吐量(Compute Throughput):单位时间能执行多少 FLOPs,例如 A100 的 BF16 峰值约 312 TFLOPS
  • 内存带宽(Memory Bandwidth):单位时间能从显存读写多少数据,例如 A100 的 HBM 带宽约 2 TB/s

对于大批量训练,矩阵乘法的 arithmetic intensity(计算量/访存量 之比)很高,GPU 的计算核心可以持续喂饱,属于计算密集型(Compute-Bound)

但在推理的 Decode 阶段,每次前向传播只生成一个 token(batch size = 1 时),需要将模型所有 PP 个参数从显存读出来,完成极少量的计算。Arithmetic intensity 极低,GPU 的计算核心大部分时间都在等数据,属于访存密集型(Memory-Bound)

:::info 直觉类比 想象一个工厂(GPU 计算单元),原材料(模型参数)存放在远处的仓库(HBM 显存)。工厂加工速度极快,但每次只接到一个小订单(一个 token),搬运原材料的时间远超加工时间——工厂大部分时间都在等货,而不是在生产。 :::

这个特性深刻影响了推理系统的设计方向:KV Cache、连续批处理(Continuous Batching)、量化压缩,都是为了在访存瓶颈下压榨性能而发展出来的技术。


18.2 Prefill 与 Decode 阶段分析

一次完整的 LLM 推理请求由两个性质截然不同的阶段组成。

Prefill(预填充)阶段

当用户发送一段 prompt,模型首先需要处理整个输入序列,计算每个 token 的注意力,并将中间的 Key-Value 向量缓存起来(即 KV Cache)。这个过程称为 Prefill

Prefill 的特点:

  • 输入序列中所有 token 的计算可以并行(使用 Causal Mask 即可)
  • 一次 Prefill 涉及大量的矩阵乘法,arithmetic intensity 高
  • 属于计算密集型(Compute-Bound)
  • GPU 利用率高,能充分发挥算力

假设 prompt 长度为 LpL_p,Prefill 的计算量约为:

FLOPsprefill2PLp+4nheadsdheadLp2L\text{FLOPs}_{\text{prefill}} \approx 2P \cdot L_p + 4 \cdot n_{\text{heads}} \cdot d_{\text{head}} \cdot L_p^2 \cdot L

后一项来自 Attention 的 O(L2)O(L^2) 复杂度,在长 prompt 时不可忽视。

Decode(解码)阶段

Prefill 完成后,模型进入逐 token 生成的 Decode 阶段。此时每步只生成一个 token,但需要访问 KV Cache 中所有已生成 token 的 Key 和 Value 向量。

Decode 的特点:

  • 串行,不可并行
  • 每步需要从显存读取整个 KV Cache
  • Arithmetic intensity 低,属于访存密集型(Memory-Bound)
  • GPU 利用率低,大量计算核心闲置

两阶段性能对比

维度PrefillDecode
并行性高(序列内并行)无(严格串行)
计算特点Compute-BoundMemory-Bound
GPU 利用率
主要瓶颈计算吞吐显存带宽
对 Batch Size 的敏感性中等高(增大 batch 可提升利用率)

关键指标:TTFT 与 TBT

用户体验上,我们用两个指标衡量推理延迟:

  • TTFT(Time To First Token,首 token 延迟):从用户发送请求到收到第一个 token 的时间。主要由 Prefill 阶段决定。Prefill 越慢,用户等待越久才看到响应开始出现。

  • TBT(Time Between Tokens,token 间隔时间),也称为 TPOT(Time Per Output Token):相邻两个输出 token 之间的时间间隔。主要由 Decode 阶段决定。TBT 决定了文字"流出"的速度是否流畅。

:::tip 优化取舍 优化 TTFT 意味着加速 Prefill(可以用更高的计算吞吐,或者减少 prompt 长度);优化 TBT 意味着加速 Decode(需要更高的显存带宽,或者减少每步需要读取的数据量)。两者的优化方向有时存在冲突,推理系统需要在二者间做权衡。 :::


18.3 解码策略

推理的最后一步是:拿到模型输出的 logits 向量(维度等于词表大小),决定选哪个 token。这个决策过程就是解码策略(Decoding Strategy),它在很大程度上影响生成文本的质量、多样性和创造性。

Greedy Decoding(贪心解码)

最简单的策略:每步选概率最高的 token。

yt=argmaxvP(vcontext)y_t = \arg\max_{v} P(v \mid \text{context})
  • 优点:确定性,速度快,实现简单
  • 缺点:容易陷入重复,缺乏多样性;局部最优不等于全局最优

:::warning 贪心的陷阱 考虑句子"今天天气很……"。贪心可能选"好",然后是"。",序列终止。但实际上"好,我们去……"可能是更有信息量的延续,只是第一步的概率稍低。贪心策略永远放弃了这种可能性。 :::

Beam Search(束搜索)

Beam Search 维护 KK 条(称为 beam width 或 beam size)最优路径,每步对每条路径扩展所有可能 token,保留得分最高的 KK 条,最终输出分数最高的序列。

路径得分通常是对数概率之和:

score(y1,,yt)=i=1tlogP(yiy<i,x)\text{score}(y_1, \dots, y_t) = \sum_{i=1}^{t} \log P(y_i \mid y_{<i}, x)

为避免偏向短序列,常对长度做归一化:

scorenorm=1tαi=1tlogP(yiy<i,x)\text{score}_{\text{norm}} = \frac{1}{t^\alpha} \sum_{i=1}^{t} \log P(y_i \mid y_{<i}, x)

其中 α[0.6,0.7]\alpha \in [0.6, 0.7] 是常用值。

  • 优点:比 greedy 更接近全局最优,适合机器翻译、文摘等对准确性要求高的任务
  • 缺点:计算量是 greedy 的 KK 倍;生成的文本有时显得"机械",缺乏自然感;对话等开放域任务效果不如 sampling

Sampling(采样)

不取最大值,而是按概率分布随机采样

ytP(context)y_t \sim P(\cdot \mid \text{context})

采样引入了随机性,使得生成结果多样化,但原始分布往往过于"平"或过于"尖",需要调整。

Temperature(温度)

Temperature TT 通过缩放 logits 来调整分布的"尖锐程度":

Pi=exp(zi/T)jexp(zj/T)P_i' = \frac{\exp(z_i / T)}{\sum_j \exp(z_j / T)}

其中 ziz_i 是第 ii 个 token 的原始 logit。

  • T0T \to 0:分布极度尖锐,趋近于 Greedy Decoding(概率集中在最高分 token)
  • T=1T = 1:使用原始分布
  • TT \to \infty:分布趋向均匀,所有 token 概率相等,输出完全随机
Temperature效果适用场景
0.0 ~ 0.3保守、确定、重复风险代码生成、数学推理
0.5 ~ 0.8平衡创意与连贯性对话、写作
1.0 ~ 1.5创造性强、偶有不连贯创意写作、头脑风暴

Top-k Sampling

为避免模型采样到概率极低的"奇怪" token,Top-k 只保留概率最高的 kk 个 token,重新归一化后采样:

P(v)={P(v)uVkP(u)vVk0otherwiseP'(v) = \begin{cases} \frac{P(v)}{\sum_{u \in V_k} P(u)} & v \in V_k \\ 0 & \text{otherwise} \end{cases}

其中 VkV_k 是概率排名前 kk 的 token 集合。

  • 优点:有效截断尾部低概率 token,避免奇怪输出
  • 缺点:固定 kk 不够灵活。某些步骤分布很"尖"(几个 token 主导),k=50k=50 可能还是引入太多噪声;某些步骤分布很"平"(许多 token 概率相近),k=50k=50 可能截断太多合理选项。

Top-p Sampling(Nucleus Sampling,核采样)

Top-p 解决了 Top-k 固定截断的问题:选择累积概率恰好超过 pp 的最小 token 集合,从这个"核"中采样。

形式化地,将所有 token 按概率降序排列为 v1,v2,v_1, v_2, \dots,找到最小的 mm 使得:

i=1mP(vi)p\sum_{i=1}^{m} P(v_i) \geq p

然后在 {v1,,vm}\{v_1, \dots, v_m\} 中重新归一化采样。

  • 分布尖锐时,少数 token 就能覆盖概率质量 pp,核很小,输出保守
  • 分布平坦时,需要更多 token 才能覆盖 pp,核更大,输出多样

典型值为 p=0.9p = 0.9p=0.95p = 0.95

例子:设 p=0.9p = 0.9,词表前几名概率为:

Token概率累积概率
"好"0.450.45
"棒"0.250.70
"不错"0.150.85
"糟糕"0.060.91 ← 超过 0.9
"一般"0.040.95

核为 糟糕(前4个,累积达到 0.91 ≥ 0.9),从这4个 token 中采样。

实践建议

实际应用中通常将 Temperature 与 Top-p(或 Top-k)组合使用:

# 对话/助手场景:平衡创意与连贯性
generation_config = {
"temperature": 0.7,
"top_p": 0.9,
"top_k": 50,
}

# 代码生成/数学推理:追求确定性和正确性
generation_config = {
"temperature": 0.1, # 接近贪心
"top_p": 0.95,
"top_k": 10,
}

# 创意写作/头脑风暴:鼓励多样性
generation_config = {
"temperature": 1.0,
"top_p": 0.95,
"top_k": 100,
}

:::tip 参数调节顺序 建议先调 Temperature(控制整体"温度"),再用 Top-p 截断尾部(控制"核大小")。如果生成结果重复单调,先调高 Temperature;如果输出不连贯甚至乱码,先降低 Temperature 或减小 Top-p。 :::

解码策略确定性多样性计算开销适用场景
Greedy完全确定最低最低快速原型、调试
Beam Search确定中(KK 倍)翻译、摘要
Temperature Sampling随机中-高对话、写作
Top-k Sampling随机通用
Top-p Sampling随机中-高对话、通用

本章小结

概念核心要点
自回归解码逐 token 串行生成,每步一次完整前向传播,NN 个 token = NN 次前向传播
Decode 阶段瓶颈Memory-Bound,arithmetic intensity 低,GPU 利用率低
Prefill vs DecodePrefill 并行、计算密集;Decode 串行、访存密集
TTFT首 token 延迟,由 Prefill 决定
TBTtoken 间隔,由 Decode 决定
Greedy确定性,局部最优,易重复
Beam Search维护 KK 路径,适合翻译/摘要
Temperature控制分布尖锐度,T0T\to 0 接近 greedy
Top-k / Top-p截断低概率尾部,Top-p 自适应更灵活

理解了推理的基本计算特性之后,一个自然的问题随之而来:Decode 阶段每步都要重新计算所有历史 token 的 Key 和 Value 向量吗?如果能把这些中间结果缓存起来,不就可以大幅节省重复计算了吗?这正是下一章要介绍的核心技术——KV Cache