Transfomer详解

1. 前言

2017 年,Google 在论文 Attention is All you need 中提出了 Transformer 模型,其使用 Self-Attention 结构取代了在 NLP 任务中常用的 RNN 网络结构。相比 RNN网络结构,其最大的优点是可以并行计算,此后在此基础上又出现了GPT、Bert等优秀模型,这些优秀模型都是在Transformer的基础上衍生出来的。要想了解Transformer,就必须先了解"self-attention"。

2. 什么是self-attention?

Self-attention是早于transformer的,但是它是由于transformer才火起来。它可以应用在transformer和Bert等应用上,那么为什么要用到self-attention呢?它能解决什么问题呢?

2.1 为什么要用Self- attention机制

在某些应用场景,比如机器翻译,语音辨识,或者是语音翻译,在这些场景中,我们都是输入一段序列(计算机中用向量表示),再输出一段序列,这就是seq2seq任务,如果我们去实现,该如何操作。

  • 对于下图中的任务,分析每个单词的词性,最简单的办法就是将每一个向量单独作为一个输入,然后将其丢入到一个全连接之中,每个单独产生结果。但是这种方法的问题在于fully-connected 没办法考虑前后向量之间的联系,因为第一个saw是动词,而第二个saw是名词。那么转换另一个思路就是开一个window,每次将window中的向量一起输入到全连接网络中,如下图:
    Transfomer详解_第1张图片
    这种方式的问题在于如果遇到某些场景需要全部向量都考虑,但是我们无法事先知道这个场景中向量的数目,我们也就很难调整我们的window大小去适应。而且太大的话需要大量的参数,在这种情况下就需要用到自注意力机制了。

2.2 Self- attention机制是如何运作的

我们可以将自注意力机制看成一层能够接受所有向量的输入,然后输出向量的数目跟输入向量的数目的是一样的,可以认为它的每个输出都是考虑了整个向量集之后的结果,之后再将这些输出每个单独放入一个全连接网络中来得到输出,这样可以显著的降低全连接网络的参数量。并且这个也可以多次使用,自注意力后FC再自注意力再FC等等。
Transfomer详解_第2张图片

那么接下来的问题就是这个机制如何接受输入并考虑整个向量集之后而作出相应的输出,以第一个向量对应的第一个输出为例:怎样产生b1向量?

  • 首先在输入向量中找出与a1相关的向量(就是找出哪些是重要的,哪些是不重要的,因为我们不希望把整个sequence包含在一个window里面)
  • 每一个向量与a1关联的程度我们用一个数值a来表示,那么如何计算a,常见方法如下图,本文选择第一种方法,将左边的向量乘wq矩阵,右边向量乘wk矩阵,分别得出q和k,再将q和k进行点积运算(Dot-product)得出a
    Transfomer详解_第3张图片
  • 利用上述方法进行的话就是,将a1乘一个矩阵Wq得到q1,然后分别将a2,a3,a4分别乘Wk得到k2,k3,k4,特别注意a1也要乘Wk得到k1(因为也要和自身相比较),然后用q1分别于k1,k2,k3,k4进行Dot-product得到对应的a(attention score)然后经过soft-max得到a’

注:并不一定用soft-max,其他也行,比如relu,可以试试哪个效果更好

Transfomer详解_第4张图片

  • 然后根据关联性a’进行抽取重要资讯,我们将a1,a2,a3,a4乘上一个矩阵Wv分别得到v1,v2,v3,v4,再分别与对应的a’进行相乘,然后把得出的四个结果进行相加得到b1。这样一来我们之前所求的a’,哪个a’更大(及关联性更强)谁就和b1越接近。

Transfomer详解_第5张图片

  • 剩余三个也是一样的操作,比如b2的计算过程如下:
    Transfomer详解_第6张图片
接下来,我们用矩阵乘法的角度再看看上述过程是怎样得到的:
  • 由于每一个输入向量都需要计算q,k,v三个向量,因此可以利用矩阵的形式更加简洁方便的进行计算:

Transfomer详解_第7张图片

  • 计算α也是同理:也就是每个q和每个k进行点积运算,然后经过soft-max得到α’,用矩阵来表示就是下图所示:

  • 然后就是得出b的过程:
    Transfomer详解_第8张图片

  • 而经过上述的分析,整个过程就是一系列的矩阵乘法的过程,可以看到需要学习的参数只有Wq ,Wk,Wv 而已,通过训练数据进行找出来。

整个计算过程可以表示为:在这里插入图片描述
  总结一下步骤就是:

  1. 输入序列中每个单词之间的相关性得分,就是用Q中每一个向量与K中每一个向量计算点积,具体到矩阵的形式:a= Q ⋅ K^T
  2. 对于输入序列中每个单词之间的相关性得分进行归一化,归一化的目的主要是为了训练时梯度能够稳定,防止其结果过大。即(Q ⋅ K^T)/dk,dk就是K的维度
  3. 通过softmax函数,将每个单词之间的得分向量转换成[0,1]之间的概率分布,得到a’=softmax((Q ⋅ K^T)/dk)
  4. 根据每个单词之间的概率分布,然后乘上对应的Values值,α与V进行点积,attention=a’⋅V

2.3 Multi-head self- attention

在self- attention中我们使用q去寻找相关的k,但是这种相关并不是只有一种形式和定义,也许我们不能只有一个q,可能有多个q去负责不同种类的相关性。如何去操作呢?(以2个head为例)

  • 相比于之前的方法我们只是将q1再去乘两个不同的矩阵得到q11,q12,代表两种不同的相关性,既然q有两个,那么k和v也要有两个。但是在后续的计算中,每个q负责相对应k和v,不能交叉,如下图所示:
    Transfomer详解_第9张图片

2.4 Position embedding

在前面的自注意力过程中,我们并没有关注到各个输入向量之间的位置信息!这在一些场景里面是很关键的一部分信息,但是我们只是计算了各自的相关性没有考虑到位置信息,因此需要加上Position embedding:每个位置有一个专属的位置向量;
Transfomer详解_第10张图片

2.5 Self-attention与CNN区别

事实上,CNN是受限制的Self-attention,也就是Self-attention的特例,Self-attention可以通过某些设计和限制就可以变成CNN,完成和CNN同样的任务

2.6 RNN与Self-attention区别

RNN也是处理输入是向量序列问题的算法,其具体过程就是一开始有一个memory,它和第一个输入向量一起输入到RNN中将会输出一个向量,该向量一方面放入全连接网络中得到一个输出,另一方面和下一个输入向量一起作为下一个RNN的输入,以此类推。

Transfomer详解_第11张图片
Transfomer详解_第12张图片

它们的区别在于:

  • 对不同位置的考虑:在RNN中如果最后一个黄色的输出向量希望它能够与第一个输入的蓝色向量有关,那么就需要蓝色向量从一开始输入就一直被记得,直到最后一个,虽然有双向RNN,但是也不能解决这个问题;而在Self-attention则不用考虑到这个问题,计算就已经是统筹兼顾了
  • 计算的平行性:RNN是无法进行平行计算的,而Self-attention是可以的

3. 什么是Transformer

Transformer 其实就是一个Seq2seq的模型,输出的长度是由模型决定的,那么一般的seq2seq模型它里面会分成两块分别是Encoder和Decoder,将输入的向量给Encoder进行处理,处理后的结果交给Decoder,由Decoder来决定应该输出一个什么样的向量,原论文的transformer结构图如下:

Transformer的结构图,拆解开来,主要分为图上4个部分,其中最重要的就是2和3Encoder-Decoder部分,下面是对其四部分的讲解。

3.1 输入

首先输入inputs embedding后需要给每个word的词向量添加位置编码positional encoding,因为在Transformer中需要明确各个向量之间的位置关系,但在输入的时候有可能这个位置关系已经丢失了,那么就通过这个模块来告诉模型这些向量彼此之间的顺序关系

3.2 Encoder

从图中我们可以看出一个encoder由Multi-Head Attention 和 全连接神经网络Feed Forward Network构成
Transfomer详解_第13张图片
关于Encoder内部结构是什么样的:
Transfomer详解_第14张图片
实际上一个Encoder里面有很多个Block(一个Block不止有一层layer),而每一个Block实现的功能都是输入一排向量然后也是输出一排向量,而我们从图的右边可以看到每一个Block内部的实现,就是一排向量先经过自注意力机制后得到一排处理过的向量,那么再逐个放于全连接的全向网络之中,最终的输出也是一排向量。

但其实在Transformer中关于block的实现会更复杂一点:
Transfomer详解_第15张图片

其中不同点我已经圈出来了,其具体的流程为:

一排向量经过Self-attention之后的输出,需要和对应的输入向量一一相加起来,如图中的a需要和它对应的输入b相加起来,这种网络架构(将输出与输入相加)称为residual connection。 得到真正的输出之后,需要经过一次Layer normolization,其特别的地方在于是直接对向量进行标准化(减去均值除以标准差),因为向量中的每个元素是属于不同的维度的,属于不同的特征的。 将经过norm之后的向量作为全连接的前向网络的输入,然后输出需要再与输入相加,并且再一次经过norm,才能够真正作为这一层block的输出。

  • 其中Multi-Head Attention 很好理解就是上文所介绍的
  • Add&Normalize:每个编码器的每个子层(Self-Attention 层和 FFN 层)都有一个残差连接,再执行一个层标准化(LayerNormal)操作Add
    • 加入残差连接的目的是为了防止在深度神经网络训练中发生退化问题,退化的意思就是深度神经网络通过增加网络的层数,Loss逐渐减小,然后趋于稳定达到饱和,然后再继续增加网络层数,Loss反而增大
    • 在神经网络进行训练之前,都需要对于输入数据进行Normalize归一化,能够加快训练的速度且提高训练的稳定性。这里使用Layer Normalization(LN)而不是使用Batch Normalization(BN)

为什么会退化,为什么使用LN?

为什么深度神经网络会发生退化?
举个例子:假如某个神经网络的最优网络层数是18层,但是我们在设计的时候并不知道到底多少层是最优解,本着层数越深越好的理念,我们设计了32层,那么32层神经网络中有14层其实是多余地,我们要想达到18层神经网络的最优效果,必须保证这多出来的14层网络必须进行恒等映射,恒等映射的意思就是说,输入什么,输出就是什么,可以理解成F(x)=x这样的函数,因为只有进行了这样的恒等映射咱们才能保证这多出来的14层神经网络不会影响我们最优的效果。
但现实是神经网络的参数都是训练出来地,要想保证训练出来地参数能够很精确的完成F(x)=x的恒等映射其实是很困难地。多余的层数较少还好,对效果不会有很大影响,但多余的层数一多,可能结果就不是很理想了。这个时候大神们就提出了ResNet 残差神经网络来解决神经网络退化的问题。
为什么添加了残差块能防止神经网络退化问题呢?
咱们再来看看添加了残差块后,咱们之前说的要完成恒等映射的函数变成什么样子了。是不是就变成h(X)=F(X)+X,我们要让h(X)=X,那么是不是相当于只需要让F(X)=0就可以了,这里就巧妙了!神经网络通过训练变成0是比变成X容易很多地,因为大家都知道咱们一般初始化神经网络的参数的时候就是设置的[0,1]之间的随机数嘛。所以经过网络变换后很容易接近于0
这样当网络自行决定了哪些层为冗余层后,通过学习残差F(x)=0来让该层网络恒等映射上一层的输入,使得有了这些冗余层的网络效果与没有这些冗余层的网络效果相同,这样很大程度上解决了网络的退化问题。

为什么使用Layer Normalization(LN)而不使用Batch Normalization(BN)呢?
Transfomer详解_第16张图片
先看图,LN是在同一个样本中不同神经元之间进行归一化,而BN是在同一个batch中不同样本之间的同一位置的神经元之间进行归一化。
BN是对于相同的维度进行归一化,但是咱们NLP中输入的都是词向量,一个300维的词向量,单独去分析它的每一维是没有意义地,在每一维上进行归一化也是适合地,因此这里选用的是LN。

更好理解之间的区别还可以举例就是有b句话,每句话有len个词,每个词由d个特征表示,BN是对所有句子所有词的某一个特征做归一化,LN是对某一句话的所有词所有特征做归一化
Transfomer详解_第17张图片

3.3 Decoder

一个decoder由Masked Multi-Head Attention,Multi-Head Attention 和 全连接神经网络FNN构成。比Encoder多了一个Masked Multi-Head Attention,其他的结构与encoder相同,那么咱们就先来看看这个Masked Multi-Head Attention。

Decoder的输入分为两类:

  • 一种是训练时的输入训练时的输入,就是已经对准备好对应的target数据。例如翻译任务,Encoder输入"Tom chase Jerry",Decoder输入"汤姆追逐杰瑞"
  • 一种是预测时的输入,预测时的输入,一开始输入的是起始符,然后每次输入是上一时刻Transformer的输出。例如,输入"“,输出"汤姆”,输入"汤姆",输出"汤姆追逐",输入"汤姆追逐",输出"汤姆追逐杰瑞",输入"汤姆追逐杰瑞",输出"汤姆追逐杰瑞"结束。

Masked Multi-Head Attention与Encoder的Multi-Head Attention计算原理一样,只是多加了一个mask码。mask 表示掩码,它对某些值进行掩盖,使其在参数更新时不产生效果。Transformer 模型里面涉及两种 mask,分别是 padding mask 和 sequence mask

  • Padding mask :因为每个批次输入序列的长度是不一样的,所以我们要对输入序列进行对齐。具体来说,就是在较短的序列后面填充 0(但是如果输入的序列太长,则是截断,把多余的直接舍弃)。因为这些填充的位置,其实是没有什么意义的,所以我们的 Attention 机制不应该把注意力放在这些位置上,所以我们需要进行一些处理。具体的做法:把这些位置的值加上一个非常大的负数(负无穷),这样的话,经过 Softmax 后,这些位置的概率就会接近
  • sequence mask:与之前不同的是考虑吧的时候只能考虑a1,考虑b2时只能考虑a1,a2,原先的自注意力机制在考虑每一个向量的输出时,都是综合考虑了前后所有输入的向量
    但是在Masked中,每个向量要做输出时只能够考虑它之前的向量,不能够考虑它之后的向量
    为什么mask:因为在Decoder中,我们刚才认识到其工作流程是单个向量按照顺序的输入进去的,并不是跟Encoder一样所有向量一起进入,因此在输入当前的向量的时候它只知道之前已经输入的向量,它不知道未来输入的向量,那么它就只能够考虑之前的向量而不能够考虑之后的向量。

3.4 输出

Output如图中所示,首先经过一次线性变换,然后Softmax得到输出的概率分布,然后通过词典,输出概率最大的对应的单词作为我们的预测输出

3.5 Encoder与Decoder之间的信息交流

Encoder如何将其信息传递到Decoder:

实际上它们之间信息的传递就是用到下图中框框中的模块,该模块称为Cross attention,可以看到该模块接受Encoder两个输入,接受Decoder一个输入:
Transfomer详解_第18张图片
大致流程如下:
Transfomer详解_第19张图片

  • 首先Encoder中接受了输入之后产生了对应的输出向量,而Decoder中最开始的自注意力机制(带Mask)中也接受了BEGIN这个输入,产生对应的向量,并将该向量乘以一个矩阵Wq得到向量q
  • Encoder中的输出向量分别乘以矩阵K得到各个向量k,然后再将向量k个向量乘q去计算Attention的分数,这部分需要用到自注意力机制中计算分数α ,图片中加一个撇是表明这里的α \alphaα可以去进softmax变换
  • 将Encoder中的输出向量分别乘以矩阵V得到各个向量v,然后再其与对应的α相乘,并进行相加得到向量V(这里α 是一个常数,因此可以看成各个向量v的加权和
  • 得到的这个向量V就是Cross attention的输出,接下来会放入全连接的网络中进行处理
  • 同样的Decoder下一个输入进入也是进行相同的流程

另外一个值得注意的问题是Encoder有很多层,Decoder也有很多层,而在原始的论文中Decoder中的每一层的Crossattention都是用Encoder最后一层的输出

3.6 训练

Transformer如何进行训练
Transfomer详解_第20张图片
我们的输出BEGIN后得到的输出是一个分布,其中代表着取到每一个汉字的概率,而我们希望它输出的正确答案为一个One-hat-vector,那么损失函数就是这个分布和正确答案的向量之间的交叉熵,我们希望它们越接近越好,因此应该最小化它们之间的交叉熵
Transfomer详解_第21张图片
而在多个向量的时候也是同样的道理,我们希望每一个输出都能够和正确答案对应的向量之间的交叉熵足够小。但这边需要注意的是在训练的时候我们给Decoder看的是正确答案,例如上图给Decoder输入的是BEGIN、机器学习等都是正确的One-hat-vector,这种让机器在学习的时候看到正确答案的方法称为Teacher-Forcing,但在测试集的时候就不会给正确答案。

3.7 预测

  • 预测第t+1个输出时
  • 解码器中输入前t个预测值
    • 在自注意力中,前t个预测值作为k和v,第t个预测值还作为q
      Transfomer详解_第22张图片

4. 总结

  • Transformer 是一个纯使用注意力的编码-解码器
  • 编码-解码器都有n个transformer块
  • 每个块中使用多头(自)注意力,基于位置的前馈网络和层归一化

你可能感兴趣的:(深度学习论文笔记,深度学习,人工智能,自然语言处理)