1 从 RNN Seq2Seq 到注意力机制
1.1 Encoder-Decoder RNN 的基本形式
机器翻译是理解本讲的核心例子:输入是一个长度为 \(T\) 的源语言序列 \(x_1,\dots,x_T\),输出是一个长度为 \(T'\) 的目标语言序列 \(y_1,\dots,y_{T'}\)。传统 Seq2Seq 模型使用一个 RNN 编码器和一个 RNN 解码器。
编码器逐步读取输入 token,并维护隐藏状态:
\[ h_t = f_W(x_t, h_{t-1}) \]
读完所有输入后,模型通常用最后一个隐藏状态 \(h_T\) 生成解码器初始状态 \(s_0\),并把某个固定长度向量 \(c\) 作为上下文向量(context
vector)。很多早期模型直接令 \(c=h_T\)。解码器再从特殊 token
[START] 开始,一步一步生成输出:
\[ s_t = g_U(y_{t-1}, s_{t-1}, c) \]
例如输入英文 “we see the sky”,解码器可能依次输出意大利语 “vediamo il cielo [STOP]”。
1.2 固定长度上下文向量的瓶颈
Encoder-Decoder RNN 的主要问题是:整段输入序列的信息必须被压缩进一个固定长度向量 \(c\)。当输入很短时这可能可行;但如果 \(T=1000\),最后隐藏状态必须同时保存开头、中间、结尾以及长距离依赖,信息瓶颈会非常严重。
直观地说,翻译某个目标词时,模型并不总是需要整句输入的全部信息。生成 “il” 时最相关的源词可能是 “the”;生成 “cielo” 时最相关的源词可能是 “sky”。因此,更自然的做法是:解码器每一步都回看整段输入,并选择当前最相关的部分。
1.3 RNN Attention 的流程
Bahdanau attention 把固定上下文向量改成每个解码步动态计算的上下文向量。对于第 \(t\) 个解码步,使用上一步解码器状态 \(s_{t-1}\) 和每个编码器隐藏状态 \(h_i\) 计算对齐分数:
\[ e_{t,i} = f_{\text{att}}(s_{t-1}, h_i) \]
然后对这些分数做 softmax,得到注意力权重:
\[ a_{t,i} = \frac{\exp(e_{t,i})}{\sum_j \exp(e_{t,j})}, \quad 0<a_{t,i}<1, \quad \sum_i a_{t,i}=1 \]
最后用注意力权重对所有编码器隐藏状态做加权求和:
\[ c_t = \sum_i a_{t,i}h_i \]
解码器在第 \(t\) 步使用的是 \(c_t\),而不是同一个固定 \(c\):
\[ s_t = g_U(y_{t-1}, s_{t-1}, c_t) \]
关键结论:注意力机制让每个输出位置拥有自己的上下文向量。输入不再被压缩到单个固定向量中,模型可以在不同输出步关注输入序列的不同位置。
1.4 注意力权重的可视化意义
在翻译任务中,可以把 \(a_{t,i}\) 可视化成一个矩阵:横轴是输入词,纵轴是输出词。如果源语言和目标语言词序相近,注意力图常呈现接近对角线的结构;如果词序不同,注意力权重会偏离对角线,反映重排序关系。
注意力图并不等同于严格的语言学对齐,但它提供了一个有用直觉:每个输出 token 不是盲目依赖一个全局状态,而是在当前步从输入序列中抽取相关信息。
2 注意力层的数学形式
2.1 从 RNN Attention 抽象出通用算子
RNN attention 中有两类向量:
| 角色 | 在 RNN 翻译例子中 | 抽象含义 |
|---|---|---|
| 查询向量(Query) | 解码器状态 \(s_t\) | “我现在想找什么信息” |
| 数据向量(Data) | 编码器状态 \(h_i\) | 可被查询的信息集合 |
| 输出向量(Output) | 上下文向量 \(c_t\) | 从数据集合中按相关性混合出的结果 |
因此,注意力可以看成一个作用在向量集合上的通用层:每个 query 都会查看所有 data vectors,并输出一个由 data vectors 加权组合得到的新向量。
2.2 单个 Query 的注意力
设 query 为 \(q\in\mathbb{R}^{D_Q}\),数据向量为 \(X\in\mathbb{R}^{N_X\times D_X}\)。最一般的形式是先用某个函数计算相似度:
\[ e_i = f_{\text{att}}(q, X_i) \]
再做 softmax:
\[ a=\mathrm{softmax}(e) \]
最后输出:
\[ y=\sum_i a_i X_i \]
这个版本的输出维度与数据向量维度相同,都是 \(D_X\)。
2.3 Scaled Dot-Product Attention
现代 Transformer 中通常使用点积相似度,并加入缩放项:
\[ e_i = \frac{q\cdot X_i}{\sqrt{D_X}} \]
缩放的原因是:维度 \(D\) 增大时,点积的数值尺度通常会随 \(\sqrt{D}\) 增大。过大的 logits 会让 softmax 过早饱和,导致梯度变小。除以 \(\sqrt{D}\) 可以让 softmax 的输入尺度更稳定。
对于多个 query,令 \(Q\in\mathbb{R}^{N_Q\times D_X}\),则:
\[ E=\frac{QX^T}{\sqrt{D_X}}\in\mathbb{R}^{N_Q\times N_X} \]
\[ A=\mathrm{softmax}(E) \]
\[ Y=AX \]
其中 \(Y\in\mathbb{R}^{N_Q\times D_X}\),每个 query 得到一个输出向量。
2.4 Key、Value 分离
Transformer 更常用的写法会把数据向量 \(X\) 投影成 key 和 value:
\[ K=XW_K,\quad V=XW_V \]
其中 \(W_K\in\mathbb{R}^{D_X\times D_Q}\),\(W_V\in\mathbb{R}^{D_X\times D_V}\)。注意力计算变为:
\[ E=\frac{QK^T}{\sqrt{D_Q}} \]
\[ A=\mathrm{softmax}(E) \]
\[ Y=AV \]
Key 决定匹配方式,Value 决定被混合的信息内容。 这是一点很容易混淆:相似度不是用 value 算的,而是用 query 和 key 算的;最终输出不是 key 的加权和,而是 value 的加权和。
2.5 Cross-Attention
当 query 和 data 来自不同来源时,这就是交叉注意力(Cross-Attention)。例如在 Encoder-Decoder 结构中,decoder 的状态作为 query,encoder 的隐藏状态作为 data vectors。每个 decoder query 会从 encoder 输出中抽取当前需要的信息。
Cross-attention 的核心特征是:
| 项目 | 来源 |
|---|---|
| \(Q\) | 当前要更新的一组 query 向量 |
| \(K,V\) | 另一组 data 向量 |
| 输出数量 | 与 query 数量相同 |
3 Self-Attention、位置编码与 Masked Attention
3.1 Self-Attention 的定义
自注意力(Self-Attention)是 cross-attention 的特殊情况:query、key、value 都从同一组输入向量 \(X\) 生成。
设输入为 \(X\in\mathbb{R}^{N\times D_{\text{in}}}\),则:
\[ Q=XW_Q,\quad K=XW_K,\quad V=XW_V \]
其中通常 \(Q,K,V\in\mathbb{R}^{N\times D_{\text{out}}}\)。相似度矩阵、注意力权重和输出为:
\[ E=\frac{QK^T}{\sqrt{D_Q}}\in\mathbb{R}^{N\times N} \]
\[ A=\mathrm{softmax}(E) \]
\[ Y=AV \]
每个输入向量都会产生一个输出向量;每个输出向量都是所有 value 的加权和。因此,self-attention 是让同一组 token 彼此交换信息的机制。
实际实现时,\(Q,K,V\) 常被合并成一次矩阵乘法:
\[ [Q\ K\ V]=X[W_Q\ W_K\ W_V] \]
这样可以更好地利用矩阵乘法硬件。
3.2 Self-Attention 的排列等变性
Self-attention 对输入顺序本身没有天然感知。若把输入向量顺序打乱,\(Q,K,V\)、相似度矩阵、注意力权重和输出也会以相同方式被打乱。形式化地说:
\[ F(\sigma(X))=\sigma(F(X)) \]
这叫排列等变性(Permutation Equivariance)。它说明 self-attention 本质上适合处理“向量集合”,而不是天然知道“一维序列的第一个、第二个、第三个位置”。
3.3 位置编码
为了让 self-attention 能处理序列,需要给每个输入 token 加入位置编码(Positional Encoding):
\[ \tilde{x}_i = x_i + p_i \]
其中 \(p_i\) 是与位置 \(i\) 对应的向量,可以是固定函数,也可以是可学习参数。加入位置编码后,即使两个 token 内容相同,只要位置不同,它们进入模型的向量也不同。
在语言中,位置编码告诉模型词序;在图像 ViT 中,位置编码告诉模型 patch 的二维空间位置。没有位置编码,Transformer 很难区分 “dog bites man” 和 “man bites dog” 这类只靠顺序决定语义的情况。
3.4 Masked Self-Attention
语言建模需要预测下一个 token。训练时虽然整句话都已知,但模型在预测第 \(t\) 个位置后面的词时,不能偷看未来 token。Masked self-attention 通过把不允许访问的位置相似度改成 \(-\infty\) 来实现:
\[ E_{i,j}=-\infty \quad \text{if position } j \text{ is not visible to query } i \]
softmax 后这些位置的权重会变成 \(0\)。对于自回归语言模型,常用的是因果 mask(causal mask):每个 token 只能看见自己和它之前的 token。
例子:输入 “Attention is very” 时,预测 “is” 的位置只能看见 “Attention”;预测 “very” 的位置可以看见 “Attention is”;不能提前看见未来答案。
4 Multi-Head Self-Attention 与计算代价
4.1 多头注意力的动机
多头自注意力(Multi-Head Self-Attention)并行运行 \(H\) 组 self-attention,每组叫一个 head。不同 head 有独立的 \(W_Q,W_K,W_V\),因此可以学习不同类型的关系。
一个 head 可能关注局部邻近词,另一个 head 可能关注主谓关系,还有一个 head 可能关注长距离依赖。多头结构给模型提供了多个“关系子空间”,最后再把这些 head 的输出融合。
4.2 多头注意力的形状
设输入 \(X\in\mathbb{R}^{N\times D}\),head 数为 \(H\),每个 head 的维度为 \(D_H\)。通常取:
\[ D_H=\frac{D}{H} \]
这样所有 head 拼接后的总维度仍接近 \(D\)。计算中会得到:
\[ Q,K,V\in\mathbb{R}^{H\times N\times D_H} \]
每个 head 独立计算:
\[ E=\frac{QK^T}{\sqrt{D_H}}\in\mathbb{R}^{H\times N\times N} \]
\[ A=\mathrm{softmax}(E) \]
\[ Y=AV\in\mathbb{R}^{H\times N\times D_H} \]
然后把所有 head 的输出 reshape 成 \(\mathbb{R}^{N\times HD_H}\),再乘输出投影矩阵:
\[ O=YW_O,\quad W_O\in\mathbb{R}^{HD_H\times D} \]
4.3 Self-Attention 是四次主要矩阵乘法
多头 self-attention 的主要计算可以概括为四步:
| 步骤 | 作用 | 典型形状 |
|---|---|---|
| QKV Projection | 从输入一次性生成 \(Q,K,V\) | \([N,D][D,3HD_H]\to[N,3HD_H]\) |
| QK Similarity | 计算每个 query 对每个 key 的相似度 | \([H,N,D_H][H,D_H,N]\to[H,N,N]\) |
| V-Weighting | 用注意力权重加权 value | \([H,N,N][H,N,D_H]\to[H,N,D_H]\) |
| Output Projection | 融合所有 head | \([N,HD_H][HD_H,D]\to[N,D]\) |
这也是 Transformer 高效的重要原因:大部分工作都可以变成大矩阵乘法,非常适合 GPU。
4.4 复杂度与 Flash Attention
标准 self-attention 的注意力矩阵形状为 \(H\times N\times N\),因此计算量随序列长度增长为:
\[ O(N^2) \]
如果显式保存注意力权重,内存也会出现二次增长。课件中的例子给出:当 \(N=100000\)、\(H=64\) 时,注意力权重矩阵会达到 TB 级别,普通 GPU 显然无法直接存储。
Flash Attention 的思想是:在不显式保存完整 \(N\times N\) 注意力矩阵的情况下,分块完成 softmax 和 value 加权。它计算的是精确 attention,但通过 IO-aware 的分块算法显著降低显存占用,使大 \(N\) 的 attention 更可行。
4.5 RNN、卷积、Self-Attention 的对比
| 方法 | 输入结构 | 优点 | 局限 |
|---|---|---|---|
| RNN | 一维有序序列 | 理论上对长度 \(N\) 序列只需 \(O(N)\) 计算和内存,适合逐步累积历史 | 隐状态必须顺序计算,不易并行 |
| 卷积 | \(N\) 维规则网格 | 输出可并行计算,局部结构强 | 长距离依赖需要堆很多层扩大感受野 |
| Self-Attention | 向量集合或 token 序列 | 每个输出可直接依赖所有输入,矩阵乘法高度并行 | 标准形式计算量和注意力矩阵规模约为 \(O(N^2)\) |
5 Transformer Block
5.1 Transformer 的基本组件
Transformer block 接收一组向量 \(x_1,\dots,x_N\),输出同样数量的一组向量 \(y_1,\dots,y_N\)。经典 block 包含:
- Multi-Head Self-Attention
- 残差连接(Residual Connection)
- Layer Normalization
- 对每个 token 独立作用的 MLP / FFN
- 再一次残差连接与 Layer Normalization
Self-attention 是 token 之间唯一的信息交互路径;LayerNorm 和 MLP 都是对每个 token 独立处理的。
5.2 Layer Normalization
LayerNorm 对每个 token 的特征维度做归一化。给定某个 token 的隐藏向量 \(h_i\in\mathbb{R}^D\):
\[ \mu_i=\frac{1}{D}\sum_j h_{i,j} \]
\[ \sigma_i=\sqrt{\frac{1}{D}\sum_j (h_{i,j}-\mu_i)^2} \]
\[ z_i=\frac{h_i-\mu_i}{\sigma_i} \]
\[ y_i=\gamma \odot z_i + \beta \]
其中 \(\gamma,\beta\in\mathbb{R}^D\) 是可学习参数。LayerNorm 与 BatchNorm 不同,它不依赖 batch 维度统计量,因此很适合序列模型和 Transformer。
5.3 MLP / FFN
Transformer block 中的 MLP 通常是两层前馈网络,经典结构为:
\[ D \rightarrow 4D \rightarrow D \]
它对每个 token 独立应用,负责在每个位置内部做非线性特征变换。可以把 self-attention 理解为“token 间通信”,把 MLP 理解为“每个 token 自己内部加工信息”。
5.4 Transformer 是堆叠的 block
一个完整 Transformer 就是很多个相同结构的 block 堆叠起来。课件列举了模型规模增长的例子:
| 模型 | block 数 | \(D\) | head 数 | 上下文长度 \(N\) | 参数量 |
|---|---|---|---|---|---|
| 原始 Transformer | 12 | 1024 | 16 | 512 | 约 213M |
| GPT-2 | 48 | 1600 | 25 | 1024 | 约 1.5B |
| GPT-3 | 96 | 12288 | 96 | 2048 | 约 175B |
Transformer 架构自 2017 年以来主体变化不大,但规模越来越大,训练数据和工程系统也越来越重要。
6 Transformer 在语言建模中的用法
6.1 Token Embedding
语言模型首先学习一个 embedding matrix,把离散 token 转成连续向量。若词表大小为 \(V\),模型维度为 \(D\),embedding matrix 形状为:
\[ [V\times D] \]
每个 token id 对应查表得到一个 \(D\) 维向量。
6.2 因果 Mask 与下一词预测
语言模型使用 masked self-attention,使每个位置只能看到自己及其之前的 token。这样模型可以被训练成预测下一个 token:
\[ p(x_{t+1}\mid x_1,\dots,x_t) \]
训练时,模型末端用一个投影矩阵把每个位置的 \(D\) 维向量投影回词表大小:
\[ [D\times V] \]
得到每个词表 token 的分数,再使用 softmax + cross-entropy loss 训练。
6.3 自回归生成
生成时,模型先读入已有上下文,预测下一个 token 的分布,采样或选择一个 token 后把它追加到上下文,再重复这个过程。因果 mask 保证模型的训练目标和推理过程一致:每一步只基于过去生成未来。
7 Vision Transformer(ViT)
7.1 从图像到 Patch Token
Vision Transformer 把图像转换成 token 序列。以 \(224\times224\times3\) 图像和 \(16\times16\) patch 为例:
- 将图像切成不重叠 patch。
- 每个 patch 的形状为 \(16\times16\times3\),展平后长度为 \(768\)。
- 用线性层把 \(768\) 维 patch 向量投影到 \(D\) 维。
- 得到 \(N\) 个 \(D\) 维 patch token,输入 Transformer。
这个 patch embedding 操作也可以等价理解为一个卷积层:卷积核大小 \(16\times16\),stride 为 \(16\),输入通道为 \(3\),输出通道为 \(D\)。
7.2 ViT 中的位置编码和注意力
图像 patch 本身被整理成 token 集合后,Transformer 并不知道它们在二维图像中的位置。因此 ViT 需要加入位置编码,告诉模型每个 patch 的二维空间位置。
与语言模型不同,ViT 分类通常不使用因果 mask。每个 image patch 可以 attend 到所有其他 patch,因为图像分类任务中不存在“不能看未来”的限制。
7.3 分类输出
Transformer 会为每个 patch 输出一个 \(D\) 维向量。分类时可以:
- 对所有 \(N\times D\) patch 输出做平均池化,得到 \(1\times D\) 表示;
- 再接一个线性层 \(D\to C\),输出 \(C\) 个类别分数。
很多 ViT 变体也会使用额外的 class token,但本讲重点强调的是:图像先被 patch 化,再作为 token 序列交给 Transformer 处理。
8 Transformer 的常见现代改动
8.1 Pre-Norm Transformer
原始 Transformer 中 LayerNorm 位于残差连接外侧。这会带来一个不太理想的问题:残差结构本来希望容易学习恒等映射,但如果归一化放在残差之后,严格的恒等映射会被 LayerNorm 改变。
Pre-Norm 把 LayerNorm 移到 self-attention 和 MLP 之前,也就是残差分支内部:
\[ x_{l+1}=x_l+\mathrm{Block}(\mathrm{Norm}(x_l)) \]
这种结构通常训练更稳定,尤其在层数很深时更明显。
8.2 RMSNorm
RMSNorm(Root-Mean-Square Normalization)用均方根归一化替代 LayerNorm。对输入 \(x\in\mathbb{R}^D\):
\[ y_i=\frac{x_i}{\mathrm{RMS}(x)}\gamma_i \]
其中:
\[ \mathrm{RMS}(x)=\sqrt{\epsilon+\frac{1}{D}\sum_{i=1}^{D}x_i^2} \]
它不像 LayerNorm 那样显式减去均值,形式更简单,很多大模型中也很常见。
8.3 SwiGLU MLP
经典 Transformer MLP 可以写成:
\[ Y=\sigma(XW_1)W_2 \]
其中 \(W_1\in\mathbb{R}^{D\times4D}\),\(W_2\in\mathbb{R}^{4D\times D}\)。
SwiGLU 使用门控形式:
\[ Y=\sigma(XW_1)\odot(XW_2)W_3 \]
其中 \(W_1,W_2\in\mathbb{R}^{D\times H}\),\(W_3\in\mathbb{R}^{H\times D}\)。若设置 \(H=\frac{8D}{3}\),可以让参数量与经典 \(4D\) MLP 大致相当。
直观上,SwiGLU 让 MLP 的一条分支产生门控信号,另一条分支产生候选特征,再逐元素相乘后输出,表达能力通常更强。
8.4 Mixture of Experts(MoE)
Mixture of Experts 在每个 block 中学习 \(E\) 套不同的 MLP 权重,每套 MLP 称为一个 expert。例如:
\[ W_1: [D,4D]\rightarrow[E,D,4D] \]
\[ W_2: [4D,D]\rightarrow[E,4D,D] \]
每个 token 只被路由到其中 \(A<E\) 个 active experts。因此 MoE 可以把参数量扩大约 \(E\) 倍,但计算量只增加到与 active experts 数 \(A\) 相关。
核心意义:MoE 用稀疏激活的方式扩大模型容量,让模型拥有更多参数,但每个 token 只使用一小部分参数,从而控制推理和训练计算量。
9 复习重点与易混点
9.1 必须掌握的核心公式
Scaled dot-product attention:
\[ Q=XW_Q,\quad K=XW_K,\quad V=XW_V \]
\[ E=\frac{QK^T}{\sqrt{D_Q}} \]
\[ A=\mathrm{softmax}(E) \]
\[ Y=AV \]
多头 self-attention:
\[ Q,K,V\in\mathbb{R}^{H\times N\times D_H} \]
\[ A\in\mathbb{R}^{H\times N\times N} \]
\[ O=YW_O \]
9.2 Query、Key、Value 的区别
| 名称 | 作用 | 类比 |
|---|---|---|
| Query | 发起查询,决定“我想找什么” | 搜索请求 |
| Key | 被匹配,决定“我是否相关” | 索引标签 |
| Value | 被加权求和,决定“取回什么内容” | 实际内容 |
考试或面试中常见错误是把 key 和 value 混为一谈。相似度由 \(QK^T\) 决定,输出由 \(AV\) 决定。
9.3 Self-Attention 为什么需要位置编码
Self-attention 对输入顺序是排列等变的;它只看到一组向量,天然不知道哪个 token 在前、哪个 token 在后。位置编码把位置信息加入 token 表示,使模型能够利用顺序或空间布局。
语言模型需要一维位置编码;ViT 需要表达二维 patch 位置。二者目的相同:弥补 self-attention 本身不带顺序偏置的问题。
9.4 Masked Attention 与普通 Self-Attention 的区别
| 类型 | 可见范围 | 典型用途 |
|---|---|---|
| 普通 self-attention | 每个 token 可以看所有 token | 图像分类、编码器、双向表示 |
| masked self-attention | 每个 token 只能看允许的位置,语言模型中通常只能看过去 | 自回归语言建模、下一词预测 |
mask 的实现方式通常是在 softmax 前把不允许的位置设为 \(-\infty\),使其 softmax 权重为 \(0\)。
9.5 Transformer Block 的分工
| 组件 | 是否混合 token 间信息 | 主要作用 |
|---|---|---|
| Self-Attention | 是 | 不同 token 之间通信 |
| MLP / FFN | 否 | 每个 token 内部非线性变换 |
| LayerNorm / RMSNorm | 否 | 稳定数值尺度和训练 |
| Residual Connection | 保留原输入并改善梯度传播 | 让深层网络更容易训练 |
9.6 复杂度重点
标准 self-attention 的主要瓶颈来自注意力矩阵 \(A\in\mathbb{R}^{H\times N\times N}\)。当序列长度 \(N\) 很大时,计算和显存都会迅速增长。Flash Attention 的意义不是改变 attention 的数学定义,而是通过更好的分块计算方式避免保存完整注意力矩阵,从而降低显存压力。
9.7 本讲的主线
本讲可以用一句话串起来:
RNN Seq2Seq 的固定上下文向量造成信息瓶颈,于是引入 attention;attention 被抽象成 Q/K/V 的通用向量集合算子;self-attention 让 token 之间高效并行通信;堆叠 self-attention、MLP、归一化和残差连接,就得到 Transformer;Transformer 再通过 mask、patch embedding、现代归一化和 MoE 等设计扩展到语言、视觉和大规模 AI 模型。