06. 完整 Transformer 架构:残差、LayerNorm 与层堆叠
06. 完整 Transformer 架构:残差、LayerNorm 与层堆叠
前面几章我们学了 Embedding、Self-Attention、Multi-Head Attention、FFN。这一章把它们组装在一起,看看完整的 Transformer 是怎么工作的。
一、残差连接:让深层网络能训练
1.1 深层网络的问题
GPT-3 有 96 层,GPT-4 据传更多。如果简单地把 96 个 Transformer 层串联:
x → Layer1 → Layer2 → ... → Layer96 → output
反向传播时,梯度需要从第 96 层传回第 1 层。经过 96 次乘法后,梯度要么爆炸(变成天文数字),要么消失(变成接近零)。
梯度消失意味着前面的层几乎不更新,等于白堆了。
1.2 残差连接的做法
非常简单——把输入直接加到输出上:
不用残差: output = f(x)
用残差: output = x + f(x)
图示:
x ──────────────────────┐
│ │
└──→ Attention/FFN ──→ (+) ──→ output
f(x) x + f(x)
1.3 为什么有效
数学上,残差连接让梯度有了一条”捷径”直接流回去:
∂output/∂x = ∂(x + f(x))/∂x = 1 + ∂f(x)/∂x
即使 ∂f(x)/∂x 很小(接近 0),梯度至少还有 1。这个 “1” 保证梯度不会消失。
直觉:每一层学的是增量改进(在输入基础上加了什么),而不是”完全重新计算”。
第 1 层: x₁ = x₀ + Δ₁ ("在原始输入上加了一点理解")
第 2 层: x₂ = x₁ + Δ₂ ("在上一层基础上又加了一点")
...
第 96 层: x₉₆ = x₉₅ + Δ₉₆
最终: x₉₆ = x₀ + Δ₁ + Δ₂ + ... + Δ₉₆
每一层都是一个小的修正。即使某些层学到的 Δ 很小,也不会影响其他层的信息传递。
二、Layer Normalization:稳定训练
2.1 问题:数值飘移
经过多层计算后,向量中的数值可能越来越大或越来越小,分布不稳定。这会让训练变得困难。
2.2 LayerNorm 的计算
LayerNorm 把向量归一化为均值 0、方差 1,然后用可学习的参数缩放和偏移:
LayerNorm(x) = γ * (x - μ) / √(σ² + ε) + β
其中:
- μ = mean(x):向量所有元素的均值
- σ² = var(x):方差
- ε = 1e-5:防止除零的小常数
- γ, β:可学习的缩放和偏移参数(每个维度各一个)
2.3 数值例子
输入: x = [2.0, 4.0, 6.0, 8.0]
第 1 步 - 计算均值和方差:
μ = (2 + 4 + 6 + 8) / 4 = 5.0
σ² = ((2-5)² + (4-5)² + (6-5)² + (8-5)²) / 4
= (9 + 1 + 1 + 9) / 4 = 5.0
σ = √5.0 = 2.236
第 2 步 - 归一化:
x_norm = [(2-5)/2.236, (4-5)/2.236, (6-5)/2.236, (8-5)/2.236]
= [-1.342, -0.447, 0.447, 1.342]
第 3 步 - 缩放和偏移 (假设 γ=[1,1,1,1], β=[0,0,0,0]):
output = [-1.342, -0.447, 0.447, 1.342]
归一化后,无论原始值有多大或多小,输出都在一个稳定的范围内。
2.4 Pre-Norm vs Post-Norm
Post-Norm(原始 Transformer):先计算,再归一化
Post-Norm:
x' = LayerNorm(x + Attention(x))
x'' = LayerNorm(x' + FFN(x'))
Pre-Norm(GPT-2 之后普遍使用):先归一化,再计算
Pre-Norm:
x' = x + Attention(LayerNorm(x))
x'' = x' + FFN(LayerNorm(x'))
Pre-Norm 训练更稳定,因为:
- LayerNorm 限制了每个子层的输入范围
- 残差分支(Attention/FFN)的输出幅度受控
- 梯度流更平滑
现代 LLM 基本都用 Pre-Norm。
三、一个完整的 Transformer 层
把所有组件组装在一起:
def transformer_layer(x):
# ===== 子层 1: Multi-Head Attention =====
# 1. LayerNorm (Pre-Norm)
normed = LayerNorm(x, gamma_1, beta_1)
# 2. Multi-Head Attention
Q = normed @ W_Q # 所有头的 Q, [seq, d]
K = normed @ W_K # 所有头的 K
V = normed @ W_V # 所有头的 V
# 拆成 h 个头, 每个头独立计算 attention
attn_out = multi_head_attention(Q, K, V) @ W_O
# 3. 残差连接
x = x + attn_out
# ===== 子层 2: FFN =====
# 4. LayerNorm (Pre-Norm)
normed = LayerNorm(x, gamma_2, beta_2)
# 5. FFN (两层线性 + 激活)
hidden = GELU(normed @ W_1 + b_1) # 升维: d → 4d
ffn_out = hidden @ W_2 + b_2 # 降维: 4d → d
# 6. 残差连接
x = x + ffn_out
return x
信息流图:
输入 x
│
├───────────────────────────────────┐
↓ │
LayerNorm │
↓ │
Multi-Head Attention │
↓ │
← ── ── ── ── ── ── ── ── ── ── ── ┘ (残差: +x)
│
├───────────────────────────────────┐
↓ │
LayerNorm │
↓ │
FFN │
↓ │
← ── ── ── ── ── ── ── ── ── ── ── ┘ (残差: +x)
│
输出 x'
四、堆叠多层
一个完整的 GPT 模型就是把 N 个这样的层堆叠起来:
Token IDs
↓
Embedding + 位置编码
↓
Transformer Layer 1
↓
Transformer Layer 2
↓
...
↓
Transformer Layer N
↓
Final LayerNorm
↓
输出投影 → 词汇表概率分布
4.1 模型规模对比
| 模型 | 层数 N | 嵌入维度 d | 头数 h | FFN 中间维度 | 总参数量 |
|---|---|---|---|---|---|
| GPT-2 Small | 12 | 768 | 12 | 3072 | 124M |
| GPT-2 Medium | 24 | 1024 | 16 | 4096 | 350M |
| GPT-2 Large | 36 | 1280 | 20 | 5120 | 774M |
| GPT-2 XL | 48 | 1600 | 25 | 6400 | 1.5B |
| GPT-3 | 96 | 12288 | 96 | 49152 | 175B |
| LLaMA 3 70B | 80 | 8192 | 64 | 28672 | 70B |
规律:
- 层数翻倍 → 参数量大约翻倍
- 嵌入维度翻倍 → 参数量大约翻 4 倍(因为矩阵大小是 d×d)
五、输出层:从隐藏状态到词汇表
5.1 最后一步
经过 N 层 Transformer 后,我们得到最后一个 token 位置的隐藏状态 h_final。
需要把它转换为词汇表上的概率分布(“下一个 token 最可能是什么”):
logits = h_final @ W_e^T (形状: [d] @ [d, V] = [V])
V = 50257 (词汇表大小)
logits 是一个 50257 维的向量
注意:这里复用了 Embedding 矩阵 W_e 的转置!这叫 Weight Tying。
Embedding: token ID → 向量 (用 W_e 的某一行)
输出投影: 向量 → 词汇表分数 (用 W_e^T)
复用同一个矩阵,节省了 50257 × 768 ≈ 38.6M 参数
5.2 Logits → 概率
Logits 是未归一化的分数。通过 softmax 变成概率:
logits = [2.1, 5.3, 1.8, 0.2, ..., 3.7] (50257 个数)
probs = softmax(logits)
= [0.002, 0.041, 0.001, 0.000, ..., 0.008]
sum(probs) = 1.0
概率最高的那个 token 就是模型的”最佳猜测”。但实际生成时通常不会直接选最高的——会用温度、Top-p 等策略引入随机性(第 9 章详解)。
六、参数量详算(GPT-2 Small 为例)
1. Token Embedding
W_e: [50257, 768] = 38,597,376
2. 位置 Embedding
W_p: [1024, 768] = 786,432
3. 每个 Transformer 层:
a) Attention:
W_Q: [768, 768] = 589,824
W_K: [768, 768] = 589,824
W_V: [768, 768] = 589,824
W_O: [768, 768] = 589,824
偏置: 768 × 4 = 3,072
Attention 小计: 2,362,368
b) FFN:
W_1: [768, 3072] = 2,359,296
b_1: [3072] = 3,072
W_2: [3072, 768] = 2,359,296
b_2: [768] = 768
FFN 小计: 4,722,432
c) LayerNorm (2 个, 各有 γ 和 β):
768 × 2 × 2 = 3,072
每层总计: 2,362,368 + 4,722,432 + 3,072 = 7,087,872
4. 12 层: 7,087,872 × 12 = 85,054,464
5. 最终 LayerNorm: 768 × 2 = 1,536
6. 输出投影: 复用 W_e (不额外计算)
总计:
38,597,376 + 786,432 + 85,054,464 + 1,536
= 124,439,808
≈ 124M ✓
参数分布:
- Embedding: 31.0%
- Attention (12 层): 22.8%
- FFN (12 层): 45.6%
- 其他: 0.6%
FFN 占了近一半的参数——这就是为什么说”知识存储在 FFN 中”。
七、不同层学到了什么
研究表明,Transformer 的不同层有不同的”职责”:
浅层 (Layer 1-4):
→ 学习基础的语法和词法模式
→ 局部特征: 词性、短语结构
→ 注意力更多关注相邻 token
中间层 (Layer 5-8):
→ 学习语义关系和知识
→ 指代消解、实体识别
→ 注意力模式变得更复杂
深层 (Layer 9-12):
→ 学习高层抽象和任务特定特征
→ 逻辑推理、生成决策
→ 为最终输出做准备
这也是为什么”剪枝”研究发现,删掉中间某些层后影响不大——信息通过残差连接可以绕过被删的层。
本章总结
- 残差连接让梯度有捷径流回去,解决深层网络训练难题
- LayerNorm 归一化每层输入,稳定训练
- Pre-Norm 是现代主流做法(先归一化,再计算)
- 一个 Transformer 层 = Pre-Norm → Attention → 残差 → Pre-Norm → FFN → 残差
- N 个层堆叠 + Embedding + 输出投影 = 完整的 GPT 模型
- Weight Tying 复用 Embedding 矩阵做输出投影,节省参数
- FFN 占了模型近一半的参数,是知识存储的主要场所
下一篇:07. MoE 混合专家模型 — 怎么用更少的计算做更大的模型