Transformer详解

        感谢各位大佬的博客分享,我在你们这里学到了许多,下面我就将结合大佬们的分享来总结一下我的学习内容。下面我们先介绍seq2seq,attention,然后进入正题,transformer。

一、seq2seq模型

1. 1 seq2seq模型简介

       所谓Seq2Seq(Sequence to Sequence), 就是一种能够根据给定的序列,通过特定的方法生成另一个序列的方法。

       举个简单的例子,当我们使用机器翻译时:输入(Hello) --->输出(你好)。再比如在人机对话中,我们问机器:“你是谁?”,机器会返回答案“我是某某某”。机器翻译、人机对话、聊天机器人等等,这些都是应用在当今社会都或多或少的运用到了我们这里所说的Seq2Seq。

       如图1,这是一个简单的邮件对话场景。图中的Encoder和Decoder都只展示一层普通的LSTMCell(这个cell不局限于LSTM)。从下面结构上来看,整个模型结构还是非常简单的,EncoderCell最后一个时刻的状态就是中间语义向量C,它将作为DecoderCell的初始状态。然后在DecoderCell中,每个时刻的输出将会作为下一个时刻的输入,以此类推,直到DecoderCell某个时刻预测输出特殊符号<END>结束。

图1.1 邮件对话

        这里我们将Encoder阶段叫做编码阶段。对应的Decoder阶段叫做解码阶段。中间语义向量C可以看做是所有的输入内容的一个集合,所有的输入内容都包括在C里面。详细内容会在后面继续讲解。

1.2 seq2seq原理解析

        首先,我们要明确Seq2Seq解决问题的主要思路是通过深度神经网络模型(常用的是LSTM)将一个作为输入的序列映射为一个作为输出的序列,这一过程由编码输入(encoder)与解码输出(decoder)两个环节组成,如图2。

图1.2 简单的Seq2Seq模型

         常见的encoder-decoder结构,其基本思想就是利用两个RNN,一个RNN作为encoder,另一个RNN作为decoder。

        Encoder负责将输入序列压缩成指定长度的向量,这个向量就可以看成是这个序列的语义,这个过程称为编码,如图1.2,获取语义向量最简单的方式就是直接将最后一个输入的隐状态作为语义向量C。也可以对最后一个隐含状态做一个变换得到语义向量,还可以将输入序列的所有隐含状态做一个变换得到语义变量。

        而decoder则负责根据语义向量生成指定的序列,这个过程也称为解码,如下图,最简单的方式是将encoder得到的语义变量作为初始状态输入到decoder的RNN中,得到输出序列。可以看到上一时刻的输出会作为当前时刻的输入,而且其中语义向量C只作为初始状态参与运算,后面的运算都与语义向量C无关,如图1.3。

图1.3 语义向量只作为初始化参数参与运算

      decoder处理方式还有另外一种,就是语义向量C参与序列所有时刻的运算,如下图1.4,上一时刻的输出仍然作为当前时刻的输入,但语义向量C会参与所有时刻的运算。

图1.4 语义向量参与解码的每个过程

       这里我们必须强调一点,Seq2Seq的实现程序设计好之后的输入序列和输出序列长度是不可变的。

如何训练seq2seq模型呢?下面详细讲解seq2seq的原理

        RNN是可以学习概率分布,然后进行预测,比如我们输入t时刻的数据后,预测t+1时刻的数据,比较常见的是字符预测例子或者时间序列预测。为了得到概率分布,一般会在RNN的输出层使用softmax激活函数,就可以得到每个分类的概率。

图1.5 encoder-decoder模型

1.3 seq2seq的应用场景

        下面我们具体看看,我们学习的Seq2Seq技术到底有什么应用场景呢?

        Seq2Seq的应用随着计算机技术、人工智能技术、算法研究等方面的发展以及社会发展的需求,它在许多领域产生了一些运用。目前,它主要的应用场景有(如果你感兴趣可以看看这里):

① 机器翻译(当前最为著名的Google翻译,就是完全基于Seq2Seq+Attention机制开发出来的)

② 聊天机器人

③ 文本摘要自动生成(今日头条等使用了该技术)

④ 图片描述自动生成

二、Attention注意力机制

       前面一章,我们有讲过它的基本原理。具体它是如何作用于Encoder-Decoder的呢?

        Encoder-Decoder 作为一种通用框架,在具体的自然语言处理任务上还不够精细化。换句话说,单纯的Encoder-Decoder 框架并不能有效的聚焦到输入目标上,这使得像 seq2seq 的模型在独自使用时并不能发挥最大功效。比如说在上图3中,编码器将输入编码成上下文变量 C,在解码时每一个输出 Y 都会不加区分的使用这个 C 进行解码。而注意力模型要做的事就是根据序列的每个时间步将编码器编码为不同 C,在解码时,结合每个不同的 C 进行解码输出,这样得到的结果会更加准确,如下所示:

图2.1基于attention的seq2seq

具体原理如下:

若从公式上难以理解,下面我们图上来进一步理解。

如:

图2.2 seq2seq Attention详解图

注:左侧为Encoder+输入,右侧为Decoder+输出。中间为Attention。

图2.3 seq2seq Attention第一步
图2.4 seq2seq Attention第二步
图2.5 seq2seq Attention第三步

下一个时间点:

       来到时间点2,之前的context vector可以作为输入和目标的单词串起来作为lstm的输入。之后又回到一个hiddn state。以此循环。

图2.6 seq2seq Attention第四步
图2.7 seq2seq Attention第五步

        另一方面,context vector和decoder的hidden state合起来通过一系列非线性转换以及softmax最后计算出概率。

2.2 Attention计算方法

我们先讲解一下第一种:Attention score function: dot

图2.8 Attention点乘计算法

         输入是encoder的所有hidden states H: 大小为(hidden dim, sequence length)。decoder在一个时间点上的hidden state,s:大小为(hidden dim, 1)。

第一步:H转置为(sequence length, hidden dim) 与s做点乘得到一个大小为(sequence length, 1)的向量。

第二步:对向量做softmax得到一个合为1的权重。

第三步:将H与第二步得到的权重做点乘得到一个大小为(hidden dim, 1)的context vector。

第二个 :Attention score function: general

图2.9 Attention点乘计算法

        输入是encoder的所有hidden states H: 大小为(hidden dim1, sequence length)。decoder在一个时间点上的hidden state , s:大小为(hidden dim2, 1)。此处两个hidden state的纬度并不一样。

第三个 :Attention score function:  concat

       这里就不在用图讲述啦!就简单说明一下。

       输入是encoder的所有hidden states H: 大小为(hidden dim, sequence length)。decoder在一个时间点上的hidden state , s:大小为(hidden dim, 1)。

三、Transformer

         这是google于2017年发表的大作,论文提出了一种序列见面的新方法:transformer,其模型依然是沿用了经典的Encoder-Decoder结构,不同的是不再使用RNN或是CNN作为序列建模机制了,而是使用self-attention机制(一种完全摈弃RNN单元的机制),一步到位获得全局信息,并获得并行训练。简单来说,可以将Transformer看成和RNN类似的特征提取器。

图3.1 Encoder-Decoder结构

        Transformer所使用的注意力机制的核心思想是:计算一句话中的每个词对于这句话中所有词的相互关系,然后认为这些词与词之间的相互关系在一定程度上反应了这句话中不同词之间的关联性以及重要程度。因此再利用这些相互关系来调整每个词的重要性(权重)就可以获得每个词新的表达。这个新的表征不但蕴含了该词本身,还蕴含了其他词与这个词的关系,因此和单纯的词向量相比是一个更加全局的表达。

        Transformer通过对输入的文本不断进行这样的注意力机制层得到最终的文本表达。

RNN/CNN/TRansformer比较:

       深度学习做NLP的方法,基本上都是先将句子分词,然后每个词转化为对应的词向量序列。这样一来,每个句子都对应的是一个矩阵X=(x1,x2,…,xt),其中xi都代表着第i个词的词向量(行向量),维度为d维,故X∈Rn×d。这样的话,问题就变成了编码这些序列了。

第一个基本的思路是RNN层,RNN的方案很简单,递归式进行:yt=f(yt−1,xt)

       不管是已经被广泛使用的LSTM还是GRU,都并未脱离这个递归框架。RNN结构本身比较简单,也很适合序列建模,但RNN的明显缺点之一就是无法并行,因此速度较慢,这是递归的天然缺陷。

第二个思路是CNN层,其实CNN的方案也是很自然的,窗口式遍历,比如尺寸为3的卷积,就是:yt=f(xt−1,xt,xt+1)

        Google的大作提供了第三个思路:纯Attention!单靠注意力就可以!RNN要逐步递归才能获得全局信息,因此一般要双向RNN才比较好;CNN事实上只能获取局部信息,是通过层叠来增大感受野;Attention的思路最为粗暴,它一步到位获取了全局信息!它的解决方案是:yt=f(xt,A,B)

     其中A,B是另外一个序列(矩阵)。如果都取A=B=X,那么就称为Self Attention,它的意思是直接将xt与原来的每个词进行比较,最后算出yt!

#Transformer

       1.先把Transformer想象成一个黑匣子,在机器翻译的领域中,这个黑匣子的功能就是输入一种语言然后将它翻译成其他语言。如图:

图3.2 黑匣子图

      2.打开The Transformer,我们可以看到一个编码组件、一个解码组件,和它俩之间的链接。

图3.3 Encoder-Decoder结构

       3.再对这个黑匣子进一步剖析,每个Encoders中分别由6个Encoder组成,每个Decoders中同样也是由6个Decoder组成。(论文中是这样配置的)

图3.4 Encoder-Decoder结构

       4.对于Encoders中的每一个Encoder,他们结构都是相同的,但是并不会共享权值。每层Encoder有2个部分组成,如下图:

图3.5 Encoder结构

         每个Encoder的输入首先会通过一个self-Attention层,通过self-Attention层帮助Endcoder在编码单词的过程中查看输入序列中的其他单词。Self-Attention的输出会被传入一个全连接的前馈神经网络,每个encoder的前馈神经网络参数个数都是相同的,但是他们的作用是独立的。

       一个decoder是在自注意力层和前馈网络层中间再加一个attention layer。

图3.6 Encoder-Decoder结构

#Self-Attention

1.Self-Attention的作用:

       假设下面的句子就是我们需要翻译的输入句:

”The animal didn't cross the street because it was too tired”

       这句话中的"it"指的是什么?它指的是“animal”还是“street”?对于人来说,这其实是一个很简单的问题,但是对于一个算法来说,处理这个问题其实并不容易。self attention的出现就是为了解决这个问题,通过self attention,我们将“it”与“animal”联系起来。

       当模型处理单词的时候,self attention层可以通过当前单词去查看其输入序列中的其他单词,以此来寻找编码这个单词更好的线索。即:self-attention是在计算每个单词对当前单词的贡献,也就是对每个单词对当前单词的贡献进行打分。

2.Self-Attention的结构:

Self-Attention结构如下:

图3.7 Scaled dot-product Attention

3.Self-Attention的定义:

        对于如何计算Self-Attention,论文作出了具体的定义:Attention函数的本质可以被描述为一个查询(query)到一系列(键key-值value)对的映射。google对attention的定义如下:

4.Self-Attention的向量计算:

        计算self attention的第一步:创造三个encoder的输入向量Q,K,V。这3个向量是通过词嵌入乘以我们训练过程中创建的3个训练矩阵而产生的。

         如下图,假设embedding向量的维度为4,我们希望得到一个维度为3的query、key和value向量,只需将每个embedding乘上一个维度为4*3的矩阵即可。这些矩阵就是训练过程中要学习的。

图3.8 Self-Attention的向量计算
图3.9 Self-Attention的向量计算
图3.10 Self-Attention的向量计算

      第五步是将每个Value向量乘以softmax后的得分(注意是对应位置相乘)。这里实际上的意义在于保存对当前词的关注度不变的情况下,降低对不相关词的关注。

       第六步是累加加权值的向量。 就得到了self-attention层对当前位置单词的输出(对于第一个单词)。对每个单词进行如上操作,就能得到整个句子的attention输出了。

图3.11 Self-Attention的向量计算

总结一下上面的步骤:

        注意:上面阐述的过程实际上是Attention机制的计算流程,对于self-Attention,Query=Value。

       总结self-attention的计算过程,(单词级别)得到一个可以放到前馈神经网络的矢量。,在实际操作中是直接将每个文字同时处理,因此会变成一个矩阵,而非单一词向量。下面来看看Self-Attention的矩阵计算方式。

5. Self-Attention的矩阵计算:

        计算Query,Key和Value矩阵:将embedding向量转化成矩阵X(假设有一句话有长度为2,embedding维度为4,那么X的维度为(2 × 4)),然后把通过训练得到的权重矩阵(W^Q, W^K, W^V)和X相乘。

      假定query、key、value的维度为3,构造一个维度为(4×3)的权值矩阵。将其与X做矩阵乘法,得到一个2×3的矩阵,即为Query,类似可以得到Key,Value。

图3.12 Self-Attention的矩阵计算

将Query和Key相乘,得到打分,然后经过softmax,接着乘上V的到最终的输出。

图3.13 Self-Attention的矩阵计算

这个过程可以看成是并行的。

#Multi-Head Self-Attention

        按照上面的矩阵计算,Query,Key,Value的维度是词向量的维度,215维,计算耗时多,所以google使用“Multi-Head”机制改善Attention层,实现并行计算。

Multi-Head Attention结构如下:

图3.14 Multi-Head Attention

        所谓“Multi-Head”,即:将词嵌入(或者来自较低层的Encoder/Decoder的矢量)投影到不同的“representation subspaces(表示子空间)”中,便于模型学习不同子空间位置的特征表示。其做法是:通过h个不同的线性变换(随机初始化生成h个矩阵)对Q,K,V进行投影,生成h套Q/K/V权重矩阵,然后还是如之前单词级别的计算过程一样处理这些数据,得到h个不同的attention结果。这样,通过multi-headed attention,为每个“head”都独立维护一套Q/K/V的权值矩阵。

     公式如下:

图3.15 Multi-Head Attention

论文具体做法如下:

       设置h=8,通过不同的8个矩阵W_i(随机初始生成,维度:512x64)对Q,K,V(维度512x64)矩阵进行投影,得到8个head,所以会在8个时间点去计算这些不同的权值矩阵,但最后结束时,会得到8个不同的Z矩阵。

图3.16 Multi-Head Attention

        最后,把这8个矩阵压缩成一个矩阵,将这8个矩阵拼接在一起,再与一个矩阵W^O相乘即可。

整个Self-Attention计算过程总结如下:

图3.17 Multi-Head Attention

X:是第一个encoder,输入是embedding向量。

R:是2-6个encoder,输入是前一个encoder的输出。

#Positional Embedding

        截止目前为止,我们介绍的Transformer模型并没有捕捉顺序序列的能力,也就是说无论句子的结构怎么打乱,Transformer都会得到类似的结果。换句话说,Transformer只是一个功能更强大的词袋模型而已。

        Transformer抛弃了RNN,而RNN最大的优点就是在时间序列上对数据的抽象,所以文章中作者提出两种Positional Encoding的方法,将Positional Encoding后的数据与输入embedding数据求和,加入了相对位置信息。

论文提到两种Positional Encoding方法:

1.不需要训练,用不同频率的sin和cos函数直接计算

2.学习出一份positional embedding(参考文献)。学习时注意,每个batch的pos emb都一样,即在batch维度进行broadcast。

       经过实验发现两者的结果一样,所以最后选择了第一种方法,公式如下:

图3.18 embedding

如果嵌入维度为4,那么实际上的位置编码就如下图所示:

图3.19 embedding

#The Residuals

        encoder过程中,每个self-attention层都有个左右连接子层,采用了残差网络中的layer-normalization步骤。

图3.20 encoder过程

#Decoder

        最后一层encoder的输出会被转化为一组attention vector:K和V,然后这一组attention vector会在每个decoder层的encoder-decoder attention层被用到,目的是帮助decoder专注在输入序列中合适的位置:

图3.21 decoder过程

         decoder中的self-attention layer和encoder中的有一点区别:在decoder中,自注意力层只能处理输出序列中当前词之前的序列,即对真实label y像encoder层的x一样做self-attention,但每个位置的y只与它之前的y有关(mask)。做法是在计算softmax之前把后面的词都设成 -inf。

       得到encoder层的key和value以及decoder层的query后,在encoder-decoder Attention层利用key和value以及query再做最后一个attention。“Encoder-Decoder Attention”层就和multiheaded self-attention的运作方式一样。得到每个位置的输出。

参考文献

[1] https://www.jianshu.com/p/b2b95f945a98

[2] https://blog.csdn.net/qq_32241189/article/details/81591456

[3] https://zhuanlan.zhihu.com/p/40920384

[4] https://blog.csdn.net/qq_42208267/article/details/84967446

[5] https://blog.csdn.net/Flying_sfeng/article/details/100996524

注:部分文字、图片来自网络,如涉及侵权,请及时与我们联系,我们会在第一时间删除或处理侵权内容,电话:4006770986。