注意力机制(Attention Mechanism)¶
注意力机制是从 RNN 时代到 Transformer 时代的关键桥梁。它让模型在处理信息时,能动态聚焦于最相关的部分,而非平等对待所有输入。
1. 为什么需要注意力?¶
回顾 Seq2Seq 的瓶颈:编码器将整个输入序列压缩为一个固定长度的向量 \(\mathbf{c}\),解码器只能依赖这一个向量生成输出。
问题
当输入序列很长时,一个向量根本无法承载所有信息。翻译一句 50 词的长句,解码器到后面已经"忘了"前面的内容。
Attention 的核心思想:解码器在生成每个词时,不只看 \(\mathbf{c}\),而是回头看编码器的所有隐藏状态,并根据相关性分配不同的注意力权重。
类比
你在写英语翻译时,不是先把整句中文背下来再翻译。而是翻译到哪个词,就回头看对应的中文部分——这就是注意力。
2. 基础 Attention(Bahdanau Attention)¶
由 Bahdanau 等人在 2015 年提出,用于改进 Seq2Seq 机器翻译。
计算流程¶
第 1 步:计算注意力分数(Alignment Score)
对于解码器在时间步 \(t\) 的隐藏状态 \(\mathbf{s}_t\) 和编码器的第 \(j\) 个隐藏状态 \(\mathbf{h}_j\):
第 2 步:Softmax 归一化为注意力权重
\(\alpha_{tj}\) 表示在生成第 \(t\) 个输出词时,应该关注输入序列第 \(j\) 个位置的程度。
第 3 步:加权求和得到上下文向量
第 4 步:结合上下文向量进行解码
graph TD
subgraph 编码器隐藏状态
H1[h₁]
H2[h₂]
H3[h₃]
H4[h₄]
end
S[解码器状态 s_t] -->|计算分数| H1
S -->|计算分数| H2
S -->|计算分数| H3
S -->|计算分数| H4
H1 -->|α₁| C[上下文向量 c_t]
H2 -->|α₂| C
H3 -->|α₃| C
H4 -->|α₄| C
C --> OUT[解码输出 y_t]
注意力分数函数¶
不同的打分方式:
| 名称 | 公式 | 特点 |
|---|---|---|
| 加性注意力(Bahdanau) | \(\mathbf{v}^T \tanh(\mathbf{W}_1 \mathbf{s} + \mathbf{W}_2 \mathbf{h})\) | 灵活,参数多 |
| 点积注意力(Luong) | \(\mathbf{s}^T \mathbf{h}\) | 简单高效 |
| 缩放点积注意力 | \(\frac{\mathbf{s}^T \mathbf{h}}{\sqrt{d}}\) | ⭐ Transformer 使用 |
3. Self-Attention(自注意力)¶
基础 Attention 是解码器关注编码器(跨序列)。Self-Attention 则是序列中每个位置关注同一序列中的所有位置,捕捉序列内部的依赖关系。
关键区别
- Attention:一个序列的元素关注另一个序列(如翻译时目标语言关注源语言)
- Self-Attention:同一个序列的元素互相关注(如理解"它"指代前文的哪个名词)
QKV 框架¶
Self-Attention 将每个输入向量 \(\mathbf{x}_i\) 分别映射为三个向量:
- Query(查询):我想找什么信息?
- Key(键):我包含什么信息?
- Value(值):我要传递什么内容?
类比:图书馆检索
- Query = 你的搜索关键词
- Key = 每本书的索引/标题
- Value = 每本书的实际内容
搜索时,你用 Query 和每个 Key 做匹配,找到最相关的几本书(注意力权重),然后阅读它们的内容(Value 的加权和)。
缩放点积注意力¶
其中 \(d_k\) 是 Key 的维度。
为什么要除以 \(\sqrt{d_k}\)?
当 \(d_k\) 很大时,\(\mathbf{Q}\mathbf{K}^T\) 的值会很大,Softmax 之后分布会极度尖锐(一个位置接近 1,其余接近 0),梯度几乎为 0。除以 \(\sqrt{d_k}\) 使方差回到 1,让 Softmax 的输出更平滑。
计算过程图解¶
假设序列长度为 4,每个位置的向量维度为 \(d_k\):
Q (4×d_k) × K^T (d_k×4) = 注意力分数 (4×4)
┌─────┐ ┌──────┐ ┌─────────────┐
│ q₁ │ │k₁ k₂ k₃ k₄│ │ q₁·k₁ q₁·k₂ ...│
│ q₂ │ × │ │ = │ q₂·k₁ q₂·k₂ ...│
│ q₃ │ │ │ │ ... │
│ q₄ │ └──────┘ │ q₄·k₁ ... │
└─────┘ └─────────────┘
→ Softmax → × V (4×d_v) → 输出 (4×d_v)
每一行代表一个位置对所有位置的注意力分布。第 \(i\) 行告诉我们:位置 \(i\) 应该关注序列中哪些位置。
4. 多头注意力(Multi-Head Attention)¶
一个 Self-Attention 只能学习一种"关注模式"。多头注意力让模型同时从多个角度关注信息。
直觉理解
多头注意力就像多个平行的审阅者:
- 头 1 可能关注语法关系(主语-谓语)
- 头 2 可能关注指代关系("它"→"猫")
- 头 3 可能关注位置邻近关系
最后把所有审阅者的意见综合起来。
参数量分析¶
假设模型维度 \(d_{\text{model}} = 512\),头数 \(h = 8\):
- 每个头的维度:\(d_k = d_v = d_{\text{model}} / h = 64\)
- 每个头有 3 个投影矩阵:\(\mathbf{W}^Q, \mathbf{W}^K, \mathbf{W}^V \in \mathbb{R}^{512 \times 64}\)
- 输出投影:\(\mathbf{W}^O \in \mathbb{R}^{512 \times 512}\)
- 总参数量:\(3 \times 8 \times 512 \times 64 + 512 \times 512 = 4 \times 512^2 = 1,048,576\)
计算量和单头 Attention(全维度)相同,但表达能力更强。
5. 位置编码(Positional Encoding)¶
Self-Attention 的计算是排列不变的——打乱输入顺序,输出不会变。但序列的顺序很重要!
"猫 吃 鱼" 和 "鱼 吃 猫" 的 Self-Attention 结果完全一样(如果不加位置信息)。
位置编码给每个位置注入顺序信息。
正弦位置编码(Sinusoidal)¶
原始 Transformer 使用的方案:
- 不同位置的编码向量是唯一的
- 可以泛化到训练时没见过的序列长度
- 相对位置信息可以通过线性变换表达
可学习位置编码¶
直接为每个位置学一个向量(BERT、GPT 系列使用):
- 表达能力更强(不受固定公式限制)
- 但最大序列长度固定(超出训练长度需要外推)
旋转位置编码(RoPE)¶
LLaMA、Qwen 等现代大模型使用的方案:
其中 \(\mathbf{R}_m\) 是一个旋转矩阵,将位置信息编码为向量的旋转角度。
核心优势:位置信息融入 Q 和 K 中,使得 \(\mathbf{q}_m^T \mathbf{k}_n\) 只依赖于相对位置 \(m - n\)。
6. Attention 的掩码(Mask)¶
Padding Mask¶
序列长度不同时需要 padding,padding 位置不应该被关注:
将 padding 位置的注意力分数设为 \(-\infty\),Softmax 后变为 0。
Causal Mask(因果掩码)¶
用于自回归生成(GPT 等),每个位置只能看到自己和之前的位置,不能"偷看"未来:
加到注意力分数上后,再做 Softmax:
7. Attention 的变体与优化¶
标准 Self-Attention 的计算复杂度是 \(O(n^2 d)\),当序列长度 \(n\) 很大时(如 100K tokens),计算量和显存开销都难以承受。
常见优化方法¶
| 方法 | 核心思想 | 复杂度 | 代表 |
|---|---|---|---|
| 标准 Attention | 全量计算 | \(O(n^2)\) | 原始 Transformer |
| 稀疏 Attention | 只关注部分位置(局部 + 全局) | \(O(n\sqrt{n})\) | Longformer、BigBird |
| 线性 Attention | 用核函数近似 Softmax | \(O(n)\) | Performer |
| Flash Attention | IO 优化,减少显存读写 | \(O(n^2)\) 但更快 | ⭐ Flash Attention 2/3 |
| 分组查询注意力 (GQA) | 多个 Q 头共享 K/V | 减少 KV 缓存 | LLaMA 2/3、Gemma |
| 滑动窗口 Attention | 只关注邻近窗口内的 token | \(O(nw)\) | Mistral |
Flash Attention¶
Flash Attention 不改变 Attention 的数学计算,而是优化了 GPU 显存访问模式:
- 避免在高带宽显存(HBM)中存储完整的 \(n \times n\) 注意力矩阵
- 使用分块计算(Tiling):将 Q、K、V 分块,在 SRAM 中完成计算
- 减少 HBM 读写次数,速度提升 2~4 倍,显存降低数倍
GQA(Grouped Query Attention)¶
标准多头注意力中,每个头有独立的 Q、K、V。GQA 让多个 Q 头共享同一组 K/V 头:
| 方法 | Q 头数 | K/V 头数 | 推理 KV 缓存 |
|---|---|---|---|
| MHA | \(h\) | \(h\) | 大 |
| GQA | \(h\) | \(h/g\) | 减少 \(g\) 倍 |
| MQA(极端) | \(h\) | 1 | 最小 |
这在大模型推理中极为重要,因为 KV 缓存是显存占用的主要来源之一。
8. 从 Attention 到 Transformer¶
注意力机制的发展历程:
graph TD
A[Seq2Seq 瓶颈<br>固定长度上下文向量] --> B[Bahdanau Attention 2015<br>解码器关注所有编码器状态]
B --> C[Self-Attention<br>序列内部互相关注]
C --> D[Multi-Head Attention<br>多角度并行关注]
D --> E[Transformer 2017<br>完全基于注意力的架构]
E --> F[BERT / GPT / ViT<br>预训练 + 微调范式]
Transformer 详细内容
Transformer 的完整架构(编码器-解码器、前馈层、层归一化等)请参阅 Transformer。本页重点介绍注意力机制的原理和演进。