推理
inference概述
推理应用场景
- 实际应用 (Actual Use) :这是用户最直观的感受,包括聊天机器人(如 ChatGPT)、代码自动补全(如 Copilot)以及对海量数据的离线批处理。
- 模型评估 (Model Evaluation) :为了衡量模型的性能(例如指令遵循能力),需要运行推理来生成结果并进行打分。
- 测试时计算 (Test-time Compute) :这与当前流行的“推理模型”(如 OpenAI o1 系列)相关,即通过增加推理时的计算量(如思考过程、多路径搜索)来提升模型解决复杂问题的能力。
- 强化学习训练 (RL Training) :在强化学习(如 PPO 或 GRPO)过程中,模型需要生成大量样本,然后由奖励模型进行打分,这个“生成样本”的过程本质上就是大规模推理。
指标
- ime-to-first-token (TTFT) / 首字延迟
- 定义 :从用户发出请求到屏幕上蹦出第一个字的时间。
- 重要性 :这是即时交互(Chatbot)体验的生命线。如果 TTFT 过长,用户会感觉系统“卡死”了。这主要取决于Prefill(预填充)阶段的速度,即模型处理输入 Prompt 的速度。
- Latency (seconds/token) / 生成延迟
- 定义 :第一个字出来后,后续每个字蹦出来的速度。
- 重要性 :决定了用户阅读的流畅感。如果这个数值过高(比如 0.5秒/token),用户阅读速度会超过生成速度,体验极差。这取决于Generation(生成)阶段的内存带宽。
- Throughput (tokens/second) / 吞吐量
- 定义 :系统在单位时间内处理的所有 Token 总量(包括所有并发用户的输入和输出)。
- 重要性 :这是批处理(Batch Processing)的核心指标。比如你要在一夜之间给 100 万条数据打标签,你不在乎每条数据是 1 秒还是 3 秒出来的,你只在乎这 100 万条数据总共要跑多久。提高 Batch Size 可以显著提升吞吐量,但通常会牺牲 TTFT 和 Latency。
效率的关键考量
这部分解释了为什么推理比训练更难优化:
- 训练 (Training) = 并行 (Parallel)
- 在训练时(特别是 Teacher Forcing 模式),所有的输入 Token($x_1, x_2…x_T$)和目标 Token 都是已知的。
- 优势 :我们可以一次性将整个序列输入 Transformer,利用矩阵乘法(MatMul)并行计算出所有位置的预测结果。GPU 的计算单元(Compute)可以被瞬间填满,利用率极高。
- 推理 (Inference) = 串行 (Sequential)
- 在生成时,我们必须先生成第一个词 $y_1$,把它加到输入里,才能生成 $y_2$。
- 劣势 :这种自回归特性导致无法在时间轴上并行。
- 后果 :每生成一个 Token,都要把几百 GB 的模型权重从显存(HBM)搬运到计算单元,只为了做一次很小的矩阵向量乘法。 显存带宽(Memory Bandwidth)成为了绝对瓶颈 ,GPU 大部分时间都在“等数据”,而不是在“算数据”。
重温计算强度
这部分从数学角度解释了为什么推理是内存受限而不是计算受限
1. 设定场景:矩阵乘法
为了简化分析,我们只看 Transformer 中的一个典型操作: MLP 层中的矩阵乘法 。
- 操作 :$Y = X \times W$
- $X$ (Input) :维度为 $(B, D)$。$B$ 是 Batch Size(并发请求数),$D$ 是隐藏层维度。
- $W$ (Weights) :维度为 $(D, F)$。$F$ 是 MLP 的中间维度(通常 $F=4D$)。
- 数据类型 :bf16(每个元素占 2 bytes )。
2. 算账:计算量 vs. 数据搬运量
我们需要计算“做多少运算”和“搬运多少数据”之间的比例。
- 计算量 (FLOPs) :
矩阵乘法的标准计算公式。每个输出元素需要 $D$ 次乘法和 $D$ 次加法。
$$
\text{FLOPs} = 2 \cdot B \cdot D \cdot F
$$
- 数据搬运量 (Bytes Transferred) :
我们需要从显存(HBM)读取输入和权重,并写回输出。
- 读输入 $X$ :$B \times D$ 个元素 $\times$ 2 bytes = $2BD$
- 读权重 $W$ :$D \times F$ 个元素 $\times$ 2 bytes = $2DF$ ( 注意:这是模型参数,非常大 )
- 写输出 $Y$ :$B \times F$ 个元素 $\times$ 2 bytes = $2BF$
$$
\text{Total Bytes} = 2BD + 2DF + 2BF
$$
3. 核心指标:算术强度 (Arithmetic Intensity)
算术强度 = 每搬运 1 byte 数据,能进行多少次浮点运算 。我们希望这个值越高越好,这样 GPU 的计算单元才不会闲着等数据。
$$
\text{Intensity} = \frac{\text{FLOPs}}{\text{Bytes}} = \frac{2BDF}{2BD + 2DF + 2BF}
$$
关键近似 :
在实际的大模型中,模型参数量($D \times F$)通常远大于 Batch Size 带来的数据量($B \times D$ 或 $B \times F$)。
当 $D$ and $F$ 很大时,分母中的 $2DF$ 占主导地位。
$$
\text{Intensity} \approx \frac{2BDF}{2DF} = B
$$
结论:MLP 层的算术强度约等于 Batch Size ($B$)。
4. 硬件的瓶颈:H100 的“门槛”
为了判断我们的算法是否高效,我们需要把它和硬件的极限进行对比。这里使用了 NVIDIA H100 作为基准:
- 计算能力 :989 TFLOPs ($989 \times 10^{12}$)
- 内存带宽 :3.35 TB/s ($3.35 \times 10^{12}$)
$$
\text{H100 Intensity} = \frac{989}{3.35} \approx 295
$$
这意味着:在 H100 上,你的算法必须每搬运 1 byte 数据至少做 295 次运算,才能把计算核心跑满。
- 如果你做的运算少于 295 次(Intensity < 295),计算核心就会 闲置 ,等待内存传输数据。这就是 Memory-limited(内存受限) 。
- 如果你做的运算多于 295 次,内存带宽就不是瓶颈了。这就是 Compute-limited(计算受限) 。
5. 为什么推理是“内存受限”的?
结合第 3 步和第 4 步的结论:
- 算法强度 $\approx B$
- 硬件门槛 $\approx 295$
这就得出了本节最重要的结论:
$$
\text{只有当 } B > 295 \text{ 时,我们才能充分利用 H100 的算力。}
$$
但在推理生成阶段(Generation)发生了什么?
- 我们在逐个 token 生成,通常每个用户是独立的。
- 如果是单用户推理, $B = 1$ 。
- 算术强度 = 1 。
结局 :
我们在 H100 上运行 $B=1$ 的推理时,算术强度只有 1 ,而硬件要求 295 。
这意味着我们 仅仅利用了 GPU 约 1/300 的计算能力 。GPU 99% 的时间都在痛苦地等待巨大的权重矩阵($W$)从显存搬运到芯片上,仅仅为了做一次极小的向量乘法运算,然后又把权重扔掉。
这就是为什么推理优化(如 Batching)如此重要的数学本质:必须增加 $B$ 才能跨过内存墙(Memory Wall)。
