Transformer 架构¶
背景知识
- 词嵌入 (Embedding):将离散 token 映射为连续向量,使神经网络能处理文本 → Word2Vec 论文
- 残差连接 (Residual Connection):跳跃连接让梯度直接回传,解决深层网络训练困难 → ResNet 论文
- Softmax:将任意实数向量归一化为概率分布(所有值 ∈ (0,1) 且求和为 1)
- 表示学习:模型自动从原始数据中学习有用的特征表示 → 详见
Transformer 是当前几乎所有前沿语言模型的底层架构。本文档聚焦其核心机制的技术原理和设计权衡,不涉及具体模型。
相关文档:KV Cache 与推理优化 | Mamba 与状态空间模型
推荐视频:3Blue1Brown 直观讲解 Transformer
1. 架构原理¶
Transformer 由 Vaswani 等人在 2017 年提出1,用纯注意力机制替代了 RNN 的循环结构。
graph TB
Input["输入 Tokens"] --> Emb["Token Embedding\n+ 位置编码"]
Emb --> Block
subgraph Block ["Transformer Block × N"]
direction TB
Attn["Multi-Head\nSelf-Attention"]
Attn --> AN1["Add & Norm"]
AN1 --> FFN["前馈网络\n(FFN / MoE)"]
FFN --> AN2["Add & Norm"]
end
Block --> Out["输出概率分布\n(Softmax)"]
每个 Transformer Block 重复堆叠 N 次(GPT-3 有 96 层,Llama-3-70B 有 80 层)。一个 Block 内部的完整流程:
输入 x
│
├──→ Self-Attention(x) ──→ + ──→ Norm ──→ y ① 交流:token 之间交换信息
│ ↑
└──────────────────────────┘ (残差连接:把原始输入加回来,防止信息丢失)
├──→ FFN(y) ──→ + ──→ Norm ──→ 输出 ② 思考:每个 token 独立做非线性变换
│ ↑
└───────────────┘ (残差连接)
- Self-Attention:每个 token 从其他 token 收集相关信息("交流"阶段)。输出和输入维度相同,比如
(4×128)进(4×128)出 - Add & Norm:把 Attention 的输出和原始输入相加(残差连接),再做归一化。残差连接保证信息不会在深层网络中丢失
- FFN:对每个 token 的向量独立做非线性变换("思考"阶段)。这是模型存储知识的主要位置——研究表明事实知识主要记忆在 FFN 的权重里
为什么要堆叠很多层? 每一层都在上一层的基础上进一步精炼表示。浅层捕获简单模式(词性、局部搭配),深层捕获复杂模式(语义、长距离依赖、推理链条)。类似人的理解过程——先看懂字面意思,再理解言外之意。
经过所有 Block 后,每个位置的向量都融合了上下文信息。最终每个位置预测"下一个 token 是什么":
训练时所有位置同时算 loss;推理时只看最后一个位置的输出,采样出下一个 token,追加到序列末尾,重复这个过程——这就是自回归生成。
2. 自注意力机制¶
一个 Transformer Block 里有很多个 Attention 单元(称为"头"),每个头负责捕获一种小的统计模式——比如"动词后面的名词要关注"、"相隔 5 个词的两个代词经常相关"。单个头学到的模式人类往往看不懂,但几十个头组合起来,就涌现出了对语言的整体理解。
每个头做的事情可以用一句话概括:对序列中的每个 token,根据它与其他 token 的相关度做加权求和,得到一个融合了上下文信息的新表示。
用一个简化的例子来理解——假设某个头学会了"通过动词判断名词含义":
graph LR
subgraph s1 ["「我 喜欢 吃 苹果」"]
我 -.-|低| apple1["苹果 → 水果"]
喜欢 -.-|低| apple1
吃 ===|高| apple1
end
subgraph s2 ["「苹果 发布 了 新手机」"]
发布 ===|高| apple2["苹果 → 公司"]
了 -.-|低| apple2
新手机 ===|高| apple2
end
具体来说,每个 token 的 embedding 经过三个不同的线性投影(即乘以三个学习到的权重矩阵 W_Q、W_K、W_V),得到三个向量:
- Query(Q):「我在找什么?」—— 当前 token 发出的查询,表达它需要从上下文中获取什么信息
- Key(K):「我是什么?」—— 每个 token 的标签,用于被其他 token 的 Query 匹配
- Value(V):「我能提供什么?」—— 匹配成功后实际传递的信息内容
Q 和 K 的点积衡量两个 token 的相关度(匹配分数),经 softmax 归一化后作为权重,对 V 做加权求和:
以「我 喜欢 吃 苹果」(4 个 token,假设 \(d_k=128\))为例,各矩阵的大小:
Q: (4×128) — 4 个 token,每个一条 128 维的 Query 向量
Kᵀ: (128×4) — K 转置后,列数 = token 数
Q \cdot K^T = (4×4) — 4×4 方阵,每个位置是两个 token 的匹配分数
┌─────────────────────────────┐
│ 我 喜欢 吃 苹果 │
│ 我 [0.30 0.30 0.25 0.15] │
│ 喜欢[0.20 0.10 0.40 0.30] │
│ 吃 [0.05 0.05 0.10 0.80] │
│ 苹果[0.05 0.10 0.65 0.20] │
└─────────────────────────────┘
↑ softmax 归一化后的注意力权重(每行和为 1)
V: (4×128) — 4 个 token 的 Value 向量
输出: (4×4) · (4×128) = (4×128) — 每个 token 得到一条新的 128 维向量
从右往左拆解这个公式的每一步:
| 步骤 | 运算 | 做了什么 | 例子 |
|---|---|---|---|
| 1 | \(Q \cdot K^T\) | 每个 token 的 Q 和所有 token 的 K 做点积,得到原始匹配分数 | "苹果"的 Q 和"吃"的 K 点积 = 7.4,和"我"的 K 点积 = 0.6 |
| 2 | \(/ \sqrt{d_k}\) | 除以维度的平方根,把分数缩小到合理范围 | 7.4 → 0.65,0.6 → 0.05(假设 \(d_k=128\),\(\sqrt{128}\approx11.3\)) |
| 3 | \(\text{softmax}(...)\) | 把所有分数归一化成概率(和为 1),变成注意力权重 | [0.05, 0.10, **0.65**, 0.20] → "吃"权重最高 |
| 4 | \(\cdot V\) | 用这些权重对所有 token 的 V 做加权求和,得到最终输出 | 输出 = \(0.05 \cdot V_\text{我} + 0.10 \cdot V_\text{喜欢} + 0.65 \cdot V_\text{吃} + 0.20 \cdot V_\text{苹果}\) |
最终,"苹果"这个 token 的新表示主要包含了"吃"的语义信息——模型因此理解这里的"苹果"是水果。
类比搜索引擎:你输入一个搜索词(Query),它和每个网页的标题(Key)算匹配度,然后按匹配度加权返回网页内容(Value)。区别在于 Q/K/V 都是从同一个输入学出来的,模型自己决定"问什么"和"答什么"。
除以 \(\sqrt{d_k}\) 是为了防止点积过大导致 softmax 退化成 one-hot。Multi-Head Attention (MHA) 将 Q/K/V 拆分成多个头并行计算,最后拼接输出。
GQA / MQA:共享 KV 头的变体¶
标准 MHA 中每个 Q 头都有自己独立的一组 K 和 V。GQA(Grouped-Query Attention)和 MQA(Multi-Query Attention)的思路是:强制让多个 Q 头共用同一组 KV,减少 KV 的数量。分几组是训练前定好的超参数,模型从头训练来自适应这种约束。
graph TB
subgraph MHA ["MHA — 每个 Q 头独占 KV"]
direction LR
Q1a["Q₁"] --- KV1a["KV₁"]
Q2a["Q₂"] --- KV2a["KV₂"]
Q3a["Q₃"] --- KV3a["KV₃"]
Q4a["Q₄"] --- KV4a["KV₄"]
end
subgraph GQA ["GQA — 每组 Q 头共享 KV"]
direction LR
Q1b["Q₁Q₂"] --- KV1b["KV₁"]
Q3b["Q₃Q₄"] --- KV2b["KV₂"]
end
subgraph MQA ["MQA — 所有 Q 头共用一组 KV"]
direction LR
Q1c["Q₁~Q₄"] --- KV1c["KV₁"]
end
为什么共享 KV 不会严重损失质量? 因为注意力的多样性主要靠 Q 提供。每个 Q 头仍然有自己独立的权重矩阵,所以不同 Q 头对同一个 token 会生成不同的 Query。不同的 Q 和同一组 K 做点积,得到的注意力分布也不同——类似几个人带着不同问题去同一个图书馆查资料,查到的内容自然不同:
共享同一组 KV,但 Q 不同 → 注意力分布不同 → 输出不同
$Q_1 \cdot K^T = [0.7, 0.1, 0.1, 0.1]$ → 主要关注第 1 个 token
$Q_2 \cdot K^T = [0.1, 0.1, 0.1, 0.7]$ → 主要关注第 4 个 token
↑ 同一组 K ↑ 但结果完全不同
GQA/MQA 的主要动机是减少推理时 KV Cache 的显存占用——KV 头越少,需要缓存的向量越少。具体的缓存大小对比见 KV Cache 与推理优化。
位置编码:从固定到 RoPE¶
Attention 本身对位置不敏感——打乱输入顺序,输出不变。位置编码注入序列顺序信息。
原始 Transformer 用固定的正弦/余弦函数1编码绝对位置。现代 LLM 普遍采用 RoPE(旋转位置编码)2——将位置信息编码为 Q/K 向量的旋转角度:
直觉:把每对相邻维度视为二维平面上的一个点,位置 \(m\) 对应旋转角度 \(m\theta\)。两个 token 做点积时,结果只取决于它们的相对位置差(旋转角度之差),而非绝对位置。
| 特性 | 效果 |
|---|---|
| 天然编码相对位置 | 点积自动包含相对信息,不需要显式计算位置差 |
| 外推性好 | 配合 NTK-aware 缩放,可处理比训练时更长的序列 |
| 无额外参数 | 旋转角度由公式确定,不增加可学习参数 |
RoPE 已被 LLaMA、Mistral、Qwen 等主流模型采用,成为事实标准。
3. 前馈网络与 MoE¶
前馈网络(FFN)¶
前馈网络是最基础的神经网络结构——输入向量乘以权重矩阵,过一个非线性激活函数,再投影回来:
"前馈"的意思是数据单向从输入流向输出,没有循环、没有回头看。在 Transformer 里,Attention 负责 token 之间"交流"(你和谁相关),FFN 负责每个 token 独立地"思考"(对收集到的信息做非线性变换)。研究发现模型记住的事实知识(比如"巴黎是法国首都")主要存储在 FFN 的权重矩阵里。
MoE:稀疏激活的参数扩展¶
Mixture of Experts 是 FFN 层的一种替换策略:与其用一个大 FFN,不如用很多个小 FFN(Expert),每次只激活其中几个:
| 模型 | 总参数 | 活跃参数 | Expert 数 | Router 策略 |
|---|---|---|---|---|
| Mixtral 8x7B | 46.7B | 12.9B | 8, Top-2 | Token-choice |
| DeepSeek-V3 | 671B | 37B | 256, Top-8 | 辅助 loss-free 均衡 |
| DeepSeek-V4-Pro | 1.6T | 49B | 384, Top-6 | loss-free + Hash routing(前 3 层) |
| Qwen2.5-MoE | 14.3B | 2.7B | 60, Top-4+4 shared | Fine-grained |
MoE 的关键工程挑战:
- 负载均衡:如果 Router 总选同几个 Expert,其他 Expert 白训练。传统方案加辅助 loss 强制均衡,但这个 loss 会干扰主任务。DeepSeek-V3 提出 loss-free 均衡——用动态偏置调节 Router 分数,不引入额外 loss
- Expert 并行:256 个 Expert 不可能放在一张卡上,需要跨设备调度。通信开销是瓶颈
- Token 丢弃:当某个 Expert 过载时需要丢弃 token,影响质量