注意力机制在深度学习模型中无处不在,它有助于提高机器翻译的准确性。在这篇文章中,我们将学习Transformer方法,一个利用注意力来提升模型训练速度的机制。Transformer在特定任务中优于谷歌机器翻译模型。然而,最大的好处得益于Transformer的并行能力。事实上,谷歌云建议使用Cloud TPU提供的Transformer作为参考模型。因此,接下来我们对模型以及其相应模块进行分析。
Transformer是在论文《 Attention is All You Need》中提出的。TensorFlow对应的实现可以使用Tensor2Tensor库提供的接口。在这篇文章中,我们将尝试简化理解,并逐一介绍概念,希望使没有相应基础的同学更容易理解。
首先将模型视为单个黑盒。如下图1所示,在机器翻译应用程序中,输入一种语言的句子,然后输出另一种语言的翻译。
一个典型的Transformer模型如下图2所示,作为seq2seq模型,它也分为编码和译码两个阶段,两个阶段都由一系列Transformer模块堆叠而成,注意这些模块是不共享权重的,以原论文为例则分别为编码和译码各堆叠了6个Transformer模块。
Transformer的编码器和解码器结构如下图3所示,编码器由自注意模块(Self-Attention)和前馈网络(Feed Forward Neural Network, FFNN)组成;解码器则与编码器类似,但在自注意模块和前馈网络中间由插入了一层编解码器注意力模块(也叫Cross-Attention,该模块与前述RNN中的上下文注意力类似)。
具体执行过程:
(1)首先将输入文本转换为词向量,并且融合位置编码,输入给第一个编码器;
(2)第一个编码器,结合上下文,对每个词向量进行编码,分别得到相同数量的向量作为下一个编码器的输入(输出向量的长度是一个可设定的超参);编码器依次连接,对编码阶段最终输出的向量按照注意力机制,分别转换成K向量和V向量,K和V分别表示每个每个单词编码后的键(Key)和值(Value),这两套向量将用于解码阶段的编解码注意力模块;
(3)解码器的工作与编码器类似,首先输入一个代表“起始”的token(记为<s>),然后经过层层解码最后转换为一个分类问题用全连接和softmax输出单词的概率分布;
(4)然后将<s>和第一个单词拼接,作为解码阶段的新输入(与编码阶段的输入类似,需要经过词向量的转换和位置编码的融合),进而输出第二个单词的概率分布……以此类推,直到最终输出代表“结束”的token(记为<eos>)。
首先,对每个词向量(或经过若干次编/解码后得到的特征向量)进行处理,分别产生查询向量(Query)、键向量(Key)和值向量(Value),这三个向量的长度是相同的,并且该长度是可设置的超参。为了产生这三个向量,我们需要模块提供三个可训练的权重,通过权重矩阵与词向量(或特征向量)的乘法得到。
不妨用Python的“字典(Dict)”来类比这三个向量的作用(一种模糊映射关系)
Dict[Key] = Value # Key-Value是实际映射关系中的键值对,
Value_ = Dict[Query] # 用Query去匹配Key以查询Value,
# 在Python的Dict中Query和Key是精确配对的;
# 但在自注意模块中,是用Query去模糊匹配Key并提取出模糊的Value_
当每个词向量(或特征向量)都有了对应的查询向量、键向量和值向量之后,就可以进行如下的自注意力机 制。比如计算第一个词向量(对应单词Thinking)的特征向量:
- 用该单词的查询向量
$q_1$ 与所有单词对应的键向量$q_1,q_2,q_3,...q_n$ 做点乘,分别得到每个单词的查询得分$s_1,s_2,s_3,...s_n$ ; - 用
$\sqrt{d_k}$ 规范化得分($d_k$ 代表查询向量的长度),并将规范化后的得分作为一个向量执行$softmax$ 作为权重; - 利用得到的权重加权求和,即为该词向量对应的输出向量。
对应示例如下图5所示:
将所有输入的词向量堆叠成矩阵,则可以简写成如下的矩阵形式:
前述自注意模块中,对于每个输入向量,只产生一个对应的特征向量,而多头设计即是希望对每个输入向量能产生多个不同的特征向量(与CNN中特征图的“通道”类似)。实现上也不麻烦,只需要设置多套不共享的权重矩阵
(1) 使模型能更好的将注意力放在多个不同的位置上;
(2) 使注意力模块能拥有更复杂的表示子空间(representation subspaces);
为了融合多头提取的特征向量,FFNN也要做出相应的调整,将这些特征向量进行拼接,同时扩大$W^O$权重矩阵的规模。
完整的多头设计的Transformer编码器如下图9所示,
Transformer抛弃了天然具有位置信息的RNN,便丢失了序列本身的位置信息(比如一个单词在句子中的位置),因此需要借助其他手段来加强输入的数据,以引入位置信息。这里Transformer采用的是一种位置编码(Positional Encoding)的技术。用一个与词向量长度相等的向量表示其所在的位置(或次序),并与词向量逐元素相加,从而将位置信息编码到词向量。
论文采用如下的位置编码方案:
其中,
定义中,三角函数的频率沿着维度逐渐减小,波长形成了
该编码方案的优点如下:
-
对于每个时间步,其位置编码向量都是独一无二的;
-
编码向量只取决于时间步的序号,与序列长度无关,能够适用于任意长度的序列;
-
编码的数值是有界的;
-
编码方式是确定的;
-
模型更容易关注到相对位置信息, 因为对于任意偏移量
$k$ ,$PE_{t+k}$ 和$PE_t$ 都满足一定的线性关系,也即存在某个矩阵$T^{(k)}$ 可以使$PE_{t+k}$ =$T^{(k)}\cdot{PE_t}$
与ResNet类似,Transformer也为编码器和解码器引入了残差结构。
无论是自注意模块,还是前馈神经网络、编解码注意力模块,均加入了短连接设计和Normalize,但注意在序列任务中通常是不使用BN的,像论文中就使用了LN来进行规范化。
与CNN等不同,RNN和Transformer在产生一个新的输出时都与先前的输出的有关,因此在产生完整的输出时会有不同的方案。
最简单的方案是贪心解码(Greedy Decoding),也即每次选取概率最高的一项作为新的输出;
复杂点的方案则是束搜索(Beam Search),每次选取概率较高的$k$个候选项,分别假设本次输出是某一候选项来预测下一输出(共