第 2 章 · 约 28 分钟
模型架构
计算、参数和上下文状态,如何变成 M、F、R?
从哪里来
8B 只说明参数量的约数
第 1 章把一次执行写成容量、运算量和读取量。数字从哪里来?“一个 8B 模型”只给出参数规模的数量级,没有说明每个 token 用哪些参数、上下文保存在哪、哪些工作必须先后发生。
计算图用节点表示运算、用边表示数据依赖。节点决定 F,边上的张量决定传递的字节,边的方向决定谁必须等谁。权重在请求之间保持不变,KV 随生成追加,中间激活往往用完即弃——三种数据的生命周期不同,优化手段也不同。
| 量 | 值 | 它改什么 |
|---|---|---|
| 层数 L | 36 | 重复的计算、逐层状态、深度依赖 |
| 隐藏维 d | 4096 | 投影与激活的宽度 |
| FFN 中间维 f | 12288 | 三个 FFN 矩阵 |
| Q / KV 头 | 32 / 8 | GQA:每 4 个查询头共享一组 KV |
| 头维 dh | 128 | 点积与状态宽度 |
| 词表 V | 151936 | 嵌入与输出头 |
Prefill 能并行,decode 被自己的输出卡住
RNN 在同一层等待前一个 token。因果 Transformer 不同:只要上一层的前缀已经算完,同一层里已知的输入 token 可以一起算。深度方向仍有依赖——第 l+1 层要用第 l 层的结果。
生成新 token 又多了一条依赖:下一步的输入就是这一步的输出。所以 prefill 处理已知输入,decode 逐步吐出新词。缓存把“重新计算旧 token 的投影和 FFN”变成“保存并读取它们的 K、V”。旧的查询已经完成任务,下一步用新的 Q 去打已经存下来的上下文。
c_KV = 2 · L · n_KV · d_h · b
Qwen3-8B、BF16:2 × 36 × 8 × 128 × 2 B = 144 KiB。8K 上下文就是 1.125 GiB。Batch 条独立请求就有 B 份。
MHA 32 KV
GQA 8 KV
MQA 1 KV
MLA 低秩
MHA、GQA、MQA、MLA
多头注意力若为每个查询头都存一份 KV,容量按头数线性涨。分组查询注意力(GQA)让若干查询头共享一组 KV:Qwen3-8B 是 32 个 Q 头配 8 个 KV 头,相对于 MHA 省 4 倍状态。MQA 让全部查询共享一组,再省 8 倍,但表达力更差。MLA 把 KV 压进低秩潜空间,用计算换容量——DeepSeek 系列走这条路,8K 上下文大约只有 GQA 的一半。
权重、上下文、临时数据要分开记账。Qwen3-8B 的 BF16 权重约 16.38 GB,驻留后供所有请求反复使用;一步 decode 的主要权重读取约 15.14 GB(词嵌入按行查找,不必整表扫过)。8K 上下文约 1.125 GiB。当 batch 增大,各请求的 KV 读取会在某一点超过权重读取:H=8192 时大约 B=13,H=2048 时大约 B=51。上下文越长,权重复用的红利越早被独立状态吃掉。
实验
KV 缓存
每 token
147 KB
全部 KV
1.21 GB
权重+工作区后剩余
60.41 GB
还能再塞几条
50
Qwen3-8B 默认是 144 KiB/token。把 n_KV 从 8 拖到 32,就是从 GQA 回到 MHA。
考核
第 2 章考核
4 题
1.Qwen3-8B、BF16、GQA。每 token 的 KV 是多少?
2.把 Qwen3-8B 的 KV 头从 8 改成 4,查询头仍是 32。KV 容量如何变?
3.Qwen3-8B 一步 decode 权重读取约 15.14 GB,每请求 8K KV 为 1.125 GiB。上下文读取超过权重读取的最小整数 batch 约为?
4.为什么不能用总参数量直接代替一次 decode 的运算量?
全部作答后交卷。