自然语言处理(九):大语言模型架构深度解析
拆解现代 LLM 的内部结构:Pre-norm + RMSNorm + SwiGLU + RoPE + GQA、KV Cache 机制、FlashAttention 的 IO 调度、稀疏 MoE,以及 INT8/INT4 量化。
2017 年的 Transformer 论文里画了一个 block。今天每一款生产级 LLM 还在沿用它的轮廓,但内部几乎每一个零件都换过:post-norm 换成了 pre-norm,LayerNorm 换成了 RMSNorm,GELU 换成了 SwiGLU,正弦位置换成了旋转位置(RoPE),多头注意力变成了分组查询注意力(GQA),稠密 FFN 在某些模型里被稀疏 MoE 替换。更重要的是,主导推理性能的那个数据结构——KV Cache——根本没出现在原论文里。
这篇文章按照"实现/训练/部署时真正起作用的顺序"来串这些改动:先讲现代 decoder block,再讲为长上下文买单的 KV Cache,然后讲位置怎么编(RoPE / ALiBi),讲让 cache 小下来的注意力布局(GQA / MQA),讲让注意力跑得快的 IO 友好内核(FlashAttention),最后讲怎么"长大但不增加每 token 计算量"(MoE)以及"压小但不掉精度"(量化)。
你将学到什么
- 现代 block 布局:pre-norm + RMSNorm + SwiGLU + RoPE + GQA,每一项替换的动机。
- KV Cache 机制:把前缀注意力从 $O(n^2)$ 重算变成每步 $O(n)$ 的代价是什么,内存代价又是什么。
- 位置编码:sinusoidal、RoPE、ALiBi 三种方案的不同回答。
- 注意力变种:MHA、MQA、GQA 在 70B 量级模型上具体差多少 cache。
- FlashAttention:用 IO 感知调度让 tile 留在 SRAM、不再实例化 $n \times n$ 分数矩阵。
- MoE:top-$k$ 稀疏路由让总参数变大但每 token FLOPs 不变。
- 量化:FP16 → INT8 → INT4 的 GPTQ/AWQ 路线,以及精度与显存的取舍。
前置知识
- 基础 Transformer block(第四篇 — 注意力机制与 Transformer )。
- 预训练模型概念(第五篇 — BERT 与预训练模型 )。
- 能读 PyTorch 和基础线性代数。
三大家族:encoder-only、decoder-only、encoder-decoder
在进入现代 block 之前,先回顾一下为什么几乎所有通用 LLM 都收敛到了 decoder-only。三种架构的差别只在注意力 mask:
| 家族 | Mask | 预训练目标 | 强项 | 代表 |
|---|---|---|---|---|
| Encoder-only | 双向 | 掩码语言模型(MLM) | 语义理解 | BERT、RoBERTa、DeBERTa |
| Decoder-only | 因果(下三角) | 下一 token 预测(LM) | 生成、ICL | GPT、LLaMA、Qwen、Mistral |
| Encoder-Decoder | 双向 encoder + 因果 decoder + cross-attn | 去噪 / span corruption | seq-to-seq | T5、BART、FLAN-T5 |
| |
Decoder-only 之所以赢在 scaling 上有两个原因:第一,所有任务都可以转成"预测下一个 token",单一目标和数据格式可以无限扩展;第二,因果 mask 让前缀缓存(KV Cache)非常便宜——encoder-decoder 还要缓存 cross-attention,而 encoder-only 根本没法生成。今天说"LLM"几乎都默认指带后面这些改动的 decoder-only 模型。
现代 decoder block

LLaMA 风格的 block(LLaMA、LLaMA-2、Mistral、Qwen、Yi、DeepSeek……)相对 2017 年的 block 改了五处。每一处单独看影响都不大,但叠加起来就得到一个"无须 warmup 调参也能稳定训练、能扩展到长上下文、推理更快"的模型。
1. Pre-norm 替代 post-norm。 原版是 Norm(x + Sublayer(x)),现代版是 x + Sublayer(Norm(x))。残差路径变成干净的恒等通路,深层堆叠时梯度尺度更稳,也省掉了著名的 Transformer 学习率 warmup。
2. RMSNorm 替代 LayerNorm。 LayerNorm 减去均值再除以标准差,RMSNorm 只除以均方根,没有均值,也没有 bias:
$$ \mathrm{RMSNorm}(x) = \frac{x}{\sqrt{\frac{1}{d}\sum_i x_i^2 + \varepsilon}} \cdot g. $$少一个归约、少一组参数,质量没有可测损失。
3. SwiGLU 替代 GELU。 标准 FFN 是 Linear → GELU → Linear,SwiGLU 多加一条 gating linear:
逐元素乘法给 FFN 注入了乘性交互,等参数量下 perplexity 通常改善 1–2%。为了维持参数预算,隐藏维度按 $2/3$ 缩。
4. RoPE 替代学习式绝对位置。 位置不再加在 embedding 上,而是在注意力时旋转 Q/K,下一节展开。
5. GQA / MQA 替代 MHA。 多个 query 头共享一个 KV 头,下下节展开。
| |
KV Cache:为长上下文买单的数据结构

自回归解码每步只新增一个 token。朴素地实现,新位置的注意力需要"所有过去 token 的 K 和 V"——每步都从头投影一次的话,每步 $O(n)$,总共 $O(n^2)$。KV Cache 的关键观察是:这些投影只是过去 token 的确定函数,算一次存下来就行。
它能成立靠两个事实:
- 模型是因果的,已写入 cache 的位置不会因后续 token 而需要修改;
- K、V 投影对输入是线性的,缓存投影后张量与重算等价。
| |
代价并不便宜:cache 显存与序列长度线性增长,是 decode 阶段的主导内存项。以 LLaMA-2-70B 形状(80 层、64 KV 头、head_dim=128、fp16)的朴素 MHA 为例:
$$ 2 \cdot 80 \cdot 64 \cdot 128 \cdot 2\text{ B} = 2.6\text{ MB / token}, $$32K 上下文光 cache 就要 84 GB——比权重还大。这正是 GQA、MQA 和 PagedAttention(vLLM)出现的根本原因。
位置编码:sinusoidal、RoPE、ALiBi

自注意力本身没有顺序:把输入排列一下,输出也只是同样地排列。三种主流方案在三个不同的位置注入位置信息。
Sinusoidal 绝对位置(Vaswani 等,2017)。 在 embedding 层把固定的 $\sin/\cos$ 向量加到 token 上。位置信息要靠后续每一层线性投影把它带过去,外推到训练长度之外效果很差。
RoPE — 旋转位置嵌入(Su 等,2021)。 不加在 embedding 上,而是在注意力时旋转 Q 和 K。把 $d$ 维 head 切成 $d/2$ 个二维平面,平面 $i$ 的频率为 $\theta_i = 10000^{-2i/d}$,位置 $m$ 把第 $i$ 个平面旋转角度 $m\theta_i$。关键恒等式:
$$ \langle R_m q,\; R_n k \rangle = \langle q,\; R_{n-m} k \rangle, $$点积只依赖于相对偏移 $n - m$。这就是为什么 RoPE 能外推到比训练长度更长的上下文,也是为什么所有现代模型(LLaMA、Qwen、Mistral、Yi、DeepSeek、GPT-NeoX)都用它。
| |
ALiBi — 线性偏置注意力(Press 等,2021)。 直接不要位置嵌入,在 softmax 前的分数上加一个按头不同的线性惩罚 $-m_h \cdot |i - j|$。$m_h$ 小的头看全局,$m_h$ 大的头看局部。原论文里 ALiBi 比 RoPE 外推更远,但因为没注入相对相位,在知识密集的长上下文 benchmark 上通常输给 RoPE。BLOOM、MPT 用过它。
工程上的现实是:RoPE 配上 NTK-aware scaling、YaRN、position interpolation 这些后训练技巧,可以让 4K 训练的模型微调到 32K–128K,已经成为绝对主流。
注意力变种:MHA → GQA → MQA

标准多头注意力(MHA)每个 query 头都有自己的 K、V 投影,因此 KV Cache 与 KV 头数成正比,正是上一节算出 84 GB 的元凶。两个折中方案:
- MQA — 多查询注意力(Shazeer,2019): 所有 query 头共享一个 KV 头,cache 缩 $H$ 倍,但 K/V 投影失去头多样性,难任务上质量明显下降。
- GQA — 分组查询注意力(Ainslie 等,2023): LLaMA-2-70B、Mistral 等当下普遍采用的中间方案。把 $H_q$ 个 query 头分成 $G$ 组,每组共享一个 KV 头。LLaMA-2-70B 用 $H_q = 64$、$G = 8$,cache 缩 8 倍而质量与 MHA 持平。
| |
LLaMA-2-70B 形状下的具体数字(fp16,每 token):
- MHA,64 KV 头 → 2.56 MB/token → 32K 上下文 80 GB;
- GQA-8,8 KV 头 → 0.32 MB/token → 32K 上下文 10 GB;
- MQA,1 KV 头 → 0.04 MB/token → 32K 上下文 1.25 GB。
GQA-8 几乎拿走了 MQA 的全部内存收益,而质量基本无损——这就是为什么近期开源权重模型默认都用它。
FlashAttention:相同的数学,IO 感知的调度

朴素注意力内核会把 $S = QK^\top$ 整个 $n \times n$ 分数矩阵写进 HBM(GPU 主显存),在 HBM 上跑 softmax,再乘 $V$。$n=8192$、fp16 时,仅 $S$ 一个矩阵每个 head 每层就是 128 MB,绝大多数流量都被反复读写浪费掉。
FlashAttention(Dao 等,2022)做的是同一份数学、不同的调度:
- 把 $Q$、$K$、$V$ 切成行/列 tile,使其能放进每个 SM 上 ~192 KB 的 SRAM;
- 在 SRAM 内一次只算一个 $S$ tile;
- 用在线 softmax:维护逐行的最大值 $m$ 和分母 $\ell$,每个 tile 只增量更新部分输出,整行 $S$ 永远不需要存在;
- 只把最终 $O$ 写回 HBM。完整的 $S$ 从未出现。
结果是数学上完全等价(仅浮点归约顺序不同)的注意力,HBM 流量从 $O(n^2)$ 降到 $O(n)$,$n \geq 2048$ 时墙钟提速 2–4 倍,显存最高省 8 倍。FlashAttention-2 进一步优化 warp 级任务划分,能跑到 A100 FP16 峰值的约 70%。
| |
记住:FlashAttention 不改变模型计算什么,只改变 GPU 上的算子顺序。序列长度超过 ~1K 时一律应该开。
混合专家:长大但不增加每 token 计算量

稠密 Transformer 的参数和 FLOPs 大头都在 FFN。MoE 把单个 FFN 换成 $N$ 个"专家"FFN,再加一个微型 router,对每个 token选 top-$k$ 个专家来执行:
$$ y = \sum_{i \in \mathrm{TopK}(W_g x)} g_i(x)\, E_i(x),\qquad g(x) = \mathrm{softmax}(W_g x). $$每 token 的 FFN FLOPs 随 $k$(通常 2)增长,而非随 $N$ 增长。所以 8 专家模型的 FFN 参数大约是稠密模型的 8 倍,但 FFN 计算只多了 2 倍。Mixtral 8×7B 总参数 47B,每 token 只激活约 13B,得到 70B 量级的质量、13B 量级的推理成本。
老实说,代价在别的地方:
- 显存。 所有专家都得常驻,即使只跑 $k$ 个。Mixtral 8×7B fp16 要 ~94 GB,比稠密 70B 还大;INT4 后才能塞进 24 GB。
- 负载均衡。 没干预的 router 会塌缩到几个偏爱的专家。真实训练要加辅助负载均衡损失(Shazeer 2017、Switch Transformer)和小幅 router 噪声鼓励探索。
- All-to-all 通信。 多卡训练时每个 token 要跑到目标专家上再跑回来,对互联拓扑非常敏感。
| |
(生产实现会用 grouped GEMM 和容量限制 bucket 并行调度 token 到专家,上面的循环只为了清晰。)
量化:每个权重少几比特

现代 LLM 权重一般用 BF16 训练(每参数 2 字节),70B 模型 140 GB,单张 A100 80GB / H100 80GB 装不下。量化把每个权重换成低比特整数加一组 per-block scale,以小幅精度代价换显存。
对称 INT8。 选一个 per-tensor(或 per-channel)scale $s = \max|w| / 127$,存 $\hat w = \mathrm{round}(w / s) \in [-127, 127]$。计算时恢复 $w \approx s \cdot \hat w$。显存减半,INT8 tensor core 上吞吐大致也翻倍。
| |
INT4 + GPTQ(Frantar 等,2022)。 直接把每个权重独立量化到 4 bit 会精度暴跌。GPTQ 改为按列量化,每量完一列就用一小批校准数据估计的 Hessian 修正剩余未量化列以补偿当前列的舍入误差。结果是 7B 以上模型 4 bit 后 perplexity 损失 <1%。
INT4 + AWQ(Lin 等,2023)。 AWQ 观察到权重通道里只有约 1% 是"显著"的(由大激活值驱动),只要保护这部分通道(用 per-channel scaling,并不真的留 fp16)就能恢复绝大部分精度。AWQ 比 GPTQ 计算快很多,是当下许多量化开源 checkpoint 的默认方案。
| 精度 | 字节/参数 | LLaMA-2-7B 权重 | LLaMA-2-70B 权重 | 典型 PPL 增量 |
|---|---|---|---|---|
| FP16 / BF16 | 2.0 | 13.5 GB | 140 GB | 参考基准 |
| INT8(RTN) | 1.0 | 6.7 GB | 70 GB | <0.5% |
| INT4(GPTQ / AWQ) | 0.5 | 3.4 GB | 35 GB | <2% |
| INT3(高级算法) | 0.375 | 2.5 GB | 26 GB | 3–6% |
| |
激活通常仍保留 FP16/BF16,因为它们分布是重尾的(LLM.int8(),Dettmers 2022 发现少量"离群特征"承载了大部分激活能量,必须留高精度)。仅权重 INT4 是当下推理的最佳折中。
拼起来:高吞吐推理栈
这些组件是可组合的。一个现代的 vLLM 服务栈把以下东西凑在一起:
- LLaMA 风格 decoder block(pre-norm + RMSNorm + SwiGLU + RoPE + GQA);
- 每层 KV Cache,但按固定大小页布局(PagedAttention),上下文增删不再产生碎片;
- 预填和解码都跑 FlashAttention 内核;
- 权重 INT4 量化(AWQ / GPTQ)省显存;
- 连续批处理:某条请求结束后空出的 slot 立刻被新提示填上,而不是等批里最慢的请求。
| |
实测下来,单张 A100 80GB 跑 7B AWQ 模型能做到 3000–5000 tokens/s 的聚合吞吐,单张 H100 跑 Mixtral 8×7B AWQ 能做到 >1000 tokens/s——这两个数字按 2017 年原版 block 来写是不可想象的。
常见问题
为什么是 decoder-only 而不是 encoder-decoder?
因果 decoder-only 模型只要换提示就能当分类器、翻译器、对话机;encoder-decoder 还要额外一套 cross-attention 参数和更复杂的训练流程,且生成时无法享受廉价的前缀缓存。decoder-only 也更适合扩展,因为语料里每个 token 都是训练信号。
RoPE 还是 ALiBi?
工程上几乎都选 RoPE:它给模型真正的相对相位信息,配合 NTK / YaRN 等后训练缩放可以把上下文从 4K 扩到 32K–128K。ALiBi 的吸引力是不微调也能外推,但代价是 in-distribution 质量更弱。当下主流开源权重一律 RoPE。
FlashAttention 会改变模型输出吗?
不会。FlashAttention 是精确注意力,仅浮点归约顺序不同,与朴素内核的数值差远低于训练噪声。
MoE 是不是总比稠密便宜?
计算上更便宜,显存上更贵。如果你被显存卡住(比如单张 24GB 卡推理),量化的稠密模型通常优于 MoE;如果你有多卡显存但 decode FLOPs 是瓶颈,MoE 胜出。
7B 模型 INT4 大约掉多少精度?
用 GPTQ 或 AWQ 配 ~128 条校准样本,perplexity 增量通常 <2%,绝大多数下游任务质量肉眼难辨。70B 模型损失通常 <1%。7B 以下时量化会明显更难。
为什么 KV Cache 是长上下文的瓶颈?
cache 大小是 $2 \cdot L \cdot H_{kv} \cdot d_h \cdot T \cdot \text{字节数}$。70B 模型 32K 上下文走 MHA 是 ~80 GB,比权重还大。GQA 把它降到 ~10 GB,PagedAttention 又把碎片率压到 <5%,两者合起来才让长上下文服务在普通硬件上跑得起来。
系列导航
| 部分 | 主题 | 链接 |
|---|---|---|
| 8 | 模型微调与 PEFT | <– 上一篇 |
| 9 | 大语言模型架构深度解析(本文) | |
| 10 | RAG 与知识增强系统 | 下一篇 –> |