Transformer架构学习笔记-Attention is all you need
推荐学习顺序:周志华《机器学习》->《深度学习进阶:自然语言处理》->RNN->LSTM->seq2seq->transformer
-----一些要用到的参数-------
Dmodel:模型的维度,或者说词语token经过了embedding层后变成词向量的维度。这里取512维。
DK DV:分别代表编码器和解码器的多头自注意力层里,向量经过每个头权重矩阵后,得到的Q K V 的维度。在这里取64.
Dff:在这里取2048,在前馈神经网络层,将向量从512维拓展到了2048维。随后再恢复到512维。中间加以非线性函数。增加维度可以让模型学习到更多的特征表示。
----------正篇--------------
我们先从RNN和其衍生模型讲起。循环神经网络(RNN)及其衍生模型,包括LSTM、seq2seq等模型,在embedding层后所连接的都是RNN层或是RNN层的变种。循环神经网络的基本特点是:在神经元内处理时序数据后,会向下一个神经元或者自身发送一个隐藏状态向量h,这个隐藏向量包含了以前和当前的一部分信息,当前的输出不止取决于当前t时刻的输入,还与t-1时刻的输入有关。这使得RNN网络具有了一定的时序信息记忆能力。为了避免梯度消失\爆炸等问题,又有了LSTM(长短期记忆)、GRU(门控循环单元)等改进型。
但是,RNN神经网络模型有着明显的缺点:它的长时记忆能力不佳,尽管LSTM在一定程度上克服了这一问题,但长期记忆能力仍然很难提升;并且由于处理下一个时序的数据需要依赖上一个时刻的隐藏状态,模型很难并行训练;相比之下transformer这样的无状态模型则很好地克服了这一点。此外,由于RNN的设计思路,它只能处理从过去到未来单个方向的时序数据,无法利用未来时序信息,为了解决这一问题,人们又提出了“双向RNN”的模型,这里暂且不提。
LSTM神经元,包含有输入门、输出门和遗忘门
Seq2seq模型中引入了“编码器-解码器”的架构,它将整个神经网络模型分为了编码器和解码器两个部分。编码器的作用是将输入token转化为固定长度的词向量,而解码器的作用是将向量转化为输出序列。Transformer整体上使用了与seq2seq类似的编码器-解码器架构,也有类似思路的attention层。但是不同的是,seq2seq仍然属于LSTM(长短期记忆网络),编码器和解码器中有各有一层由RNN改进来的LSTM层。也保留了RNN网络的基本特点。此外,seq2seq中默认编码器向解码器传递的是编码器的最后隐藏状态向量,attention层的加入才使得seq2seq不止关注最后的隐藏状态向量。
除开这几个特点,transformer还引入了多头的机制,编码器层包含多头自注意力层和前馈神经网络两个层,解码器则包含多头掩码自注意力层、编码-解码注意力层和前馈神经网络三个部分。每经过一个子层,都要进行一次残差连接和norm操作(数值均一化)。
在开始之间,我们可以先讨论一下,什么是attention(注意力)?当你阅读这样的一篇文章的时候,你不会关注所有词语,而是找到那些你认为重要的内容,重点观看。在处理文本时,有的单词会与其他某些单词有着更为明显的关系。例如这样的一个句子:“我喜欢吃苹果,偶尔吃香蕉”。在这样的句子中,“吃”和“苹果”、“香蕉”的相关性显然大于其他词。我们希望在处理“吃”这个词时,能够带有一定的上下文的信息。注意力机制使得我们在处理词向量时,能够隐含地带有一些上下文的内容,间接地达到了一种“记忆”的效果。
Self-attention和attention层有很明显的区别。它们的区别和计算也会有更详细的讲解。一般来说:
1)Attention层是为了连接编码器和解码器,起到连接组件的功能;也可以用于多模态的连接,一般只使用一次。Self-attention则一般用于一种模态下,并且在模型内会被多次使用。
2)Self-attention更擅长挖掘一个序列内不同部分间的关系,attention则更侧重于两个序列间的关系。
-----------------------------------------------
从编码器层开始,句子被预处理为token,然后送入embedding层,被转化为512维的词向量。假设有一个8个token的句子,在这里就会被转化为8*512的矩阵。随后被送入位置编码部分。Embedding层的作用有点类似于一个字典,在token进入embedding层后,它会被映射为一个固定长度的词向量,以便后续进行处理。
(附:我们一般说的向量都是列向量,这里用的是行向量。8个token的句子不是512行8列即512*8。为了方便计算,这里使用的是8*512的矩阵,每行对应的是一个token 的对应词向量,方便tensor和pytorch等库使用)
Transformer相比于RNN的一个特点就是可以并行处理,在RNN神经网络模型内,下一个RNN神经元需要得到上个神经元的隐藏状态向量h才能进行运算,这限制了RNN的训练速度。transformer没有这个设计,取而代之的是自注意力机制,但这同时也意味着它会丢失文本的时序信息,这意味着什么呢?文本时序被打乱后,模型仍然能正常学习,但这样的学习显然是无价值的。为了解决这一问题,需要进行一次位置编码。
设计位置编码时,我们需要考虑一些问题:
1)如果说给首个向量编号为0,末尾编号为1,中间的向量按差赋值,那么每个不等长的句子,它的编号不等价,不适合进行学习。
2)如果给首个向量编号为0,逐个线性增大编号,那么不等长的句子也会使得模型难以进行泛化学习,此外,长句子还可能导致位置编码数值爆炸。
由此可见,要增加位置信息,要确保每个位置的编码是独一无二的,还要向量间距离一致,便于学习;并且还要控制总长度。transformer的论文中提到了一种编码方式,满足了上面的要求:这种编码并不是单一数值,而是包含句子特定位置的d维向量:
从定义可以看出,随着维度增大,频率会逐渐减小,这样就达成了目标。比如说,我们此时假设向量维度为4,那么每个位置向量应该长得如下所示:
为什么这样的位置编码可以表示位置?我们可以用热力图来展示一下,下图就是上面的向量转化为热力图的效果:
图片来源已在末尾链接标注
这么看似乎还是看不出什么,再分别对x轴和y轴切片,观察效果:
在这张图中,X 轴切代表某一个位置在不同维度上的数值;Y 轴切表示同一个维度在不同位置上的数值。从中我们可以看出,每个位置对应的位置向量都是不同的,同时低维度的数值变化较大,高维度变化则相对更小。这样做就有了一个好处:在与词向量相加时,可以保护原有词向量的信息,同时加入了位置信息。
位置编码设计好后,让其维度等于词向量的维度,以便直接相加。相加后,送入多头自注意力层。此时单个词向量仍然是512维。
走过了位置编码,向量被送入了编码器的多头自注意力层。多头自注意力层是transformer的亮点。Attention层这个概念并不是第一次出现,在seq2seq中就已经出现。只不过形式稍有区别:在seq2seq模型里,编码器的lstm层会逐个输出隐藏向量h_enc,看作行向量组合起来就能组成一个矩阵hs。而解码器中的每个lstm神经元得到的隐藏状态向量h_dec,会逐个与hs中的行向量做点积,再进行softmax,得到一组标量权重a,a的各权重与hs中各行向量相乘,而后相加, 最终便得到上下文向量c。
seq2seq中的attention层原理
在transformer 模型中,我们使用的是另一种描述:在自注意力层中包含三个权重矩阵:WQ、WK、WV,Q、K、V分别代表Query、Key和Value。在向量进入后,它会分别与三个权重矩阵相乘,产生对应向量Q、K、V。随后Q和K会相乘,相乘后除以dk,再softmax,然后与V相乘,再将结果送入前馈网络部分。
总体来说,可以写成这个样式:
------为什么可以这么做?
------从本质上来说,自注意力机制就是对源数据的Value值进行加权求和,这点和seq2sq的思想是一样的。Q和K的相乘就是为了计算对应的Values的权重。与普通的注意力机制相比,普通的attention层运行在编码-解码层上,判断解码器的输入和编码器输入的相关性;而自注意力可以单独架在编码器或解码器上,判断每个输入和其他输入间的相关性。它能够捕获一个句子中单词之间的语法特征。
在计算过程中,输入向量分别乘以三个权重矩阵,得到了Query 、Key、Value三个向量,它们是原向量在高维空间中的表达。Q和K必须在同一高维空间下(维度数一样),K则无强制要求,但在这里我们令其三者维度一致。在注意力机制里,Key和Value一般情况下默认相同,且与Query不同,这种是我们一般的注意力输入形式,但在自注意力层中,三者来源是相同的。
如果要用例子来比喻,可以想象,在注意力机制中,Q是问题,而Key是提示,Value则是答案。但在自注意力中,我们没有提示,看到的问题Key也就是Query。而Value则毫无头绪。在不断训练的过程中,我们逐渐对Query进行了关键词提取,使得Value逐渐产生。我们在做的,就是计算每个单词对其他单词的评分,用评分决定对单词进行编码时,要在其他词语上投入多少注意力。
具体来说,Q和K的点积计算了每个元素对其他元素的需求和提供的相互匹配程度。如果一个元素的查询Q和另一个元素的键K非常匹配,那么这两个元素之间的相互关系就会被赋予较大的权重。这个权重经过softmax函数处理后,可以理解为一个元素对另一个元素的注意力或者重要性,称作attention score。然后,这个权重用于加权求和值,得到每个元素的新表述。
通过这个过程,自注意力机制可以捕捉输入序列中的全局依赖关系,无论这些依赖关系发生在哪个位置,或者这些依赖关系的距离有多远。这是自注意力机制的一个重要优点,也是它在处理自然语言处理任务,特别是处理长序列任务时非常有效的原因。
除过自注意力机制,多头机制也值得详细讨论。多头机制也是transformer模型新增的内容:
缩放点积注意力的基本流程,在上面已经详述
多头注意力的原理
乍一听,多头似乎意味着多组线性变换层,实则不然,多头仍然对应的是一组线性变换。即三个向量对Q,K,V分别进行线性变换,这些变换不会改变原有向量的尺寸,因此每个变换矩阵都是方阵,得到输出结果后,每个头开始从词义层面分割输出的向量,也就是每个头都获得一组Q,K,V进行注意力机制的计算,但是句子中的每个词的表示只获得一部分,这就是所谓的多头和多头注意力机制。说人话就是词向量与权重矩阵相乘后得到Q、K、V三个向量,整个向量被多个头拆分,各自拿走一部分进行分析。这样做,每个头只拿到了每个向量的一部分信息,或许会有一些信息的损失,但这样使得不同的头可以关注不同的特征,最后再将结果拼接,降低因为注意力机制产生的偏差。实验结果表明,这样做能够提高模型性能。
还是之前的例子,一个有8个token的句子,词向量维度是512,头数是8。那么在多头自注意力层,三个权重矩阵的大小都是dim*dim,也就是512*512.当然,这里是将8个头的对应权重矩阵拼在一起的结果,单个头,单个矩阵还是512*64。输入的词向量会分别与WQ WK WV相乘,得到的Q、K、V向量还是512维,然后再进行拆分,拆成8个64维的向量。值得注意的是,在这里就已经完成了拆分。之前的矩阵相乘是8个头一并计算,而之后每个头的缩放点积、softmax计算都是每个头独立计算。
计算完成后,向量会被重新拼接在一起。在这个例子中,就是8*64的Q和KT相乘后,进行缩放和softmax,得到一个8*8的矩阵,然后与8*64的V相乘,得到8*64的向量,再重新拼接回8*512的向量。
在得到这样的向量以后,就目前来说,它还只是8个头的信息的拼接,我们还需要让它经过一次512*512的矩阵做线性变换。这样做是为了让多个头的信息融合,让不同头之间产生交互。最终,要进入前馈神经层的还是一个8*512的矩阵。
-------在这里又出现了一个小问题:为什么要对Q和KT的乘积进行缩放点积(即除以√dim)?
-------简单地来说,除以这个数是为了“归一化”。
假设 Q和K里的元素的均值为0,方差为1,那么AT=QTK中元素的均值为0,方差为d. 当d变得很大时,A中的元素的方差也会变得很大,那么softmax
(A)的分布会趋于陡峭(分布的方差大,分布集中在绝对值大的区域),可能导致梯度消失的问题。因此A中每一个元素除以 √dim后,方差又变为1。这使得softmax(A)的分布“陡峭”程度与d解耦,使得训练过程中梯度值保持稳定。
(插入:在标准情况下,不是一次只输入一个词向量或者一个句子,而是一批批地输入内容。因此输入输出的是一个多维矩阵,包含batch_size、seq_length、embedding_dim等参数)
走过了自注意力层,向量\矩阵还需要进行一次layer norm和残差连接。在这里,残差连接可以粗略地认为是f(x)=y+x。也就是说经过了自注意力层的数据,又加了一次经过前的数据。在没有残差连接的模型里,深度增加更容易导致梯度消失\爆炸,影响训练效果。残差连接可以让结果直接输入到下一层,最差的情况也能让上一层的数据继续向前流动。
经过残差后,还要经过一次layer norm(层归一化)操作,使得每层的输出有类似的范围和分布。norm,即normalization。这里的归一化和之前的缩放点积不同,此时的归一化需要让每个时序数据的各维度数值均值为0,方差为1,以便运算,减小梯度爆炸的可能。
补充:与layer norm层归一化相对应的,还有batch norm,即批量归一化。Batch norm的基本方法是对一批样本中的每个样本在同一维度上做归一化,layer norm则是如果在实际运算中,矩阵的坐标为[batch_size,seq_length,dim]。那么batch norm针对的就是第一维度,layer norm针对第三维度。在transformer里通常使用layer norm与残差连接相匹配,因为在实际情况中,可变长度的序列数据更适合使用层归一化,即使是在使用了填充和截断的固定长度词向量中,不同序列包含结构和信息不同也使得层归一化更为合适,并且实现更为简单。关于二者的详细区别:残差连接和层归一化
Batch norm与Layer norm的区别,可以发现批量归一化没有解决句子结构不对应的问题
在走过残差连接和归一化后,向量便进入了前馈神经网络。这个前馈神经网络只有两个线性变换层,中间夹杂一个非线性函数(如ReLU)(如果没有非线性函数,那两层和一层就没有什么区别)。由于我们的向量维度为512维,所以这里的两层线性变换层,权重矩阵一般取512*2048和2048*512.也就是说,向量经过第一层后会变为2048维,更高的维度有利于模型学习更多特征。而前馈神经网络层的存在,是为了处理的转换自注意力层中得到的信息。理解Transformer中的FFN的作用 同样,在经过前馈神经网络层后,还需要进行一次残差运算和归一化。
如果我们采用ReLU函数,那么前馈神经层就可以表示为这样的式子:可以近似看作一种Neural Memory的实现方式。
前馈神经网络层去掉也可以用,但是会影响效果。
(ReLU函数:在输入x大于0时输出取x,x小于0时取0)
解码器:
与编码器类似,解码器层中也有多头自注意力层,残差连接、前馈网络等部分都一样,这里不过多赘述。不过它相比较解码器,还增加了跨编码-解码多头注意力层。此外,多头自注意力层也增加了掩码,避免在并行训练时让模型错误学习到当前时序数据后面的内容,造成过拟合。
掩码的增加很好理解。在编码器中,我们需要让整个序列中每个时序数据都能关注到所有它应该关注到的数据。但解码器中,我们所期望的是解码器按照已有的内容逐步生成剩下的内容,而不是利用t+1时刻的信息去生成t时刻的内容,后者违反了训练的目的。
关于跨编码-解码注意力层,与seq2seq类似,编码-解码注意力层是为了计算输入间的关系,在自注意力层中,Q、K、V 三个向量均来自相同的来源--输入词向量W。但是在跨注意力层中,来自编码器的输入,提供Key和Value,而Query由解码器方面经过自注意力层的输出来充当。在计算方面,与自注意力没有什么大的区别。
跨编码-解码 注意力层:
Tranformer模型包含编码器和解码器两大部分,每个部分均将编码器\解码器层进行了多次重复,多次重复的设计意味着模型在后面可以学习到训练数据中更为抽象的部分,也能更好地处理长距离依赖。在图中也可以看出,编码器层的输出,除了最后一层外,只继续进入下一层编码器。在最后一层编码器输出结果后,编码器的输出结果会被送入解码器每层的跨注意力层中,与每个解码器内自注意力层的输出做跨编码-解码注意力计算。
在训练时,LSTM等模型采用的是自回归的训练方式:简单的来说,就是用解码器的上个输出作为当前的解码器输入。在transformer模型训练时,这种训练方法也可以使用,但为了并行训练,我们通常选择将目标语言序列(除了最后一个词)直接输入到解码器,这种行为称为“教师强制”。为了避免模型过拟合,在输入序列时还要加上掩码,避免每个解码器在训练时错误关注到了当前时序数据后面的内容。当然,在实际使用时,我们只能使用自回归的方式生成序列。
在解码器层的最后,我们需要做的是用softmax来预测下一个要输出的文本。
而softmax会根据输出矩阵的每一行来预判下一个单词。
除过图里面有的内容,还有dropout没有被提到。dropout的基本思想是在训练过程中,随机丢掉一些神经元,比如直接将其置零,以防止模型过拟合,或是减轻对某些词语的依赖。它可以发生在embedding层,也可以发生在位置编码、自注意力层和前馈神经网络层等位置。在部分模型变体里,也可以发生在词向量经过权重矩阵得到Q、K、V时。
具体操作上,在给定超参数Pdrop后,每到一个预定可以发生dropout的步骤,就独立地用这个概率随机丢弃一些神经元。对于被丢弃的神经元,它们的权重在本轮不进行更新。但在测试和评估模型时,一般不启用dropout,而是所有神经元全部使用。为了避免训练时和测试时模型输出相差太大,可以缩放神经元的输出,比如将输出乘以(1-Pdrop),Pdrop即为dropout发生几率。
--------20道问题,检查一下自己是否看懂了-------
1. Transformer为何使用多头注意力机制?(为什么不使用一个头)
2.Transformer为什么Q和K使用不同的权重矩阵生成,为何不能使用同一个值进行自身的点乘? (注意和第一个问题的区别)
3.Transformer计算attention的时候为何选择点乘而不是加法?两者计算复杂度和效果上有什么区别?
4.为什么在进行softmax之前需要对attention进行scaled(为什么除以dk的平方根),并使用公式推导进行讲解
5.在计算attention score的时候如何对padding做mask操作?
6.为什么在进行多头注意力的时候需要对每个head进行降维?(可以参考上面一个问题)
7.大概讲一下Transformer的Encoder模块?
8.为何在获取输入词向量之后需要对矩阵乘以embedding size的开方?意义是什么?
9.简单介绍一下Transformer的位置编码?有什么意义和优缺点?
10.你还了解哪些关于位置编码的技术,各自的优缺点是什么?
11.简单讲一下Transformer中的残差结构以及意义。
12.为什么transformer块使用LayerNorm而不是BatchNorm?LayerNorm 在Transformer的位置是哪里?
13.简答讲一下BatchNorm技术,以及它的优缺点。
14.简单描述一下Transformer中的前馈神经网络?使用了什么激活函数?相关优缺点?
15.Encoder端和Decoder端是如何进行交互的?(在这里可以问一下关于seq2seq的attention知识)
16.Decoder阶段的多头自注意力和encoder的多头自注意力有什么区别?(为什么需要decoder自注意力需要进行 sequence mask)
17.Transformer的并行化提现在哪个地方?Decoder端可以做并行化吗?
19.Transformer训练的时候学习率是如何设定的?Dropout是如何设定的,位置在哪里?Dropout 在测试的需要有什么需要注意的吗?
20.解码端的残差结构有没有把后续未被看见的mask信息添加进来,造成信息的泄露。
-------------补充内容----------
由Transformer衍生出来的bert预训练模型和GPT模型与transformer有着明显的区别。关于GPT,它基本上就是一个去掉了解码器层的transformer,每个解码器层包含一个自注意力(self-attention)层和一个前向神经网络(feed forward neural network)。然而,与完整的transformer解码器不同,GPT在自注意力层中使用了掩码(mask)机制,这防止了模型在生成序列的每个步骤中查看"未来"的信息。此外,由于GPT没有编码器,因此它不包含解码器中的编码器-解码器注意力层。适合做文本生成类的任务。
bert则正好相反,它只有transformer的编码器层,同时没有掩码机制,这使得它在训练时能够同时看到两侧的文本信息。相比之下,它更适合做文本分析、情感分析之类的任务。与此同时,作为一个预训练模型,它不能单独使用,还需要在标注数据集上进行进一步微调。bert的功能是生成每个单词的向量表示(此时的向量表示包含了上下文的信息)。面对各种实际任务,如分类、识别、问答等,还需要增加额外的输出层。
-------------一点个人的想法--------
Transformer模型衍生出来的bert和gpt(Generative Pre-trained Transformer)已经证明了transformer的价值。不过作为一个学习nlp不久的人,我觉得这个架构能实现这么好的效果真的是出人意料。transformer有个特点,它的模型参数量明显多于RNN等传统模型,这也意味着它对训练数据的数量要求更高。不过值得高兴的是,诸如bert这样的模型,它的训练数据可以直接来自百科、新闻等来源,不需要人工过多清洗。
此外,transformer的性能十分强大,但它仍然只是一个概率模型,并不具有逻辑能力。人们在使用诸如chatgpt这样的对话模型时所感受到的逻辑性,可能是因为它的训练数据就来自于各种论文、新闻等内容,语言和思维逻辑被暗含在了这些文字中,被模型巧合,或者说就是目的地学习了进去,因此它的语言生成看起来才有了逻辑。但这并不意味着它真的能够理解每个字背后的含义,同时这也是它“易产生幻觉”这个缺点的来源,毕竟它的输出语言看起来真的很有逻辑很有条理,缺少相关专业知识的话确实很容易轻信。
参考到的文章与博客:
2. 《深度学习进阶:自然语言处理》 、周志华《机器学习》
4. Transform结构详解_transform架构_DBY9909的博客
7. 论文解读:Attention is All you need
8. 深度学习 Transformer架构解析_落花雨时的博客
10. 经典算法·从seq2seq、attention到transformer
12. Transformer 一篇就够了(一): Self-attenstion -