Transformer论文解析及模型理解

0.前言

本文为一个深度学习研一初学者对于论文《Attention Is All You Need》的部分内容翻译和个人理解,理解过程中结合了B站李沐老师的论文精读以及台大李宏毅老师的课程,如有理解上的错误,欢迎各位大佬批评指正,讨论交流。以下是相关参考链接:
李沐老师论文精读:https://www.bilibili.com/video/BV1pu411o7BE/?spm_id_from=333.999.0.0&vd_source=91bd39877c449d1e919867a8e30d2058
李宏毅老师课程:https://www.bilibili.com/video/BV1wB4y1o7is/?spm_id_from=333.337.search-card.all.click
文中涉及到Q、K、V生成过程的部分图片来源于知乎PP鲁大佬的文章:https://zhuanlan.zhihu.com/p/414084879

1.摘要

transformer模型的并行化更好、全靠注意力机制。采用三个注意力机制模块完成之前循环层完成的任务,一开始是在机器翻译领域实现突破,后面被BERT、GPT给带火,应用在了计算机视觉领域上。

2.Introduction

RNN

介绍了RNN的特点,同时也是他的缺点。在RNN里面,给出一个序列的话,他的计算是把这个序列从左往右移一步一步往前做。假设你的序列是一个句子的话,他就是一个一个词的看。

对第t个词。他会计算一个输出叫ht,也叫它的隐藏状态。然后它的h_t是由前面一个词的隐藏状态ht-1和当前第t个词本身决定的。这样他就可以把前面学到的历史信息呢,通过ht-1放到当下,然后和当前的词做一些计算,然后得到输出,这也是RNN如何能够有效处理时序信息的一个关键之所在,他把之前的信息全部放在隐藏状态里面,然后一个一个放下去。但RNN的问题也来自于这里,第一个就是它是时序,就是一步一步计算的过程,它比较难以并行。在算第t个词的时候,算ht的那个出处的时候呢,必须要保证前面那个词的ht-1输入完成了。假设你的句子有100个词的话,你的时序得算100步。导致你在时序上无法并行,现在在主流的GPU和加速器(TPU),都是实现并行操作,如果无法在这个上面并行,会导致你的并行度比较低,使得在计算上性能比较差。

3.Background(背景)

第一段是说如何使用卷积神经网络来替换掉你的循环神经网络,使得减少你的时序地计算,他提到了一些工作。并且提出了一个叫做Multi-Head Attention,可以模拟卷积神经网络多输出通道的一个效果。
第二段他讲的是自注意力机制(self-attention),这个是Transformer里面一个关键性的点呢,但是他说这个工作其实之前已经有人提出来了,并不是他这个工作的创新。
第三段他举了一个End-to-end memory networks(端到端内存网络)的例子,但我本人并没了解过过这个网络模型,所以这里不做说明。
第四段再次提出了讲了Transformer是第一个只依赖于自注意力来做encoder到decoder架构的模型。

4.Model Architecture(模型架构)

首先他解释了编码器(encoder)和解码器(decoder)的概念:对于编码器,假设你有一个有n个词的句子,它可以看成一个长为n的一个(X1, … , Xn)数据。此时Xn就可以代表这个句子里的第n个词,这个编码器会把他表示成一个长度为n的向量Zt(Z1, … , Zn)。其中每一个Zt【z = (Z1, … , Zn)】,它对应的是Xt的一个向量的表示,假设你是一个句子的话呢,那么Zt就表示你第t个词的一个向量的表示。编码器通过这些处理,将你有n个词的句子处理为机器能够理解的一系列向量。

对于解码器,他首先拿到了编码器的输出,然后根据这个输出会生成一个长度为m(Y1, … , Ym)的向量,这里的m、n不一定相等,举一个中英文翻译的例子:The animal didn’t cross the street because it is too tired可以翻译为:这个动物没有通过这条街因为它太累了。这里的中英文表达的意思相同,但是长度明显不同。在解码器里面,词是一个一个生成的。因为对编码器来讲,很有可能是一次性能看全整个句子,就是说做翻译的时候,我可以把整个英语的句子给你,但是在解码的时候呢只能一个一个的生成,这个东西叫做一个自回归(auto-regressive)的模型。在这个里面,你的输入又是你的输出。具体来看就是在最开始我给定的Z,那么你要去生成第一个输出,叫做Y1,在拿到Y1之后,我就可以去生成我的Y2,然后一般来说你要去生成Yt的话,你可以把之前所有的Y1到Yt-1全部拿到,也就是说在翻译的时候 词是一个一个地往外蹦,所以就是说在过去时刻的输出也会作为当前时刻的输入,所以这个叫做自回归。然后又提到Transformer是使用了一个编码器——解码器的架构,是使用编码器和解码器的堆叠的自注意和点的全连接层。

4.1Transformer模型结构图

图中左侧为编码器
图中左侧为编码器,右侧为解码器,Inputs为编码器的输入,假如用于英汉互译,这里输入的就是你提供的英文句子,Outputs是解码器的输入,但是解码器在做信息预测的时候是没有输入的,所以他实际上将解码器在之前时刻的一些输出作为输入放在这个位置,这里的shifted right就是一个一个往后往右移。

4.2编码器

编码器是用n(这里n=6)个完全一样的layer层,每个layer里面有两个Sub-layer:
第一个Sub-layer是multi-head self-attention
第二个sub-layer是simple,position-wise fully connected feed-forward network(其实说白了就是一个MLP,但是他为了显得fancy一点呢,就把名字搞的特别长。(李沐原话,咱小白是不敢乱说话的)
对于每个子层他都采用了残差连接(这里的残差链接可以参考ResNet),最后再使用一个叫做layer normalization的东西,残差连接减小了梯度消失的影响。加入残差连接,就能保证层次很深的模型不会出现梯度消失的现象。其公式如下:
LayerNorm(x + Sublayer(x))
为了保证残差连接方便实现,这里保证了向量的维度相同不需要做投影操作,这里将每一个层的输出维度定义为512,也就是说对于任何一个词,不管经过哪一层的处理,其输出结果都是512,这与CNN不同,也和MLP不同,之前的MLP和CNN要么是维度往下减,要么是空间维度往下降低,但是channel维度会拉高。这里的调参工作也比较简单,只需要调整层数n和维度d的大小,来实现自己想要的效果。例如GPT和BERT等。

4.3LayerNorm

我在这篇文章中第一次接触到了LayerNorm这个概念,这里将BatchNorm进行了对比,两者的区别就是BatchNorm是对所有不同样本的同一特征进行归一化,而LayerNorm是对某一样本所有不同特征进行归一化,二者提出的目的都是为了加快模型收敛实现减少训练时间
两者区别图示如下:
在这里插入图片描述
更具体来讲,我们假设我们输入的是一个二维矩阵,那么我的每一行都是我的一个样本,每一列就是我样本里面的一个特征,对于batchnorm来讲他就是把我每一列特征(即每个特征)在一个min-batch里面的均值置0,方差置1。
对于均值和方差的处理,就是你把它的这个向量本身的均值减掉,然后再除以它的方差就行了。这个地方你算均值的时候是在每一个小批量(即每一个列向量)里面算出它的均值,算出它的方差。
在训练的时候可以做小批量处理,但是在做预测的时候需要给他的全局数据算出均值和方差,在所有数据上将这些均值方差存起来,在预测的时候再使用。
batchnorm还会去学习一个λ和一个γ出来,就是说我可以把这个向量通过学习可以变成一个任意方差为某个值,均值为某个值的向量。
layernorm干的事情就是对每行(每个样本)做归一化而不是对每个特征做,batchnorm中我们是把每一个列的均值变0方差变1,现在是把每一个行变成均值为0方差为1,这个行就表示的是一个样本。所以可以认为这个LayerNorm就是整个把数据转置一下放到batchnorm里面出来的结果再转置回去,当然,这个是你的输入是二维的时候的最简单情况。
但是现在我们使用的transformer或RNN输入的都是三维的数据,因为他输入的是一个序列的样本,每一个样本其实是里面有很多个元素的,他是一个序列,你给一个句子里面有n个词,每个词都有个向量,再有一个batch,那么就是个3D的东西,画出来就是一个ndbatch的长方体。
如果再Transformer里面的话seq的长度就是n,feature就是d,batch就是取的句子的数量。这里如果还是用batchnorm的话就是取一个特征然后把他每个样本里的所有的元素都提取出来,也就是说我一刀切下来一块,然后再把它拉成一个向量再去进行运算。
如果是用layernorm的话,就是对这个长方体横切一下(batchnorm是竖切),这种切法的不同,就导致了两种归一化的区别,具体来说为什么layernorm用的多一些,有的原因是说你的每个样本的长度可能会发生变化。这里的主要的需要实现的功能其实是计算算均值和方差,对于batchnorm来说,我算均值的时候其实是通过画阴影的部分来算的,如果样本长度变化比较大的时候,每次做小批量的时候,你算出来的均值方差它的抖动相对来说是比较大的,而且我们在做预测的时候,我们要把这个全局的均值和方差记录下来,那么这个全局的均值方差,如果碰到一个新的特别特别长预测样本,假设我在训练的时候没延伸出去那么多,那么我在之前算的均值和方差很有可能是不那么好用的,但反过来讲对layernorm相对来说没有太多这个问题线,是因为它是每个样本自己来算我的均值和方差,我也不需要存下一个全局的一个均值方差,因为这个东西是对每个样本来做的,所以相对来说不管样本是长还是短,反正算均值是在单个的样本的一层里面算的,这样相对稳定一些。

4.4解码器

我们将解码器对标编码器来研究,首先他和编码器一样都是由n(这里n=6)个同样的层构成的。每个层里面跟编码器一样有两个一样的子层存在,但区别在于,解码器还有第三个子层,他也同样采用了多头注意力机制,另一个就是在解码器里他做的是一个自回归,也就是说你当前时刻的输入集,是你前一时刻的输出集。所以在编码器是Inputs的位置上,解码器是一个叫做Outputs(Shifted right)的东西。但是在注意力机制里,每一次都能看到全部的输入,但是我们在做第t时刻的预测的时候,是不应该看到第t时刻以及他以后的内容的,文章中的解法是引入了一个带掩码的多头注意力机制(Masked Multi-head Attention)

4.5Attention

注意力函数是一个将一个query和一些key-value对映射成一个输出的函数,这个函数里面所有的query、key、value已及output全都是向量,具体的来说,output是value的一个加权和,这意味着你输出的维度d和你value的维度是一致的,对于value权重的计算可以说是由key和query的相似度来决定的,在这里李沐老师举了下图的一个例子:
在这里插入图片描述
在这个图里,黄色的空心圆代表query,蓝色表示key,红色表示value,现在我有一个query来和这三个key-value对做相似度的比对,我们认为这里面左侧两根黄线比较粗,那么就说你的query和k1、k2的相似度更高,在你输出的v1、v2、v3的权重和里面v1、v2占比更大,v3相对较小。相对应的,假设我又有了一个其他的query(绿色空心圆q2),假设这里我们认定他和k3的相似度更高,线最粗,那么在你最后输出value的权重和的时候你的v3的权重会比较高,v2次之,v3最差。

4.6Scaled Dot-Product Attention

在文献中,Transformer给他自己的注意力计算方式取名为Scaled Dot-Product Attention,这里面定义query和key的长度(维度)是相等的,都等于dk,然后value的维度是dv,输出维度也是dv。具体计算是把query和key去做内积,然后将这个内积结果作为相似度的评定指标,建立在两个向量长度一样的基础下,内积的值越大(即余弦值越大),就说明两个向量的相似度越高。我个人认为对于内积和余弦值的关系可以用下图和公式来理解:

在这里插入图片描述

在这里插入图片描述
由余弦值和角大小的关系可知,当角越大的时候,对应的余弦值越小,因此我们可以说对于以上三种情况,左一的相似度最大,中间次之,最右边的是一种两向量正交的特殊情况,此时的相似度为0。当然,我们也可以说左一在另一条向量上的投影长度更长,所以相似度较好。然后算出来之后再除以根号dk,就是这个向量的长度,最后再用一个softmax来得到你的权重,因为你给一个query,假设给n个key-value对的话,那么就会算出n个值,你这个query会跟每个key做内积,算出来之后再放进softmax就会得到n个非负的而且加起来和等于1的一个权重,然后我们把这些权重作用在我们的value上面就会得到我们的输出了。当然在实际中不能一个一个这么做运算,算起来比较慢,所以它下面给了一个在实际中的时候我们应该怎么样算的。
文献中对于Scaled Dot-Product Attention的公式表达如下:
在这里插入图片描述
对于实际应用中我们显然不可能一个个query和key去内积过去,一般情况下会把query组成一个矩阵称之为q,把key也组成为矩阵k,然后两个矩阵去做内积,处理完就相当于是你的每一个query都和每一个key做了一次内积,然后再对这个矩阵的每一行进行一次softmax,最后生成的就是你的权重矩阵。把这个权重矩阵再乘以value向量组成的矩阵,就能得到存储输出结果的矩阵,这个矩阵的每一行都是我们需要的一个输出结果可能描述的比较混乱,我用下面的图示来体现一下:
在这里插入图片描述
上图中第一行的四个矩阵一次由左向右相乘最终得出第二行的结果,即一个n行(n个query)dv列的结果。这里也解释了为什么文献中说的Transformer的并行效果比较好。
文献中还提到了两种不一样的注意力机制,一种叫做加型的注意力机制(additive attention),它可以处理你的query和你的key不等长(维度不同)的情况,另外一个叫做点积的注意力机制(dot-product / multi-plicative attention)。文献中采用的就是点积的注意力机制,然后再除以一个叫scale的缩放因子(一般取根号dk)。说是因为这个实现起来比较简单,而且会比较高效,但是并没有说为什么additive attention差在哪里,我通过百度了解到它们之间的主要区别在于计算相似度的方式。
Additive attention使用一个全连接层来计算相似度,然后将查询向量和所有键向量的相似度进行加权求和得到输出向量。这种注意力机制的计算复杂度较高,但对于一些较小的数据集而言,表现也许会更好。而Dot-product/Multi-plicative attention则是通过点积或者矩阵乘法的方式来计算相似度。这些方法能够显著降低计算复杂度,并且通常在大型数据集上表现更好。除了计算相似度的方式以外,这两种注意力机制的主要区别还在于其处理输入序列的方式。Additive attention通常需要对数据进行特征变换,而Dot-product/Multi-plicative attention则直接使用原始输入序列作为输入。在某些情况下,Additive attention可能更适用,而在其他情况下,Dot-product/Multi-plicative attention则可能更有效,也就是说点积类型的注意力泛用性更好。至于为什么不直接用简单的点积方式,而要这里要除一个根号dk。是因为,当你的dk不是很大的时候,其实你除不除都没关系,但是当你的dk比较大的时候,即两个向量的长度(维度大)比较长的时候,那么做点积的时候,这些值可能就会比较大,当然也可能是比较小。当你的值相对来说比较大的时候,就导致说你值最大的那一个值做出来softmax的时候就会更加靠近于1,剩下那些值呢就会更加靠近于0,就是你的值就会更加像两端靠拢,当你出现这样子的情况的时候,你算梯度的时候你会发现梯度比较小,因为softmax最后的结果就是我希望我的预测值置信的地方尽量靠近1,不置信的地方尽量靠近0,这样子的时候我的收敛就差不多了,这时候梯度就会变得比较小,就会跑不动,所以说我们在Transformer里面一般用的dk比较大,比如之前说过的512,所以除以一个根号dk是一个不错的选择。

4.7MultiHead Attention—多头注意力机制

在了解多头注意力机制之前,我们首先需要知道什么是注意力机制(Attention)和自注意力机制(Self-Attention)

4.7.1注意力机制(Attention)

注意力机制其实是源自于人对于外部信息的处理能力。由于人每一时刻接受的信息都是庞大且复杂的,远远超过人脑的处理能力,因此人在处理信息的时候,会将注意力放在需要关注的信息上,对于其他无关的外部信息进行过滤,这种处理方式被称为注意力机制。
通俗的的讲,注意力对于我们人来说可以理解为“关注度”,比如男生在街上会关注美女,而女生在街上会关注帅哥,对于没有感情的机器来说其实就是赋予多少权重(比如0-1之间的小数),越重要的地方或者越相关的地方就赋予越高的权重。
我们还需要了解一下相关的三个概念:

查询(query):指的是查询的范围,自主提示,即主观意识的特征向量
键(key):指的是被比对的项,非自主提示,即物体的突出特征信息向量
值(value): 则是代表物体本身的特征向量,通常和Key成对出现

注意力机制是通过Query与Key的注意力汇聚(给定一个 Query,计算Query与 Key的相关性,然后根据Query与Key的相关性去找到最合适的 Value)实现对Value的注意力权重分配,生成最终的输出结果。
举一个比较现实的例子:
比如你在淘宝上买衣服,输入一个关键词“显瘦”,这里的“显瘦”就是你的query。然后淘宝的搜索引擎会根据关键词去查找一系列相关的Key(商品名称、图片),然后找的符合关键词(相似度高)的key,然后返回相应的衣服链接(具体的value)。

4.7.2自注意力机制(Self-Attention)

自注意力机制实际上是注意力机制中的一种,也是一种网络的构型,它想要解决的问题是神经网络接收的输入是很多大小不一的向量,并且不同向量向量之间有一定的关系,但是实际训练的时候无法充分发挥这些输入之间的关系而导致模型训练结果效果极差。比如机器翻译(序列到序列的问题,机器自己决定多少个标签),词性标注(Pos tagging一个向量对应一个标签),语义分析(多个向量对应一个标签)等文字处理问题。针对全连接神经网络对于多个相关的输入无法建立起相关性的这个问题,通过自注意力机制来解决,自注意力机制实际上是想让机器注意到整个输入中不同部分之间的相关性。

自注意力机制是注意力机制的变体,其减少了对外部信息的依赖,更擅长捕捉数据或特征的内部相关性。自注意力机制的关键点在于,Q、K、V是同一个东西,或者三者来源于同一个X,三者同源。通过X找到X里面的关键点,从而更关注X的关键信息,忽略X的不重要信息。不是输入语句和输出语句之间的注意力机制,而是输入语句内部元素之间或者输出语句内部元素之间发生的注意力机制

注意力机制和自注意力机制的区别:
(1)注意力机制中的Q和K是不同源的,在Encoder-Decoder模型中,K是来自于Encoder模块的,Q是来自于Decoder模块的。在中译英工作中,Q是中文单词特征,K是英文单词特征。
(2)在自注意力机制中Q和K是同源的,在Encoder-Decoder模型中,Q和K都是来自于Encoder中的元素,即Q和K都是中文特征。在他们之间做注意力凝聚。也可以理解为同一句话中的词元或者同一张图像中不同的patch,这都是一组元素内部相互做注意力机制,因此,自注意力机制(self-attention)也被称为内部注意力机制(intra-attention)

自注意力机制的问题:自注意力机制的原理是抓取重要信息,过滤掉不重要的信息,这会导致他的信息抓取能力不如CNN,这是因为自注意力机制相比于CNN,无法利用图像本身具有的尺度、平移不变性,以及图像的局部特征性(图片的相邻区域有相似的特征,即同一物体的信息往往都集中在局部)这些先验知识,只能通过大量数据来进行学习,这就导致自注意力机制只有在大数据的基础上才能有效地建立起准确的全局关系,而在小数据的前提下,他的效果不如CNN。此外,自注意力机制虽然考虑了所有输入向量,但是没有考虑向量的位置信息,在实际的文字处理问题中,可能会存在不同位置词语具有不同性质,比如名词往往高频率出现在句首,而动词往往低频率出现在句首。然后在本文献内提到了一个叫做**位置编码(Positional Encoding)**的概念。我们举一个例子:transformer模型不包含循环或卷积,输出是V的加权和(权重是 Q与K的相似度,与序列信息无关),对于任意的K-V,将其打乱后,经过注意力机制的结果都一样,它顺序变化而值不变,但在处理时序数据的时候,一个序列如果完全被打乱,那么语义肯定发生改变,而注意力机制却不会处理这种情况。这时在注意力机制的输入中加入时序位置信息,位置在encoder端和decoder端的embedding之后,以此补充Attention机制本身不能捕捉位置信息的缺陷。一次词在嵌入层表示成一个512维的向量,用另一个512维的向量表示位置数字信息的值。用周期不一样的sin和cos函数计算。

4.7.3多头注意力机制(MultiHead Attention)

通过对自注意力机制的学习,我了解到自注意力机制的缺陷是,模型在对当前位置的信息进行编码时,会过度的将注意力集中于自身,对于有效信息的抓取能力就会弱一些,所以有前辈大佬提出了多头注意力机制。

实现方法

不再使用一个attention函数,而是使用不同的学习到的到的线性映射将querys、keys、values分别线性投影到dq、dk、dv维度h次。然后在querys、keys、values的这些每一个投影版本上并行执行注意力功能,产生h个注意力函数,最后将这些注意力函数拼接并再次投影,产生最终的输出值。
在本文献中本文的点积注意力先进行了投影,而投影的权重W是可学习的。多头注意力给出h次机会学习不一样的投影方法,使得在投影进去的度量空间里面能够去匹配不同模式需要的一些相似函数,每个头h把 Q,K,V 通过 可以学习的 Wq , Wk , Wv投影到 dv上,再通过注意力函数,得到head。然后把 h 个头拼接起来,最后再做一次投影。
多头注意力的输入还是 Q 、K 、V,但是输出是将不同的注意力头的输出合并,再投影到W0 里,论文中对此部分的图示如下:
在这里插入图片描述

多头注意力机制的使用方法

1.定义多组W,用于生成多组Q、K、V。
我们可以了解到Q、K、V是输入向量X分别乘以系数Wq、Wk、Wv得到的,这里的Wq、Wk、Wv是可以进行训练的参数矩阵(即我们需要让机器去学习的参数)。对于同样的输入向量X,我们定义多组不同的Wq、Wk、Wv,例如Wq1、Wk1、Wv1,Wq2、Wk2、Wv2每组分别计算生成不同的Q、K、V,最后学习到不同的参数。

在这里插入图片描述

2.定义八组参数
对应八个head和八组Wq、Wk、Wv。再分别进行self-attention,就得到了Z0-Z7
在这里插入图片描述
3.将得到的Z1-Z7进行拼接,然后乘以W0降低维度
在这里插入图片描述
完整流程如下:
在这里插入图片描述

4.7.4Applications of Attention in our Model—注意力机制在Transformer模型中的应用

再次我们再次回顾Transformer的模型图:
在这里插入图片描述

Transformer使用了三种不同的注意力头:
(1)Encoder注意力层(上图中红色框内部分):输入数据在经过Embedding+Positional Encoding后,复制成了三分一样的东西(即Q、K、V)也就是说这个数据处理过程中Q、K、V三者同源,这就是我们接触的自注意力机制,输入n个Q进入,就会有n个输出,输出的结果是V的加权和(权重的大小与Q和K的相似度相关)。
(2)Decoder注意力层(上图中绿色框位置):这里的注意力层不再是自注意力机制,他的Q来自于掩码多头注意力的输出,K、V来自于Encoder的输出。
(3)Decoder掩码注意力层(上图中蓝色框位置):这里的注意力层依旧是自注意力机制,他的Q、K、V均来自于上一个掩码注意力层的输出结果,他和第一个Encoder注意力层的区别在于加入了Mask操作,比如我们在处理或预测第t个位置的信息时,他将t位置和t位置后的数据权重设置为 0,使机器只能看见t位置前面的东西,以此保证你的预测结果不会受到后面信息的影响。

什么是Mask:Mask表示掩码,他可以对一些信息进行掩盖,使其在参数更新时不产生效果,在Transformer模型中涉及了两种Mask,分别是Padding Mask和Sequence Mask。其中Padding Mask在所有的 scaled dot-product attention中都需要用到,而Sequence Mask只有在Decoder的Self Attention中才用到(即上图蓝色框部分)。
**为什么需要Mask:**有一些生成的attention张量中值的计算有可能是因为已知了未来的信息才得到的,而未来的信息被看到是因为训练时会把整个输出结果都一次性进行Embedding,但是理论上解码器的输出不是一次就产生的,而是一次次通过上一层的结果来产生的。所以未来的信息有被提前利用的可能,也会因此影响到训练的结果。所以,Attention中需要使用掩码来掩盖未来未知的信息,以此保证训练过程不被影响。
简单来说,我们建立模型是为了实现预测效果,而预测是利用过去的信息来对未来的状态进行判断,如果把未来的结果加进来共同推断未来,就属于是抄袭,而不是预测。如果用未来推断未来,他的训练效果会很好,但是测试效果会很差(这句是CSDN的其他大佬说的,本人刚刚入门,实践经验比较匮乏,但我后续会去进行验证)。

4.8Position-wise Feed-Forward Networks—基于位置的前馈神经网络

除了encoder子层以外,encoder和decoder中的每个层还包含一个全连接的前馈网络,他分别相同地应用于每个位置。它由两个线性变换和中间的一个ReLU激活函数组成,并且线性变换虽然在不同位置上是相同的,但是在层与层之间使用不同的参数。他还有一种描述方式是两个内核大小为1的卷积。输入和输出的维度为512,中间的维度为2048。文献中给的公式如下:
在这里插入图片描述
在上图中,括号里面是一个线性变换,max(…)是一个ReLU激活函数,然后外面又是一个线性层,文献中给出的输入输出维度是512,所以我们可以知道在注意力层的输入,即每一个query的长度和他对应的输出的长度也是512。这个公式中的x也就是一个长度为512的向量,也就是说我的W1把512投影成2048的维度,将其扩大了四倍,但是你最后还要做一次残差连接(残差连接需要两个x和y有相同的维度),所以W2又把这个2048投影回了512。
李沐老师在课程中讲到这个公式说白了就是一个单隐藏层的MLP,然后中间隐藏层把你的输入放大了四倍,最后输出的时候也回到你输入的大小。
文章的多头注意力部分有8个头,那么我们输出的8个注意力头Zi分别通过两个Feed Forward,然后接一个残差连接,即Zi和Feed Forward的输出Add对位相加。最后把相加的结果进行一次LN标准化,以此平滑的整合输入和其他层的输出。上文也提到过,这里采用LN而不采用BN是因为层归一化可以防止层内的数值变化过大,从而有利于加快训练速度并提高泛化性能。

4.9Embeddings and Softmax —词嵌入和 softmax

Embedding: 特征嵌入,embedding是可以简单理解为通过某种方式将词向量化,即输入一个词输出该词对应的一个向量。(embedding可以采用训练好的模型如GLOVE等进行处理,也可以直接利用深度学习模型直接学习一个embedding层,Transformer模型的embedding方式是第二种,即自己去学习的一个embedding层。)

在本文中embeddings将输入和输出tokens转换为向量,线性变换和softmax函数将decoder转换为预测的token概率。因为我们输入的句子可以拆分成一个个词或者是词源(token),而我们处理的信息需要是一个向量,而embedding的作用是给每一个词学习一个长度为d(文中的d=512)的向量来表示他。不管是encoder还是decoder都需要embedding,还有Softmax前的线性层也需要embedding,并且为了便于模型的训练,这三个位置的权重是一样大的,并且给这个权重乘上了一个根号d。做这个事情是因为在学embedding的时候,会把每一个向量的L2Llong学成相对来说比较小的,比如说学成1,就不管你的维度多大,最后你的值都会等于1,那就是说你的维度太大,你学的一些权重值就会变小,但是你之后要加上Positional Encoding,加这个的时候它不会随着你的长度变长了把你的long固定住,所以它乘了根号d之后使得加上Positional Encoding的时候在大小上都差不多,就是做了一个hat。

4.10Positional Encoding

由于模型中不包含循环或卷积,为了让模型可以利用序列的顺序信息,我们需要引入关于字符相对或绝对位置的信息。所以,我们在encoder和decoder底部的堆栈底部的输入嵌入中加入“位置编码”。位置编码和嵌入的维度d相同,所以这两个可以相加。有多种位置编码可以选择,例如通过学习获得的位置编码和固定的位置编码。并且使用不同频率的正弦和余弦函数:
在这里插入图片描述
文献中说明了pos 是位置,i 是维度。也就是说,位置编码的每个维度对应于一个正弦曲线。波长形成了从2π到10000·2π的几何数列。我们之所以选择这个函数,是因为我们假设它可以让模型很容易地通过相对位置来学习,因为对任意确定的偏移k, PEpos+k​可以表示为PEpos​的线性函数。

为什么使用Positional Encoding
在处理一个时序数据的时候,一个序列如果被完全打乱,那么语义就会改变,而普通的注意力机制无法处理这种情况。所以要在注意力机制中加入一个时序信息,位置在encoder端和decoder端的embedding之后,用于补充Attention机制本身不能捕捉位置信息的缺陷。一个词源在嵌入层表示成一个512维的向量,用另一个512维的向量表示位置数字信息的值。用周期不一样的sin和cos函数计算。

5.Why Self-Attention

这一部分主要解释了为什么使用自注意力机制,并且通过一个表格把使用自注意力机制的结果和使用CNN和RNN的结果做了对比,体现了自注意力机制的优越性。图表如下:
在这里插入图片描述
这张表对比了四种不一样的层(自注意力、循环层、卷积层、构造的受限的自注意力)之间的计算复杂度、顺序操作数(即并行程度)以及一个信息从一个数据点走到另一个数据点的距离三项属性之间的差异。
对于自注意力层来说,n是序列的长度,d是向量的长度,我们知道整个自注意力的话其实就是几个矩阵做运算,其中一个矩阵是query乘以key。query矩阵有n行,n个query,列数是d,就是维度是d。key也是一样的nd,所以两个矩阵一乘的话,那么算法复杂度就是n^2d,另外一些别的矩阵运算,但是它的复杂度都是一样的。然后Sequential Operation因为只涉及到几个矩阵乘法,矩阵认为是并行度比较高的,所以这个地方是一个O(1)的。也就是说并行度非常好。最大的长度是说从一个点的信息想跳到另外一个点要走多少步。我们知道在attention里面,一个query可以跟所有的key去做运算,而且输出是所有value的一个加权和,所以说任何query跟任何一个很远的一个key-value对只要一步就能过来,所以长度是O(1)。
对于循环层来说,循环层如果你的序列是乘了n的话,就一个一个做运算,每个里面主要的计算是一个nn的一个矩阵,然后再乘以你一个长为d的一个输入,所以是nd^2。然后对比一下这两个东西是有一定区别的,就取决于你是n大还是d大。实际上来说这个地方的d是512,n和d这两个差不多大。但是在循环的时候,因为要一步一步做运算,当前时间刻的那个词需要等待前面时刻去完成,所以导致你是一个长为n的一个序列化的操作,并行度比较差,所以顺序复杂度为O(n)。另外,你最初点的那个历史信息到最后一个点需要走过n步所以最长距离是O(n)。所以大家会批评RNN说应对特别长的序列的时候做的不够好,不像attention一样,就可以直接一步就能过去。
对于卷积层来说,卷积在序列上具体的做法是他用一个ed的卷积,所以它的kernel就是k,n是长度,d是输入的通道数和输出的通道数。k一般就3或5,所以这个东西可以认为是常数,导致卷积的复杂度和RNN的复杂度其实是差不多的。但是卷积的好处是说一个卷积操作就完成了,里面的并行度很高,所以卷积做起来通常比RNN要快一点,另外一个是说卷积每一次一个点是由一个长为k的一个窗口来看的,所以它一个信息在k距离内是能够一次就能传递,如果超过了k的话就要通过多层,但是他是log的一个更换过程,所以这个的距离是O(logk(n))。
对于受限的自注意力,好像是说这个应用较少,我没有去过多了解。

Regularization——正则化操作

Transformer模型中采用了以下两种正则化操作:
Dropout: dropout代表进行dropout操作时置0比率,默认是0.1,对编码器和解码器的每个子层的输出使用 Dropout 操作,在进行残差连接和层归一化之前。词嵌入向量和位置编码向量执行相加操作后,执行 Dropout 操作。Transformer 论文中提供的参数P drop=0.1。
Label Smoothing(标签平滑):标签平滑其实就是将硬标签(hard label)转化为软标签(soft label),也就是将标签的one hot编码中的1转化为比1稍小的数,将0转化为比0稍大的数,这样在计算损失函数时(比如交叉熵损失函数),损失函数会把原来值为0的标签也考虑进来,其实就相当于在标签的one hot编码中的每一维上增加了噪声。本质上是向训练集中增加了信息,使得训练集的信息量增大了,更加接近真实分布的数据集的信息量,所以有利于缓解过拟合。Transformer 论文中提供的参数 ϵls = 0.1。

6.模型变体和调参

Transformer的作者团队还在文献中提供了一个他们测试过的调参表格供大家参考,具体内容如下:
在这里插入图片描述
上表的每一列都给我们列出了一个超参数,包括:

  • N:堆叠的层数
  • dmodel:模型的宽度,即一个词源(token)被输入进来需要被表示成的向量的长度。
  • dff:表示MLP中间隐藏层的大小。
  • h:注意力层头的个数。
  • dk:一个head里面key的维度
  • dv:一个head里面value的维度
  • Pdrop:dropout
  • εls:label smoothing最后需要学习到的真实值大小。
  • train steps:需要训练的batch个数

然后是每一行对应的一种类似于控制变量法的调参操作:
**第A行:**改变Attention的head的个数,扩大dk和dv的维度,head数缩小多少倍,dk和dv就放大多少倍,以此保持计算量不变。
**调参结果:**发现对于BLEU数据集,只有一个head的训练结果与8头和16头相差0.9,而且32head的结果也低于8head和16head,最后可得出结论,head不是越多越好的,针对不同数据集,head都能取到一个合适的峰值,不管是head数过多还是过少,他的训练结果都会比这个峰值差。
**第B行:**降低dk的维度
**调参结果:**减小dk的大小会损坏模型质量,这表明确定兼容性并不容易。
**第C行:**将模型大小整体放大。
**调参结果:**模型变大,训练效果会变好
**第D行:**增大dorpout
调参结果: 发现dropout对避免过度拟合非常有帮助
第E行: 用学习到的positional encoding来替换正弦位置编码
调参结果: 与基本模型得到的结果几乎相同

7.论文中后续的其他部分

后续的部分讲述了在构建Transformer模型过程中是如何训练的、采用的优化器及其参数、果以及最终结论和参考文献等,在此不做过多讨论(其实是看不下去了)。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值