引言
最近想要学习一下注意力机制,发现这又是一个大系列,得慢慢啃。大部分人接触注意力机制是因为 transformer,但在 NLP 领域,该机制早就被 Bahdanau et al., 2014,Luong et al., 2015 等人提出,对应模型称为 seq2seq with attention。本文作为注意力机制的入门,将分别介绍这两种模型。
Seq2seq with Attention
sequence to sequence(seq2seq)是一类输入输出为序列数据(例如一句话,由单词组成)的模型,最为熟知的应用是语言翻译。以法语翻译到英语为例,seq2seq 由 encoder 和 decoder 组成,encoder 按顺序一个个读取单词,将整句话的语意信息编码成向量 context 送入 decoder,由 decoder 一个个生成翻译后的单词,其中 encoder 和 decoder 都是 RNN。
实际上 encoder 的每一步有两个输入,当前单词(实际上单词还要通过 word embedding 层获得对应向量)和 hidden state(hs),生成新的 hs,和下一个单词一起作为 encoder 下一步的输入,最后一步生成的 hs 就是上文提到的 context,我们认为 hs 保存了起始到当前位置所有单词的语意信息。decoder 也有自己的 hs,下图没有显式画出来。
当句子很长时,context 保存的语意接近当前单词,和远距离单词联系较弱,这成为了性能瓶颈。对此,人们在 decoder 中增加了 attention 机制,让 decoder 自己选择输入序列最关注的部分,此时的 context 是 encoder 所有时间点的 hs,decoder 在时间点 #t 的输出如下:
- 输入当前单词和 #t-1 hs,丢弃直接输出,保留 #t hs
- 计算 context 中各 hs 的权重 score,并对权重进行 softmax 正则化
- 利用 softmaxed score 对 context 进行加权平均,所得结果与 #t hs 连接起来
- 将连接后的 #t hs 送入前向网络,网络输出即为时间点 t decoder 的最终输出
Transformer
RNN 只能串行计算,当序列很长时,有限的存储容量将限制 batch size 的大小,Transformer 提出了 self-attention 模块,该模块几乎取代了 RNN,实现了序列的并行计算,并大大提升了性能,self-attention 的思想也被引入到计算机视觉领域,在不同任务上取得了突破。transformer 由多层 encoder 和 decoder 组成,其中一层的结构如下图:
假设序列中的每一项称为 item,self-attention 使当前位置 item 能够综合所有位置 item 信息,因为作比较的 item 来自同一个序列,故称自注意力;encoder-decoder attention 使 decoder 中当前位置 item 综合 encoder 中所有位置 item 信息,故称互注意力。
self-attention 计算
还是以翻译任务为例,假设当前输入是 word embedding 结果
x
1
,
x
2
∈
R
d
x_1,x_2 \in \mathbb{R}^d
x1,x2∈Rd,第一步是计算每个输入的 query,key 和 value 向量,它们分别是
x
x
x 和矩阵
W
Q
,
W
K
,
W
V
W^Q,W^K,W^V
WQ,WK,WV 的乘积,矩阵的值通过学习得到。
第二步是计算权重 score。假设要计算
x
1
x_1
x1 的 self-attention,那么需要计算所有
x
i
x_i
xi 相对
x
1
x_1
x1 的权重,权重代表了在 encoding
x
1
x_1
x1 时要对其他
x
i
x_i
xi 给予多少关注。权重是 query 和 key 的点积,对于
x
1
x_1
x1 来说,第一个权重是
q
1
T
k
1
q_1^T k_1
q1Tk1,第二个权重是
q
1
T
k
2
q_1^T k_2
q1Tk2。
第三步是对权重进行 normalization。在[3]中,作者首先将权重除以 d k \sqrt{d_k} dk,然后送入 softmax 函数, d k d_k dk 是 key 向量的维数,作者认为当 d k d_k dk 很大时点积增长过快,导致 softmax 移动到梯度很小的区域,因此除以 d k \sqrt{d_k} dk 以稳定训练。
第四步是加权平均 value。用 softmaxed 权重乘以所有位置的 value,并将它们相加作为
x
1
x_1
x1 self-attention 的计算结果。
self-attention 矩阵计算
上面只计算了
x
1
x_1
x1 的 self-attention,实际上需要计算所有位置
x
i
x_i
xi 的自注意力,相比起重复计算,矩阵计算大大简化了重复过程。此时将所有输入组合成矩阵
X
∈
R
N
×
d
X \in \mathbb{R}^{N \times d}
X∈RN×d,其中 N 是位置数,首先计算
Q
,
K
,
V
Q,K,V
Q,K,V:
可以看到,对于所有位置的输入
x
i
x_i
xi,
W
Q
,
W
K
,
W
V
W^Q,W^K,W^V
WQ,WK,WV 都是相同的,并非不同
x
i
x_i
xi 对应不同转换矩阵。后面的步骤可以简化为一个公式:
Multi-headed attention
[3]进一步强化 self-attention 提出了 multi-headed attention,它在两个方面提升了 self-attention 的能力:
- 提升模型关注不同位置输入的能力,不同 head 可以关注自己感兴趣的位置区域。
- 给予模型更多的表达子空间,例如一个 head 负责边缘特征,另一个 head 负责颜色特征等。
简单来说,有几个 head 就计算几个独立的 self-attention,每个 head 仅仅是
W
Q
,
W
K
,
W
V
W^Q,W^K,W^V
WQ,WK,WV 不同,最后将所有 head 计算的 self-attention 连接在一起,和矩阵
W
O
W^O
WO 相乘获得 multi-headed attention,最终的 attention 包含了所有 head 的信息,下图是 head = 8 时的计算流程:
Positional Encoding & Residuals
为了让模型获得序列中各单词的位置信息,transformer 在 embedding 上额外加入了 positional vector,这个向量包含了单词在序列中的位置信息,或者单词之间的距离信息。除此之外,encoder 中每个子层(self-attention,ffnn)之间还增加了残差连接,连接后跟着一步 layer normalization 操作,如下图所示:
Decoder
decoder 和 encoder 的原理基本相同。在网络结构上,每个 decoder 增加了一个 encoder-decoder attention 子层,计算方式和 multi-headed attention 一致,只不过 key 和 value 来自 encoder,query 来自 decoder,相当于互注意力。在 self-attention 子层中,该子层只允许接触当前及之前位置的单词(毕竟后面的翻译结果还没出来,没有意义),这是通过将除以
d
k
\sqrt{d_k}
dk 改成除以
−
∞
-\infty
−∞ 实现的。整体结构如下:
引用
[1] Mechanics of Seq2seq Models With Attention
[2] The Illustrated Transformer
[3] Attention Is All You Need