最近想要学习一下注意力机制,发现这又是一个大系列,得慢慢啃。大部分人接触注意力机制是因为 transformer,但在 NLP 领域,该机制早就被 Bahdanau et al., 2014,Luong et al., 2015 等人提出,对应模型称为 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 的输出如下:
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 信息,故称互注意力。
还是以翻译任务为例,假设当前输入是 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 的计算结果。
上面只计算了 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 对应不同转换矩阵。后面的步骤可以简化为一个公式:
[3]进一步强化 self-attention 提出了 multi-headed attention,它在两个方面提升了 self-attention 的能力:
简单来说,有几个 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 时的计算流程:
为了让模型获得序列中各单词的位置信息,transformer 在 embedding 上额外加入了 positional vector,这个向量包含了单词在序列中的位置信息,或者单词之间的距离信息。除此之外,encoder 中每个子层(self-attention,ffnn)之间还增加了残差连接,连接后跟着一步 layer normalization 操作,如下图所示:
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