Attention and Transformers⚓︎
约 1941 个字 预计阅读时间 10 分钟
-
注意力(attention):一种在向量集合上操作的原语 (primitive)
-
Transformer:一种在各处使用注意力的神经网络架构
现在 Transformers 已无处不在,但它们都是从 RNNs 的分支发展起来的,所以接下来也将从 RNNs 出发来引出注意力和 Transformer 的介绍。
Attention⚓︎
Sequence to Sequence with RNNs⚓︎
使用 RNNs 解决序列到序列 (seq2seq) 的问题时,我们可以采用一种叫做编码器 - 解码器(encoder-decoder) 的架构。顾名思义,整个网络被分成了两部分:
注:已知输入序列为 \(x_1, \dots, x_T\),输出序列为 \(y_1, \dots, y_T\);并且假设具体要完成的任务是从英文到意文的机器翻译。
-
编码器:\(h_t = f_W(x_t, h_{t-1})\)
根据最后一个隐含状态预测出:
- 初始解码器向量(initial decoder vector) \(s_0\)
- 上下文向量(context vector) \(c\)(通常 \(c = h_T\))
-
解码器:\(s_t = g_U(y_{t-1}, s_{t-1}, c)\)
也许读者发现,输入序列的瓶颈来自固定长度的上下文向量 \(c\)。尽管对上例而言影响不大,但假如 \(T = 1000\) 时该怎么办呢?解决方案是:在输出的每一步中,都要回过头来看整个输入序列——这也正是注意力机制的思路。
Seq2Seq with RNNs and Attention⚓︎
相较于前一版架构,这次我们得好好考虑 \(c\) 是怎么计算出来的。
-
根据初始解码器状态 \(s_0\) 和编码器的隐含状态,计算每个隐含状态对应的对齐分数(alignment score)(标量)
\[ e_{t, i} = f_{att}(s_{t-1}, h_i) \]其中 \(f_{att}\) 作为线性层
-
归一化对齐分数,得到注意权重,即满足 \(0 < a_{t, i} < 1, \sum_i a_{t, i} = 1\)
-
计算上下文向量,它是一个关于隐含状态的加权和
\[ c_t = \sum_i a_{t, i} h_i \] -
在解码器中使用上下文向量:
\[ s_t = g_U(y_{t-1}, s_{t-1}, c_t) \]其中 \(g_U\) 是 RNN 单元(比如 LSTM、GRU 等)
直觉上看,上下文向量关注的是输入序列的相关部分。比如意大利语 "vediamo" = 英语 "we see",所以可能 \(a_{11} = a_{12} = 0.45, a_{13} = a_{14} = 0.05\)。
好消息是:这些步骤都是可微的,这意味着可以用到反向传播!
再回过头来看解码器:
- 使用 \(s_1\) 计算新的上下文向量 \(c_2\)
- 计算新的对齐分数 \(e_{2, i}\) 和注意权重 \(a_{2, i}\)
- 继续使用上下文向量计算新的隐含状态:\(s_t = g_U(y_{t-1}, s_{t-1}, c_t)\)
- 重复上述过程,直到输出 \<STOP> 之类的 token 时停止
由于在解码器的每个时间步中使用了不同的上下文向量,因此:
- 输入序列不会因只有单个向量而受阻
- 每个时间步中,上下文向量会“查看”输入序列的不同部分
仍然回到前面机器翻译的任务,假设:
- 输入:"The agreement on the European Economic Area was signed in August 1992."
- 输出:"L'accord sur la zone économique européenne a été signé en août 1992."
我们可以得到以下关于注意权重的可视化结果:
- 蓝色和紫色部分:对角注意意味着词语按顺序对应
- 黄色部分:注意找到了其他词序(也就是有问题)
回到整体的编码器 - 解码器架构上,我们再来确定一些术语:
- 查询向量(query vector):解码器的 RNN 状态(上图绿色部分)
- 数据向量(data vector):编码器的 RNN 状态(上图蓝色部分)
- 输出向量(output vector):上下文状态(上图黄色部分)
每一个查询会关注所有数据向量,并得到一个输出向量。
Attention Layer⚓︎
我们将上述过程进行更加形式化的表述:
- 输入:
- 查询向量:\(q\)【\(D_Q\)】
- 数据向量:\(X\)【\(N_X \times D_X\)】
- 计算:
- 相似度(similarity):\(e\)【\(N_X\)
】 ,其中 \(e_i = f_{att}(q, X_i)\) - 注意权重:\(a = \text{softmax}(e)\)【\(N_X\)】
- 输出向量:\(y = \sum_i a_i X_i\)【\(D_X\)】
- 相似度(similarity):\(e\)【\(N_X\)
我们要对以上过程做一些改动:
-
计算相似度时使用比例点积,即
\[ e_i = q \cdot X_i / \sqrt{D_X} \]- 之所以要除以 \(\sqrt{D_X}\),是因为相似度太大会导致 softmax 饱和并产生消失的梯度;回想一下 \(a \cdot b = |a||b| \cos(\text{angle})\),假设 \(a, b\) 是维度为 \(D\) 的常量向量,那么 \(|a| = (\sum_i a^2)^{1/2} = a \sqrt{D}\)
-
使用多个查询向量,即 \(Q\)【\(N_Q \times D_X\)
】 ,从而改变了后续的计算- 相似度:\(E = QX^T / \sqrt{D_X}\)【\(N_Q \times N_X\)
】 ,其中 \(E_{ij} = Q_i \cdot X_j / \sqrt{D_X}\) - 注意权重:\(A = \text{softmax}(E, \text{dim}=1)\)【\(N_Q \times N_X\)】
- 输出向量:\(Y = AX\)【\(N_Q \times D_X\)
】 ,其中 \(Y_i \sum_i A_{ij} X_j\)
- 相似度:\(E = QX^T / \sqrt{D_X}\)【\(N_Q \times N_X\)
-
分离键和值
- 键矩阵 \(W_K\)【\(D_X \times D_Q\)
】 ,值矩阵 \(W_V\)【\(D_X \times D_V\)】 - 在数据向量上作用这两个矩阵,可分别得到键(\(K = XW_K\)【\(N_X \times D_Q\)
】 )和值(\(V = XW_V\)【\(N_X \times D_V\)】 ) - 相似度变为基于键的计算:\(E = QK^T / \sqrt{D_Q}\)【\(N_Q \times N_X\)
】 ,其中 \(E_{ij} = Q_i \cdot K_j / \sqrt{D_X}\) - 而输出向量则变为基于值的计算:\(Y = AX\)【\(N_Q \times D_X\)
】 ,其中 \(Y_i \sum_i A_{ij} X_j\)
- 键矩阵 \(W_K\)【\(D_X \times D_Q\)
经过这三个改变后,我们得到了更通用的注意层,如下所示:
- softmax 用来归一化每一列,使得每个列能表示每个查询预测关于键的分布
- 输出是关于值的线性组合,加权来自注意权重
- 每个查询产生一个输出,该输出是一个关于数据向量的混合信息
这个注意层已经跟 RNN 没什么关系了,它是一个独立的神经网络层,可以直接插入到神经网络架构中。
有时这又称为交叉注意层(cross-attention layer),因为它有两组输入(查询向量和数据向量
Self-Attention Layer⚓︎
上述注意层还有一种更常见的变体,叫做自注意层(self-attention layer)。它的不同之处在于只有一个输入来源,即输入向量,而它对应原来注意层中的数据向量。而查询的计算类似键和值的计算,也是让输入向量和一个查询矩阵相乘得到。下面列出修改后的各项参数:
- 输入:
- 输入向量:\(X\)【\(N \times D_{\text{in}}\)】
- 键矩阵:\(W_K\)【\(D_{\text{in}}, D_{\text{out}}\)】
- 值矩阵:\(W_V\)【\(D_{\text{in}}, D_{\text{out}}\)】
- 查询矩阵:\(W_Q\)【\(D_{\text{in}}, D_{\text{out}}\)】
- 计算:
- 查询:\(Q = XW_Q\)【\(N \times D_{\text{out}}\)】
- 键:\(K = XW_K\)【\(N \times D_{\text{out}}\)】
- 值:\(V = XW_V\)【\(N \times D_{\text{out}}\)】
- 相似度:\(E = QK^T / \sqrt{D_Q}\)【\(N \times N\)
】 ,其中 \(E_{ij} = Q_i \cdot K_j / \sqrt{D_Q}\) - 注意权重:\(A = \text{softmax}(E, \text{dim}=1)\)【\(N \times N\)】
- 输出向量:\(Y = AV\)【\(N \times D_{\text{out}}\)
】 ,其中 \(Y_i = \sum_j A_{ij} V_j\)
对应的示意图如下:
- 每个输入产生一个输出,该输出是所有输入的混合信息
-
形状上变得更简单了
- \(N\) 个输入向量,每个都是 \(D_{\text{in}}\) 维的
- 几乎总是满足 \(D_Q = D_V = D_{\text{out}}\)
-
对于每个输入,计算查询、键和值向量
- 通常融合为一个矩阵乘法:\(\begin{bmatrix}Q & K & V\end{bmatrix} = X\begin{bmatrix}W_Q & W_K & W_V\end{bmatrix}\)(\([N \times 3 D_{\text{out}}] = [N \times D_{\text{in}}] [D_{\text{in}} \times 3 D_{\text{out}}]\))
-
对每个键和每个查询,计算相似度
- 归一化每一列,用于表示每个查询预测关于键的分布
- 输出是关于值的线性组合,加权来自注意权重
注意到,调换输入顺序后,查询、键、值、相似度、注意权重和输出的顺序也发生了对应的改变,但始终和输入顺序对应。这说明自注意是排列等变的(permutation equivariant),即 \(F(\sigma(X)) = \sigma(F(X))\),这意味着自注意能在向量集合上正常运作。
自注意的一个问题是无法得知输入序列的顺序。解决方案是在每个输入上加一个位置编码(position encoding),这是一个表示关于索引的固定函数的向量。
Masked Self-Attention Layer⚓︎
Multiheaded Self-Attention Layer⚓︎
Self-Attention == 4 Matrix Multiplication⚓︎
Processing Sequences⚓︎
Transformer⚓︎
Transformers for Language Modeling (LLM)⚓︎
Vision Transformers (ViT)⚓︎
Others⚓︎
评论区













