# 第 6 章 按下回车之后：一次推理的旅程

> 本章问题：一次回答在机器里经历了什么？为什么是逐字蹦出来的？
> 来源：https://llm.weiborao.link/#ch6

**上一章留下的问题：**训练好的模型是一个文件。当你在聊天框里按下回车，这个文件是怎样一个字一个字地把答案“吐”出来的？

用训练好的模型回答问题，叫**推理**（inference）。注意这里的“推理”是工程术语，指“运行模型”，和第 5 章“推理模型”里那个“逻辑推理”的意思不同。本章跟着一个问题走完它在模型里的全过程，然后回答：为什么回答是一个字一个字蹦出来的。

### 6.1 一个问题的六站旅程

> 【图示】一次推理的旅程：一，分词，把问题切成 Token；二，预填充，一次性并行读完整个问题，同时生成 KV 缓存；三，计算下一个 Token 的概率；四，按温度采样选出一个 Token；五，把它发给用户，同时接回输入末尾，回到第三步；六，遇到结束符，停止。

1. **分词：**你的问题被切成 Token，换成编号（第 3 章）。
2. **预填充：**所有输入 Token 一起过一遍网络，顺便把每个 Token 的“名牌”记进 KV 缓存。
3. **算概率：**只用最新一个位置，回头查 KV 缓存，算出词表里每个词作为下一个 Token 的概率。
4. **采样：**按概率抽出一个 Token。
5. **发出并接回：**把它发给你，同时接到输入末尾，回到第 3 步。
6. **停止：**抽到“结束符”时，循环结束。

**图 6-1** 推理的六站。第 3、4、5 站组成一个循环，每转一圈产生一个 Token，这个循环叫**解码**。网页版可以逐站播放。

问：为什么不能一次把整个回答算出来？

答：因为第二个词取决于第一个词是什么。模型在第 4 章学会的就是“根据前文猜下一个”，前文没定，下一个就没法猜。

问：那你的问题呢？它有 12 个 Token，也要一个一个读吗？

答：不用。问题是已知的，12 个位置可以同时算，这就是预填充。只有“还没写出来的回答”必须一个一个来。

这个模式有个名字，叫**自回归**：模型的每一个输出，都会变成它下一步的输入。你在聊天窗口看到的“打字机效果”，就是这个循环的真实节奏：每转一圈，屏幕上多一个 Token。

### 6.2 两个阶段，两种瓶颈

> 【图示】一次回答的时间线：先是一段预填充，结束时出现第一个 Token，这段时间叫首 Token 延迟；之后每隔一小段时间吐出一个 Token，这个间隔叫 Token 间延迟。问题越长，预填充越久；回答越长，解码越久。

**图 6-2** 一次回答的时间线。从按下回车到看见第一个字的时间叫 TTFT（首 Token 延迟），之后相邻两个字的间隔叫 ITL（Token 间延迟）。

两个阶段累的地方不一样：

**预填充是“算力密集”的。**几千个输入 Token 同时参与大块的矩阵乘法，GPU 的计算单元被填满，瓶颈在“算得多快”。

**解码是“显存带宽密集”的。**每生成一个 Token，GPU 都要把全部权重从显存里读一遍，但只为一个位置做计算。算一次很快，读一遍却慢。粗算一下：405B 模型按 FP8 分到 8 块 H100 上，每块约 50 GB；H100 的显存带宽是 3.35 TB/s，读一遍要约 15 毫秒。所以只服务一个人时，每秒最多大约 60 多个 Token（估算，未计其他开销）。

类比：

解码像**公交车**：不管车上坐了 1 个人还是 50 个人，跑一趟的时间（把权重读一遍）差不多。所以推理服务会把很多用户的请求拼成一批，一趟拉走，这叫**批处理**。每个人等的时间稍长一点，整体的吞吐量却成倍上升。如何在“每个人等得短”和“一趟拉得多”之间取舍，是推理系统的核心难题。

表 6-1 衡量推理服务的四个指标

| 指标 | 含义 | 用户的感受 |
| --- | --- | --- |
| TTFT 首 Token 延迟 | 按下回车到第一个 Token 出现 | “它反应快不快” |
| ITL Token 间延迟 | 相邻两个 Token 的间隔 | “字蹦得顺不顺” |
| 吞吐量 | 整个系统每秒生成的 Token 总数 | （运营方关心）同样的机器能服务多少人 |
| 并发数 | 同时在服务的请求数 | 高峰期会不会排队 |

### 6.3 KV 缓存：不必每次重读全文

第 4 章说过，注意力要让每个位置“回头看”前文所有位置的名牌（Key）和内容（Value）。如果每生成一个新 Token，都要把前文所有 Token 的名牌重新算一遍，那么回答越长，每一步越慢。

解决办法很朴素：**算过的就存起来**。前文每个 Token 在每一层的 Key 和 Value 算一次后就留在显存里，这叫 **KV 缓存**。新 Token 只需要算自己的那一份，再和缓存比对。

类比：

KV 缓存像**读书时贴的便利贴**。读到第 300 页时要回想某个人物，你不会从第 1 页重读，而是翻看之前贴的便利贴。代价是便利贴会越贴越多，书越来越厚。

这些“便利贴”有多厚？以 Llama 3.1 405B 为例，用 BF16 存储时，每个 Token 的 KV 缓存约 516 KB。一个 12.8 万 Token 的长对话，光 KV 缓存就要约 68 GB，快赶上一整块 H100 的显存了。

> 【图示】显存账本示意：一台 8 卡 H100 服务器共 640 GB 显存。Llama 3.1 405B 按 FP8 存权重占 405 GB，剩下约 235 GB。一个 12.8 万 Token 长对话的 KV 缓存按 BF16 约 68 GB，只够同时放下三个这样的对话。

**图 6-3** 一台 8 卡服务器的显存账本（粗算）。权重占掉大半之后，剩下的空间只够放三个超长对话的 KV 缓存。长上下文推理的瓶颈，往往是显存而不是算力。

**停一下：为什么很多聊天产品对“上下文长度”收费更高，或者对很长的对话做截断与压缩？**

**参考答案：**

因为上下文越长，两件事越贵：预填充要算的量变大（TTFT 变长），KV 缓存占的显存变多（同一台机器能同时服务的人变少）。图 6-3 里，一个超长对话就占了一块 GPU 的大部分显存。这也是第 11 到 14 章里推理集群要精心设计显存、网络和存储的原因之一。

### 6.4 采样：同一个问题，为什么答案每次不同

第 3 站算出来的是一个概率分布，不是一个确定的词。第 4 站要从中抽一个。抽法由一个叫**温度**的参数控制：温度低，几乎总是选概率最高的词，回答稳定但刻板；温度高，冷门的词也有机会被选中，回答更多样，也更容易跑偏。

表 6-2 “今天天气真\_\_\_”：同一个分布，不同温度下的抽中概率

| 候选词 | 温度 0.2 | 温度 0.5 | 温度 1.0（原分布） | 温度 1.5 |
| --- | --- | --- | --- | --- |
| 好 | 98.3% | 77.5% | 50.0% | 38.0% |
| 不错 | 1.6% | 15.0% | 22.0% | 22.0% |
| 晴朗 | 0.1% | 4.5% | 12.0% | 14.7% |
| 冷 | ≈0 | 2.0% | 8.0% | 11.2% |
| 热 | ≈0 | 0.8% | 5.0% | 8.2% |
| 糟糕 | ≈0 | 0.3% | 3.0% | 5.8% |

原分布为示意；温度的算法是把每个概率开 1/T 次方后重新归一化。

网页版封面上的那台终端也有一个温度旋钮：把它调到 1.5 以上再重新生成，常常会在某一步抽中一个冷门的词，回答从那里走上另一条路。同一个机制，既是模型“有创造力”的来源，也是它“会跑偏”的来源。

##### 逐字输出是

- 自回归解码的真实节奏：生成一个，发出一个
- “流式”传输：不必等全部写完再显示

##### 逐字输出不是

- 为了显得像人在打字而加的动画
- 模型早已想好全文、再慢慢“念”出来：后面的字此刻还不存在

到这里，我们只看了“模型”这一层。真实的服务里，你的请求还要经过负载均衡、调度系统、多块 GPU 之间的通信，才能变成屏幕上的字。第 10 章会把这条流水线从头到尾再走一遍，那时你会认识 Kubernetes、CUDA 和 NCCL。

##### 本章带走

推理 = 一次并行的预填充 + 一个“生成一个、接回一个”的解码循环。打字机效果就是这个循环的真实节奏。

- 预填充算力密集，决定首 Token 延迟；解码显存带宽密集，决定字蹦得多快。批处理像公交车，一趟拉多人。
- KV 缓存避免重复计算，但会吃掉大量显存：405B 模型一个 12.8 万 Token 的对话约 68 GB。
- 温度控制采样：低温稳定，高温多样也更易跑偏。

**于是，下一个问题**：模型每一步都在挑“最像答案”的词。当它其实不知道答案时，它也会挑一个看起来最像答案的词。怎样让它少说错话、说真话？又怎样判断一个模型到底好不好？
