Transformer 核心原理 - 现代 LLM 的基石 / Transformer Fundamentals Behind Modern LLMs
📅 创建时间:2026-04-28 🏷️ 标签:#Transformer #Self-Attention #Multi-Head #位置编码 📚 前置知识:[[07 - LLM 进化史]]
📋 本章目标
- 深入理解 Self-Attention 的计算过程
- 掌握 Multi-Head Attention 的原理
- 理解位置编码的设计思路
- 了解 FFN(Feed-Forward Network)的作用
- 理解残差连接和层归一化
- 掌握 Transformer 的完整数据流
第1部分:Self-Attention 详解
1.1 从向量表示说起
词嵌入:将词转换为向量
输入句子:"猫 追 老鼠"
每个词被表示为一个向量(假设维度 d_model = 4):
词 → 向量
────────────────────
猫 → [0.5, 0.2, 0.8, 0.1]
追 → [0.3, 0.7, 0.2, 0.6]
老鼠 → [0.6, 0.1, 0.3, 0.9]
这些向量包含了词的"语义信息"
通过 Word2Vec 或模型学习得到1.2 Q/K/V 向量
Query(查询)、Key(键)、Value(值)
核心思想:
Query:我想要什么信息?
Key:每个位置能提供什么信息?
Value:每个位置的实际信息内容
类比图书馆:
Query:你想要找什么书(主题)
Key:每本书的索引标签
Value:书的实际内容
Attention = 根据 Query 在 Key 中查找,提取对应的 Value数学表示:
对于每个词向量 x,通过线性变换得到 Q/K/V:
Q = x · W_q (Query 矩阵)
K = x · W_k (Key 矩阵)
V = x · W_v (Value 矩阵)
W_q, W_k, W_v 是可学习的参数具体数值示例:
假设:
词向量 x_猫 = [0.5, 0.2, 0.8, 0.1] (4维)
权重矩阵 W_q = [[0.1, 0.2], (4×2矩阵)
[0.3, 0.4],
[0.5, 0.6],
[0.7, 0.8]]
计算 Q_猫 = x_猫 · W_q:
Q_猫[0] = 0.5×0.1 + 0.2×0.3 + 0.8×0.5 + 0.1×0.7
= 0.05 + 0.06 + 0.40 + 0.07
= 0.58
Q_猫[1] = 0.5×0.2 + 0.2×0.4 + 0.8×0.6 + 0.1×0.8
= 0.10 + 0.08 + 0.48 + 0.08
= 0.74
Q_猫 = [0.58, 0.74] (2维 Query)1.3 注意力分数计算
步骤1:计算 Q 和 K 的点积
对于句子 "猫 追 老鼠",假设 d_k = 2:
Q 矩阵(每个词的 Query):
Q_猫 = [0.58, 0.74]
Q_追 = [0.62, 0.81]
Q_老鼠 = [0.45, 0.67]
K 矩阵(每个词的 Key):
K_猫 = [0.71, 0.52]
K_追 = [0.83, 0.69]
K_老鼠 = [0.55, 0.78]
计算注意力分数:score = Q · K^T
score(猫, 猫) = Q_猫 · K_猫 = 0.58×0.71 + 0.74×0.52 = 0.78
score(猫, 追) = Q_猫 · K_追 = 0.58×0.83 + 0.74×0.69 = 0.97
score(猫, 老鼠) = Q_猫 · K_老鼠 = 0.58×0.55 + 0.74×0.78 = 0.96步骤2:除以 √d_k
为什么要除以 √d_k?
原因:防止点积值过大,导致 softmax 后梯度太小
d_k = 2
√d_k = 1.41
归一化后:
score(猫, 猫) = 0.78 / 1.41 = 0.55
score(猫, 追) = 0.97 / 1.41 = 0.69
score(猫, 老鼠) = 0.96 / 1.41 = 0.68步骤3:Softmax 得到注意力权重
对每行进行 softmax:
猫 的注意力权重:
exp(0.55) = 1.73
exp(0.69) = 1.99
exp(0.68) = 1.97
总和 = 1.73 + 1.99 + 1.97 = 5.69
归一化:
attention(猫, 猫) = 1.73 / 5.69 = 0.30
attention(猫, 追) = 1.99 / 5.69 = 0.35
attention(猫, 老鼠) = 1.97 / 5.69 = 0.35
解释:对于"猫"这个词,它 30% 关注自己,35% 关注"追",35% 关注"老鼠"1.4 最终输出计算
步骤4:用注意力权重对 Value 加权求和
V 矩阵(每个词的 Value):
V_猫 = [0.52, 0.61]
V_追 = [0.78, 0.43]
V_老鼠 = [0.63, 0.72]
对于"猫"的输出:
output_猫 = 0.30 × V_猫 + 0.35 × V_追 + 0.35 × V_老鼠
output_猫[0] = 0.30×0.52 + 0.35×0.78 + 0.35×0.63
= 0.156 + 0.273 + 0.221
= 0.650
output_猫[1] = 0.30×0.61 + 0.35×0.43 + 0.35×0.72
= 0.183 + 0.151 + 0.252
= 0.586
output_猫 = [0.650, 0.586]
"猫"的表示现在包含了它与所有词的关系信息!1.5 Self-Attention 的完整矩阵形式
┌─────────────────────────────────────────────────────────────┐
│ Self-Attention 矩阵计算 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 输入:X(序列的词向量矩阵) │
│ │
│ 1. X → Q = X · W_q │
│ 2. X → K = X · W_k │
│ 3. X → V = X · W_v │
│ │
│ 4. Attention = softmax(Q · K^T / √d_k) · V │
│ │
│ 其中: │
│ • Q, K, V 都是矩阵(一次计算所有词) │
│ • Q · K^T 是所有位置对的点积 │
│ • softmax 按行进行 │
│ • 最后乘以 V 得到加权求和结果 │
│ │
└─────────────────────────────────────────────────────────────┘第2部分:Multi-Head Attention
2.1 为什么需要多头?
单头 Attention 的局限:
单头只能学习一种类型的注意力模式
例如:只能学习"主语-动词"关系
但实际上,句子中有很多种关系:
• 主语-动词(谁做了什么)
• 形容词-名词(什么样的)
• 代词-指代(代词指谁)
• 全局语义相似性多头 Attention:
多个注意力头并行
每个头学习不同类型的关系
head_1 = 关注 主语-动词
head_2 = 关注 形容词-名词
head_3 = 关注 代词指代
...
最终输出 = concat(head_1, head_2, ...) · W_o2.2 多头的数值示例
假设:
- d_model = 4(词向量维度)
- num_heads = 2(2个头)
- d_k = d_v = 2(每个头的维度)
每个头独立计算 Attention:
Head_1(关注语义相似):
Q₁ = X · W_q1
K₁ = X · W_k1
V₁ = X · W_v1
Attention₁ = softmax(Q₁ · K₁^T / √2) · V₁
Head_2(关注位置关系):
Q₂ = X · W_q2
K₂ = X · W_k2
V₂ = X · W_v2
Attention₂ = softmax(Q₂ · K₂^T / √2) · V₂
最终输出:
MultiHead = concat(Attention₁, Attention₂) · W_o
= [head_1; head_2] · W_o2.3 多头注意力图示
┌─────────────────────────────────────────────────────────────┐
│ Multi-Head Attention │
├─────────────────────────────────────────────────────────────┤
│ │
│ 输入 X (d_model) │
│ ↓ │
│ ┌─────────────────────────────────────────────┐ │
│ │ 并行计算 h 个 Attention │ │
│ │ │ │
│ │ Head_1 ──→ Attention₁ ──┐ │ │
│ │ Head_2 ──→ Attention₂ ──┼──→ concat ──→ W_o │
│ │ ... ──→ ... ──┤ │ │
│ │ Head_h ──→ Attention_h ──┘ │ │
│ │ │ │
│ │ (每个头有独立的 W_q, W_k, W_v) │ │
│ └─────────────────────────────────────────────┘ │
│ ↓ │
│ 输出 (d_model) │
│ │
└─────────────────────────────────────────────────────────────┘2.4 为什么多头有效?
类比理解:
单头 = 只戴一副眼镜(只能看一种颜色)
多头 = 戴多副不同颜色的眼镜(同时看到多种信息)
技术原因:
1. 每个头学习不同的注意力模式
2. 不同的头关注句子的不同方面
3. 多个头的组合提供更丰富的表示
4. 某些头可以冗余备份,提高鲁棒性
实验观察:
• 不同的头确实学习了不同的语义关系
• 某些头在训练中被"淘汰"
• 某些头专门处理特定类型的依赖第3部分:位置编码
3.1 为什么需要位置编码?
Attention 本身是"位置无关"的
Self-Attention 的计算:
output = softmax(Q · K^T / √d) · V
这里 Q·K^T 只考虑语义相似性
不包含位置信息!
结果:
"猫 追 老鼠" 和 "老鼠 追 猫" 的 Attention 结果相同!
(因为只是交换了位置)位置编码的解决方案:
为每个位置添加一个独特的"位置向量"
位置向量 + 词向量 = 带位置信息的完整表示
"猫" 在位置 0:word_vec[猫] + pos_vec[0]
"追" 在位置 1:word_vec[追] + pos_vec[1]
"老鼠" 在位置 2:word_vec[老鼠] + pos_vec[2]3.2 Sinusoidal 位置编码
论文原文中使用的编码方式:
PE(pos, 2i) = sin(pos / 10000^(2i/d_model))
PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))
其中:
- pos = 位置(0, 1, 2, ...)
- i = 维度索引(0, 1, 2, ..., d_model/2)
- d_model = 位置编码的总维度具体数值示例:
假设 d_model = 4,则 i = 0, 1
位置 0(pos = 0):
PE(0, 0) = sin(0 / 10000^0) = sin(0) = 0
PE(0, 1) = cos(0 / 10000^0) = cos(0) = 1
PE(0, 2) = sin(0 / 10000^1) = sin(0) = 0
PE(0, 3) = cos(0 / 10000^1) = cos(0) = 1
位置向量 PE(0) = [0, 1, 0, 1]
位置 1(pos = 1):
PE(1, 0) = sin(1 / 10000^0) = sin(1) ≈ 0.84
PE(1, 1) = cos(1 / 10000^0) = cos(1) ≈ 0.54
PE(1, 2) = sin(1 / 10000^1) = sin(0.0001) ≈ 0.0001
PE(1, 3) = cos(1 / 10000^1) = cos(0.0001) ≈ 1.0
位置向量 PE(1) = [0.84, 0.54, 0.0001, 1.0]
位置 2(pos = 2):
PE(2, 0) = sin(2) ≈ 0.91
PE(2, 1) = cos(2) ≈ -0.42
...3.3 位置编码的特性
特性1:每个位置有独特编码
PE(0) = [0, 1, 0, 1]
PE(1) = [0.84, 0.54, 0.0001, 1.0]
PE(2) = [0.91, -0.42, 0.0002, 1.0]
任意两个位置都不相同!特性2:相对位置可以通过线性变换得到
PE(pos + k) 可以表示为 PE(pos) 的线性组合
这意味着:
模型可以从绝对位置推断相对位置
这对学习位置关系很重要!特性3:可以推广到任意长度
Sinusoidal 函数是周期函数
可以处理比训练时更长的序列
对于长位置,函数值仍然有定义
虽然可能不那么精确3.4 位置编码图示
┌─────────────────────────────────────────────────────────────┐
│ 位置编码示意图 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 位置 0: ▓░▓░▓░▓░ 波形 │
│ 位置 1: ▓░▓░▓░▓░▓░ │
│ 位置 2:░▓░▓░▓░▓░▓ │
│ ... │
│ │
│ 竖直看下去,每个位置有独特的"指纹" │
│ │
│ ┌──────┬──────┬──────┬──────┐ │
│ │ pos=0│ pos=1│ pos=2│ pos=3│ │
│ ├──────┼──────┼──────┼──────┤ │
│ │ [0,1,│[0.84,│[0.91,│[-0.76│ │
│ │ 0,1]│0.54, │-0.42,│ 0.65,│ │
│ │ │0.0001│0.0002│0.0003│ │
│ │ │ 1.0]│ 1.0]│ 1.0]│ │
│ └──────┴──────┴──────┴──────┘ │
│ │
└─────────────────────────────────────────────────────────────┘第4部分:前馈神经网络(FFN)
4.1 FFN 在 Transformer 中的位置
┌─────────────────────────────────────────────────────────────┐
│ Transformer Block 结构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 输入 │
│ ↓ │
│ Multi-Head Self-Attention ← 捕捉序列内关系 │
│ ↓ │
│ Add & LayerNorm │
│ ↓ │
│ Feed-Forward Network ← 处理每个位置的"思考" │
│ ↓ │
│ Add & LayerNorm │
│ ↓ │
│ 输出 │
│ │
└─────────────────────────────────────────────────────────────┘4.2 FFN 的结构
FFN = 两层全连接网络 + 激活函数
FFN(x) = max(0, x · W₁ + b₁) · W₂ + b₂
或者使用 GELU:
FFN(x) = GELU(x · W₁ + b₁) · W₂ + b₂
结构:
输入(d_model) → 线性变换(W₁) → 隐藏层(d_ff = 4×d_model) → 激活 → 线性变换(W₂) → 输出(d_model)具体数值示例:
假设:
- d_model = 4
- d_ff = 16(4倍扩展)
输入 x = [0.5, 0.2, 0.8, 0.1]
W₁ 是 4×16 的矩阵,b₁ 是 16 维向量
W₂ 是 16×4 的矩阵,b₂ 是 4 维向量
hidden_pre = x · W₁ + b₁
= [0.5, 0.2, 0.8, 0.1] · W₁ + b₁
= [0.65, -0.3, 0.45, ...16个值...]
hidden = GELU(hidden_pre) # 非线性激活
output = hidden · W₂ + b₂
= [0.65, 0, 0.45, ...] · W₂ + b₂
= [0.7, 0.1, 0.6, 0.2] # 回到 d_model 维度4.3 FFN 的作用
Attention 之后为什么要加 FFN?
Attention 的作用:
- 整合其他位置的信息
- 每个位置都"看到"了整个序列
FFN 的作用:
- 对每个位置进行独立的非线性变换
- 增加模型的表达能力
- 可以看作是对"聚合后信息"的进一步处理
类比:
Attention = 开会讨论,收集大家意见
FFN = 会后独立思考,把讨论结果内化为自己的理解为什么隐藏层要扩大 4 倍?
Transformer 原始论文建议 d_ff = 4 × d_model
经验公式,经大量实验验证
作用:
- 提供足够的"思考空间"
- 太小:表达能力不足
- 太大:计算成本高,容易过拟合
例如 BERT-base:
d_model = 768
d_ff = 3072 = 768 × 4第5部分:残差连接与层归一化
5.1 残差连接(Skip Connection)
问题:深层网络训练困难
网络太深时:
- 梯度消失/爆炸
- 训练困难
- 退化问题(层数增加,准确率反而下降)
解决方案:残差连接残差连接的原理:
普通连接:
输入 x → [网络层] → 输出 F(x)
残差连接:
输入 x → → → → → → → ↓
[网络层]
↓
输出 F(x) + x
输出 = 主路径输出 + 跳接输入具体计算:
假设:
- 输入 x = [0.5, 0.2, 0.8, 0.1]
- 主路径变换 F(x) = [0.7, 0.3, 0.6, 0.2]
残差输出 = F(x) + x
= [0.7, 0.3, 0.6, 0.2] + [0.5, 0.2, 0.8, 0.1]
= [1.2, 0.5, 1.4, 0.3]
好处:
- 即使 F(x) = 0(主路径失效),输出也是 x(至少不会变差)
- 梯度可以直接传回输入端
- 训练更稳定5.2 层归一化(Layer Normalization)
问题:每层输出的数值范围可能差异很大
不同层的输出:
Layer 1: [0.1, 0.2, 0.3] 小范围
Layer 2: [50, 60, 70] 大范围
Layer 3: [0.01, 0.02, 0.03] 又小了
这种不一致会影响训练稳定性层归一化的公式:
给定输入 x = [x₁, x₂, ..., x_d]
计算:
mean = (x₁ + x₂ + ... + x_d) / d
variance = ((x₁-mean)² + ... + (x_d-mean)²) / d
std = √variance
归一化:
y_i = (x_i - mean) / (std + ε)
可学习的缩放和偏移:
output = γ * y + β
ε 是极小值,防止除零
γ 和 β 是可学习参数具体数值示例:
输入 x = [2.0, 4.0, 6.0, 8.0]
mean = (2 + 4 + 6 + 8) / 4 = 5.0
variance = ((2-5)² + (4-5)² + (6-5)² + (8-5)²) / 4
= (9 + 1 + 1 + 9) / 4 = 20 / 4 = 5.0
std = √5.0 ≈ 2.24
归一化:
y = [(2-5)/2.24, (4-5)/2.24, (6-5)/2.24, (8-5)/2.24]
= [-1.34, -0.45, 0.45, 1.34]
输出范围被标准化了!5.3 Add & LayerNorm 组合
┌─────────────────────────────────────────────────────────────┐
│ Add & LayerNorm │
├─────────────────────────────────────────────────────────────┤
│ │
│ 输入 x │
│ ↓ │
│ ┌───────────────────────┐ │
│ │ 残差连接: │ │
│ │ x ──→ [子层] ──→ F(x)│ │
│ │ ↓ │ │
│ │ + │ │
│ │ ↓ │ │
│ │ F(x) + x ──────────→│ │
│ └───────────────────────┘ │
│ ↓ │
│ ┌───────────────────────┐ │
│ │ LayerNorm: │ │
│ │ (F(x) + x - mean)/std│ │
│ └───────────────────────┘ │
│ ↓ │
│ 输出 │
│ │
└─────────────────────────────────────────────────────────────┘第6部分:Transformer 完整架构
6.1 编码器结构
┌─────────────────────────────────────────────────────────────┐
│ 单个编码器层 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 输入 (词向量 + 位置编码) │
│ ↓ │
│ Multi-Head Self-Attention │
│ ↓ │
│ Add & LayerNorm │
│ ↓ │
│ Feed-Forward Network │
│ ↓ │
│ Add & LayerNorm │
│ ↓ │
│ 输出 │
│ │
│ 特点:每个位置可以看到所有其他位置 │
│ │
└─────────────────────────────────────────────────────────────┘
编码器堆叠 N 层(原始论文 N=6)6.2 解码器结构
┌─────────────────────────────────────────────────────────────┐
│ 单个解码器层 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 输入(已生成的词 + 位置编码) │
│ ↓ │
│ Masked Multi-Head Self-Attention ← 不能看到未来 │
│ ↓ │
│ Add & LayerNorm │
│ ↓ │
│ Multi-Head Cross-Attention ← 关注编码器的输出 │
│ ↓ │
│ Add & LayerNorm │
│ ↓ │
│ Feed-Forward Network │
│ ↓ │
│ Add & LayerNorm │
│ ↓ │
│ 输出 │
│ │
└─────────────────────────────────────────────────────────────┘
解码器堆叠 N 层(原始论文 N=6)6.3 Masked Attention 的作用
为什么需要 Mask?
问题:解码器不应该"看到"未来的词
例如生成 "我爱AI":
位置 1: "我" → 只能看自己
位置 2: "爱" → 只能看"我"和自己
位置 3: "A" → 只能看"我爱"和自己
位置 4: "I" → 只能看"我爱AI"和自己
不能提前知道答案!Mask 的实现:
对于位置 i,把位置 i+1, i+2, ... 的注意力分数设为 -∞
softmax 后这些位置的影响变成 0
示例(4个位置的 Attention):
原始注意力分数:
位置0 位置1 位置2 位置3
位置0 0.5 0.3 0.1 0.1
位置1 0.2 0.6 0.1 0.1
位置2 0.1 0.2 0.5 0.2
位置3 0.1 0.1 0.2 0.6
添加 Mask(位置2不能看位置3):
位置0 位置1 位置2 位置3
位置0 0.5 0.3 0.1 0.1
位置1 0.2 0.6 0.1 0.1
位置2 0.1 0.2 0.5 -∞
位置3 -∞ -∞ -∞ -∞
softmax 后:
位置2: [0.25, 0.38, 0.37, 0] ← 不包含位置3
位置3: [0, 0, 0, 1] ← 只看自己第7部分:完整数据流
7.1 从输入到输出
┌─────────────────────────────────────────────────────────────────────────────┐
│ Transformer 完整数据流 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 输入:"I love AI" │
│ │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ 步骤1:词嵌入 + 位置编码 │ │
│ │ │ │
│ │ "I" → Embedding + Pos Encoding → [e₁] │ │
│ │ "love" → Embedding + Pos Encoding → [e₂] │ │
│ │ "AI" → Embedding + Pos Encoding → [e₃] │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ 步骤2:编码器层堆叠(×6) │ │
│ │ │ │
│ │ 编码器层1: [e₁,e₂,e₃] → MultiHead → FFN → [h₁,h₂,h₃] │ │
│ │ 编码器层2: [h₁,h₂,h₃] → MultiHead → FFN → [g₁,g₂,g₃] │ │
│ │ ... │ │
│ │ 编码器层6: [x₁,x₂,x₃] → [z₁,z₂,z₃] ← 编码器最终输出 │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ 步骤3:解码器自回归生成 │ │
│ │ │ │
│ │ 生成"<start>" │ │
│ │ ↓ │ │
│ │ 生成"我爱" → 关注编码器输出 [z₁,z₂,z₃] + 解码器历史 │ │
│ │ ↓ │ │
│ │ 生成"<end>" │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ ↓ │
│ 输出:翻译结果 │
│ │
└─────────────────────────────────────────────────────────────────────────────┘7.2 自回归生成过程
┌─────────────────────────────────────────────────────────────┐
│ 自回归生成示例 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 目标:将 "I love AI" 翻译成 "我爱AI" │
│ │
│ 步骤1:输入 "<start>" │
│ → 解码器输出 "我" │
│ │
│ 步骤2:输入 "<start> 我" │
│ → 解码器输出 "爱" │
│ │
│ 步骤3:输入 "<start> 我 爱" │
│ → 解码器输出 "AI" │
│ │
│ 步骤4:输入 "<start> 我 爱 AI" │
│ → 解码器输出 "<end>" │
│ │
│ 完成!输出:"我爱AI" │
│ │
└─────────────────────────────────────────────────────────────┘核心总结
总结1:Self-Attention 的本质
Attention(Q, K, V) = softmax(Q · K^T / √d_k) · V
本质:
- Query:我要找什么
- Key:我能提供什么
- Value:我的实际内容
计算:Query 和 Key 的相似度 → 权重 → 加权 Value总结2:多头注意力的意义
多头的价值:
- 每个头学习不同的注意力模式
- 有的关注语法,有的关注语义
- 组合提供更丰富的表示
公式:MultiHead = concat(head₁, ..., head_h) · W_o总结3:位置编码的设计
目的:注入位置信息(Attention 本身位置无关)
方法:Sinusoidal 编码
PE(pos, 2i) = sin(pos / 10000^(2i/d))
PE(pos, 2i+1) = cos(pos / 10000^(2i/d))
特点:
- 每个位置独特
- 可表示相对位置
- 可推广到更长序列总结4:Transformer 组件作用
| 组件 | 作用 |
|---|---|
| Self-Attention | 捕捉序列内长距离依赖 |
| Multi-Head | 多角度理解 |
| FFN | 非线性变换,增强表达 |
| 残差连接 | 梯度流动,稳定训练 |
| LayerNorm | 稳定数值范围 |
| 位置编码 | 注入位置信息 |
章节测试
测试1:Q/K/V 的作用
在 Self-Attention 中,Query、Key、Value 各自的作用是什么?
测试2:为什么要除以 √d_k
在计算注意力分数时,为什么要除以 √d_k?
测试3:多头注意力
Multi-Head Attention 比单头 Attention 有什么优势?
测试4:位置编码
为什么 Transformer 需要位置编码,而 RNN 不需要?
测试5:Mask 的作用
在解码器的 Masked Attention 中,Mask 的作用是什么?
参考答案
测试1答案
答案:
- Query(Q):当前位置的"查询",表示当前位置想要获取什么信息
- Key(K):每个位置的"索引",表示该位置能提供什么信息
- Value(V):每个位置的"内容",表示该位置的实际信息
解析:
类比图书馆系统:
- Query:你想搜索的主题
- Key:每本书的索引标签
- Value:书的实际内容
Attention = 用 Query 在 Key 中搜索,找到匹配的内容(Value)测试2答案
答案:防止点积值过大,导致 softmax 后梯度太小
解析:
问题来源:
- Q 和 K 的维度是 d_k
- 点积 Q·K^T 的值范围与 √d_k 成正比
- 当 d_k 很大时,点积值会很大
具体影响:
假设 Q, K 是均值为0、方差为1的随机向量:
E[Q·K^T] = d_k
std(Q·K^T) ≈ √(2d_k)
当 d_k = 64 时,点积最大值可达 ~30
经过 softmax:
softmax([30, 0, 0, ...]) ≈ [1, 0, 0, ...]
梯度接近 0,无法学习!
解决方案:除以 √d_k
softmax([30/8, 0, 0, ...]) = softmax([3.75, 0, 0, ...])
仍然很大但更可控
或者除以 √(2d_k),方差归一化测试3答案
答案:
- 每个头可以学习不同类型的注意力模式
- 提供更丰富的语义表示
- 增加模型的鲁棒性(某些头可以备份)
解析:
单头的问题:
- 只能学习一种类型的注意力关系
- 表达能力有限
多头的优势:
- Head 1:学习主语-动词关系
- Head 2:学习形容词-名词关系
- Head 3:学习语义相似性
- Head 4:学习位置邻近性
- ...
最终输出 = concat(所有head) · W_o
- 维度不变,但信息更丰富
- 每个头互补,提供多角度理解测试4答案
答案:因为 Attention 机制本身是位置无关的
解析:
Transformer 的 Self-Attention:
output = softmax(Q · K^T / √d) · V
这里的计算:
- Q·K^T 只考虑语义相似性
- 不包含任何位置信息
- "猫 追 老鼠" 和 "老鼠 追 猫" 结果相同!
RNN 的特点:
- 顺序处理,每个位置依次计算
- 时间步天然包含位置信息
- "词1 → 词2 → 词3" 本身就代表了顺序
Transformer 需要位置编码:
- Attention 是"全连接"的,不包含顺序
- 必须显式添加位置信息
- 位置编码让模型知道词的相对/绝对位置测试5答案
答案:防止解码器在生成当前位置时"看到"未来的词
解析:
自回归生成的问题:
- 生成第3个词时,不应该知道第4、5个词是什么
- 否则模型可能"偷看答案"
Mask 的作用:
- 把位置 i+1, i+2, ... 的注意力分数设为 -∞
- softmax 后这些位置的权重变成 0
- 当前位置只能关注自己和之前的词
示例(生成第3个词):
- 应该看:位置0, 位置1, 位置2
- 不应该看:位置3, 位置4, ...
没有 Mask:泄漏未来信息
有 Mask:严格自回归,训练目标正确相关笔记
- [[01 - 神经网络基础]] - MLP、激活函数的理解
- [[07 - LLM 进化史]] - Transformer 的历史背景
- [[03 - 解码策略]] - Transformer 的解码过程
下一步学习
- [ ] 完成 LLM 前置知识模块
- [ ] 前往 Agent 基础 开始学习
学习状态:✅ 已完成
恭喜你完成了 LLM 前置知识模块的学习!