1 引言

各位朋友大家好,欢迎来到月来客栈。今天要和大家介绍的一篇论文是谷歌2017年所发表的一篇论文,名字叫做”Attention is all you need“[1]。当然,网上已经有了大量的关于这篇论文的解析,不过好菜不怕晚笔者只是在这里谈谈自己对于它的理解以及运用。对于这篇论文,笔者大概会陆续通过7篇文章来进行介绍:①Transformer中多头注意力机制的思想与原理;②Transformer的位置编码与编码解码过程;③Transformer的网络结构与自注意力机制实现;④Transformer的实现过程;⑤基于Transformer的翻译模型;⑥基于Transformer的文本分类模型;⑦基于Transformer的对联生成模型。

希望通过这一系列的7篇文章能够让大家对Transformer有一个比较清楚的认识与理解。下面,就让我们正式走进对于这篇论文的解读中来。公众号后台回复“论文”即可获得下载链接!

2 动机

2.1 面临问题

按照我们一贯解读论文的顺序,首先让我们先一起来看看作者当时为什么要提出Transformer这个模型?需要解决什么样的问题?现在的模型有什么样的缺陷?

在论文的摘要部分作者提到,现在主流的序列模型都是基于复杂的循环神经网络或者是卷积神经网络构造而来的Encoder-Decoder模型,并且就算是目前性能最好的序列模型也都是基于注意力机制下的Encoder-Decoder架构。为什么作者会不停的提及这些传统的Encoder-Decoder模型呢?接着,作者在介绍部分谈到,由于传统的Encoder-Decoder架构在建模过程中,下一个时刻的计算过程会依赖于上一个时刻的输出,而这种固有的属性就限制了传统的Encoder-Decoder模型就不能以并行的方式进行计算,如图1所示。

This inherently sequential nature precludes parallelization within training examples, which becomes critical at longer sequence lengths, as memory constraints limit batching across examples.

图 1. 循环神经网络编码图

 

随后作者谈到,尽管最新的研究工作已经能够使得传统的循环神经网络在计算效率上有了很大的提升,但是本质的问题依旧没有得到解决。

Recent work has achieved significant improvements in computational efficiency through factorization tricks [21] and conditional computation, while also improving model performance in case of the latter. The fundamental constraint of sequential computation, however, remains.

2.2 解决思路

因此,在这篇论文中,作者首次提出了一种全新的Transformer架构来解决这一问题。Transformer架构的优点在于它完全摈弃了传统的循环结构,取而代之的是只通过注意力机制来计算模型输入与输出的隐含表示,而这种注意力的名字就是大名鼎鼎的自注意力机制(self-attention)。

To the best of our knowledge, however, the Transformer is the first transduction model relying entirely on self-attention to compute representations of its input and output without using sequence- aligned RNNs or convolution.

总体来说,所谓自注意力机制就是通过某种运算来直接计算得到句子在编码过程中每个位置上的注意力权重;然后再以权重和的形式来计算得到整个句子的隐含向量表示。最终,Transformer架构就是基于这种的自注意力机制而构建的Encoder-Decoder模型。

3 技术手段

在介绍完整篇论文的提出背景后,下面就让我们一起首先来看一看自注意力机制的庐山真面目,然后再来探究整体的网络架构。

3.1 self-Attention

首先需要明白一点的是,所谓的自注意力机制其实就是论文中所指代的”Scaled Dot-Product Attention“。在论文中作者说道,注意力机制可以描述为将query和一系列的key-value对映射到某个输出的过程,而这个输出的向量就是根据query和key计算得到的权重作用于value上的权重和。

An attention function can be described as mapping a query and a set of key-value pairs to an output, where the query, keys, values, and output are all vectors. The output is computed as a weighted sum of the values, where the weight assigned to each value is computed by a compatibility function of the query with the corresponding key.

不过想要更加深入的理解query、key和value的含义,得需要结合Transformer的解码过程,这部分内容将会在后续进行介绍。 具体的,自注意力机制的结构如图2所示。

图 2. 自注意力机制结构图

从图2可以看出,自注意力机制的核心过程就是通过Q和K计算得到注意力权重;然后再作用于V得到整个权重和输出。具体的,对于输入Q、K和V来说,其输出向量的计算公式为:

其中Q、K和V分别为3个矩阵,且其(第2个)维度分别为 (从后面的计算过程其实可以发现。而公式中除以的过程就是图2中所指的Scale。

之所以要进行缩放这一步是因为通过实验作者发现,对于较大的来说在完成后将会得到很大的值,而这将导致在经过sofrmax操作后产生非常小的梯度,不利于网络的训练。

We suspect that for large values of dk, the dot products grow large in magnitude, pushing the softmax function into regions where it has extremely small gradients.

如果仅仅只是看着图2中的结构以及公式中的计算过程显然是不那么容易理解自注意力机制的含义,例如初学者最困惑的一个问题就是图2中的Q、K和V分别是怎么来的?下面,我们来看一个实际的计算示例。现在,假设输入序列为”我 是 谁“,且已经通过某种方式得到了1个形状为的矩阵来进行表示,那么通过图3所示的过程便能够就算得到Q、K以及V[2]。

图 3. Q、K和V计算过程图

从图3的计算过程可以看出,Q、K和V其实就是输入X分别乘以3个不同的矩阵计算而来(这仅仅局限于Encoder和Decoder在各自输入部分利用自注意力机制进行编码的过程,Encoder和Decoder交互部分的Q、K和V另有指代)。此处对于计算得到的Q、K、V,你可以理解为这是对于同一个输入进行3次不同的线性变换来表示其不同的3种状态。在计算得到Q、K、V之后,就可以进一步计算得到权重向量,计算过程如图4所示。

图 4. 注意力权重计算图(已经经过scale和softmax操作)

如图4所示,在经过上述过程计算得到了这个注意力权重矩阵之后我们不禁就会问到,这些权重值到底表示的是什么呢?对于权重矩阵的第1行来说,0.7表示的就是“我”与”我”的注意力值;0.2表示的就是“我”与”是”的注意力值;0.1表示的就是“我”与”谁”的注意力值。换句话说,在对序列中的”我“进行编码时,应该将0.7的注意力放在”我“上,0.2的注意力放在”是“上,将0.1的注意力放在谁上。

同理,对于权重矩阵的第3行来说,其表示的含义就是,在对序列中”谁“进行编码时,应该将0.2的注意力放在”我“上,将0.1的注意力放在”是“上,将0.7的注意力放在”谁“上。从这一过程可以看出,通过这个权重矩阵模型就能轻松的知道在编码对应位置上的向量时,应该以何种方式将注意力集中到不同的位置上。

不过从上面的计算结果还可以看到一点就是,模型在对当前位置的信息进行编码时,会过度的将注意力集中于自身的位置(虽然这符合常识)而可能忽略了其它位置[2]。因此,作者采取的一种解决方案就是采用多头注意力机制(MultiHeadAttention),这部分内容我们将在稍后看到。

It expands the model’s ability to focus on different positions. Yes, in the example above, z1 contains a little bit of every other encoding, but it could be dominated by the the actual word itself.

在通过图4示的过程计算得到权重矩阵后,便可以将其作用于V ,进而得到最终的编码输出,计算过程如图4所示。

图 5. 权重和编码输出图

根据如图5所示的过程,我们便能够得到最后编码后的输出向量。当然,对于上述过程我们还可以换个角度来进行观察,如图6所示。

图 6. 编码输出计算图

从图6可以看出,对于最终输出”是“的编码向量来说,它其实就是原始”我 是 谁“3个向量的加权和,而这也就体现了在对”是“进行编码时注意力权重分配的全过程。

当然,对于整个图4到图5的过程,我们还可以通过如图7所示的过程来进行表示。

图 7. 自注意力机制计算过程图

可以看出通过这种自注意力机制确实解决了作者在论文伊始所提出的”传统序列模型在编码过程中都需顺序进行的弊端“的问题,有了自注意力机制后,仅仅只需要对原始输入进行几次矩阵变换便能够得到最终包含有不同位置注意力信息的编码向量。

对于自注意力机制的核心部分到这里就介绍完了,不过里面依旧有很多细节之处没有进行介绍。例如Encoder和Decoder在进行交互时的Q、K、V是如何得到的?在图2中所标记的Mask操作是什么意思,什么情况下会用到等等?这些内容将会在后续逐一进行介绍。下面,让我们继续进入到MultiHeadAttention机制的探索中。

3.2 MultiHeadAttention

经过上面内容的介绍,我们算是在一定程度上对于自注意力机制有了清晰的认识,不过在上面我们也提到了自注意力机制的缺陷就是:模型在对当前位置的信息进行编码时,会过度的将注意力集中于自身的位置,因此作者提出了通过多头注意力机制来解决这一问题。同时,使用多头注意力机制还能够给予注意力层的输出包含有不同子空间中的编码表示信息,从而增强模型的表达能力。

Multi-head attention allows the model to jointly attend to information from different representation subspaces at different positions.

在说完为什么需要多头注意力机制以及使用多头注意力机制的好处之后,下面我们就来看一看到底什么是多头注意力机制。

图 8. 多头注意力机制结构图

如图8所示,可以看到所谓的多头注意力机制其实就是将原始的输入序列进行多组的自注意力处理过程;然后再将每一组自注意力的结果拼接起来进行一次线性变换得到最终的输出结果。具体的,其计算公式为:

其中

同时,在论文中,作者使用了个并行的自注意力模块(8个头)来构建一个注意力层,并且对于每个自注意力模块都限定了从这里其实可以发现,论文中所使用的多头注意力机制其实就是将一个大的高维单头拆分成了个多头。因此,整个多头注意力机制的计算过程我们可以通过如图9所示的过程来进行表示。

图 9. 多头注意力机制计算过程图

如图9所示,根据输入序列X和 我们就计算得到了,进一步根据公式就得到了单个自注意力模块的输出;同理,根据X和就得到了另外一个自注意力模块输出。最后,将水平堆叠形成,然后再用乘以便得到了整个多头注意力层的输出。同时,根据图8中的计算过程,还可以得到

到此,对于整个Transformer的核心部分,即多头注意力机制的原理就介绍完了。

4 总结

在本篇文章中,笔者首先介绍了论文的动机,包括传统网络结构所面临的问题以及作者所提出的应对办法;然后介绍了什么是自注意力机制以及其对应的原理;最后介绍了什么是多头注意力机制以及使用多头注意力的好处。同时,对于这部分内容来说,重点需要理解的就是自注意力机制计算原理与过程。在下一篇文章中,笔者将会详细介绍Transformer的位置编码与编码解码过程。

本次内容就到此结束,感谢您的阅读!如果你觉得上述内容对你有所帮助,欢迎分享至一位你的朋友!若有任何疑问与建议,请添加笔者微信'nulls8'或加群进行交流。青山不改,绿水长流,我们月来客栈见!

引用

[1] Vaswani A, Shazeer N, Parmar N, et al. Attention is all you need

[2] The Illustrated Transformer http://jalammar.github.io/illustrated-transformer/

[3] LANGUAGE TRANSLATION WITH TRANSFORMER https://pytorch.org/tutorials/beginner/translation_transformer.html

[4] The Annotated Transformer http://nlp.seas.harvard.edu/2018/04/03/attention.html

[5] SEQUENCE-TO-SEQUENCE MODELING WITH NN.TRANSFORMER AND TORCHTEXT https://pytorch.org/tutorials/beginner/transformer_tutorial.html