Transform中的Attention注意力机制

本文边讲细节边配合代码实战,代码地址为:

https://github.com/princewen/tensorflow_practice/tree/master/basic/Basic-Transformer-Demo

数据地址为:https://pan.baidu.com/s/14XfprCqjmBKde9NmNZeCNg 密码:lfwu


1、Attention发展史

       Attention注意力机制现在大火,很多深度学习的框架都带上了注意力机制,而且也取得了很好的性能指标。Attention机制最早是应用于图像领域的,九几年就被提出来的思想。随着谷歌的一波研究捣鼓,2014年google mind团队发表的这篇论文《Recurrent Models of Visual Attention》让其开始火了起来,他们在RNN模型上使用了attention机制来进行图像分类,然后取得了很好的性能。然后就开始一发不可收拾了。随后Bahdanau等人在论文《Neural Machine Translation by Jointly Learning to Align and Translate》中,使用类似attention的机制在机器翻译任务上将翻译和对齐同时进行,他们的工作算是第一个将attention机制应用到NLP领域中。接着attention机制就被广泛应用在基于RNN/CNN等神经网络模型的各种NLP任务中去了,效果较之前有了很大的提高。2017年,google机器翻译团队发表的《Attention is all you need》中大量使用了自注意力(self-attention)机制来学习文本表示。这篇论文引起了超大的反应,本身这篇paper写的也很赞,很是让人大开眼界。因而自注意力机制也自然而然的成为了大家近期的研究热点,并在各种NLP任务上进行探索,纷纷都取得了很好的性能。

        Transformer是《Attention is all you need》论文里提出来的新的框架,其就是一个升级版的seq2seq,也是由一个encoder和一个decoder组成的。encoder对输入序列进行编码,decoder对encoder的结果进行解码。但是神奇的是,encoder和decoder都不用RNN,而且换成了多个attention。

2、整体框架

     整体架构看似复杂,其实就是一个Seq2Seq结构,简化一下,就是这样的:

    Encoder的输出和decoder的结合如下,即最后一个encoder的输出将和每一层的decoder进行结合:

     好了,我们主要关注的是每一层Encoder和每一层Decoder的内部结构。如下图所示:

     可以看到,Encoder的每一层有两个操作,分别是Self-Attention和Feed Forward;而Decoder的每一层有三个操作,分别是Self-Attention、Encoder-Decoder Attention以及Feed Forward操作。这里的Self-Attention和Encoder-Decoder Attention都是用的是Multi-Head Attention机制,这也是我们本文重点讲解的地方。

     在介绍之前,我们先介绍下我们的数据,经过处理之后,数据如下:

       很简单,上面部分是我们的x,也就是encoder的输入,下面部分是y,也就是decoder的输入,这是一个机器翻译的数据,x中的每一个id代表一个语言中的单词id,y中的每一个id代表另一种语言中的单词id。后面为0的部分是填充部分,代表这个句子的长度没有达到我们设置的最大长度,进行补齐。

3、Attention回顾

     Attention其实就是计算一种相关程度,看下面的例子:

       Attention通常可以进行如下描述,表示为将query(Q)和key-value pairs映射到输出上,其中query、每个key、每个value都是向量,输出是V中所有values的加权,其中权重是由Query和每个key计算出来的,计算方法分为三步:

1)计算比较Q和K的相似度,用f来表示:

2)将得到的相似度进行softmax归一化:

3)针对计算出来的权重,对所有的values进行加权求和,得到Attention向量:

上述列举的公式比较抽象,属于概念化解释,下面会列举self-attention、multi-head attention例子来阐明上述内容。

3.1、Self-Attention

       Self attention这个单词看起来好像每个人都知道是什么意思,但实质上他是算法领域中新出的概念,可以通过阅读:Attention is All You Need 来理解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层可以通过当前单词去查看其输入序列中的其他单词,以此来寻找编码这个单词更好的线索。

        如上图,是我们第五层Encoder针对单词'it'的图示,可以发现,我们的Encoder在编码单词‘it’时,部分注意力机制集中在了‘animl’上,这部分的注意力会通过权值传递的方式影响到'it'的编码。

       下面我们先介绍如何用向量的方式来计算self attention,然后再来看看它是如何使用矩阵来实现的。

        计算self attention的第一步是从每个Encoder的输入向量上创建3个向量(在这个情况下,对每个单词做词嵌入)。所以,对于每个单词,我们创建一个Query向量,一个Key向量和一个Value向量。这些向量是通过词嵌入乘以我们训练过程中创建的3个训练矩阵而产生的。

       注意这些新向量的维度比嵌入向量小。我们知道嵌入向量的维度为512,而这里的新向量的维度只有64维。新向量并不是必须小一些,这是网络架构上的选择需要适当降低系统的计算量。

        我们将X_{1}乘以W^{Q}的权重矩阵得到新向量q_{1}q_{1}既是“query”的向量。同理,最终我们可以对输入句子的每个单词创建“query”,
“key”,“value”的新向量表示形式。

        那么“query”,“key”,“value”是什么向量呢?有什么用呢?这些向量的概念是很抽象,但是它确实有助于计算注意力。不过先不用纠结去理解它,后面的的内容,会帮助你理解的。

        计算self attention的第二步是计算得分。以上图为例,假设我们在计算第一个单词“thinking”的self attention。我们需要根据这个单词对输入句子的每个单词进行评分。当我们在某个位置编码单词时,分数决定了对输入句子的其他单词的关照程度。

       通过将query向量和key向量点击来对相应的单词打分。所以,如果我们处理开始位置的的self attention,则第一个分数为q_{1}k_{1}的点积,第二个分数为q_{2}k_{2}的点积。如下图

       第三步和第四步的计算,是将第二部的得分除以8(\sqrt{d_{k}})(论文中使用key向量的维度是64维,其平方根=8,这样可以使得训练过程中具有更稳定的梯度。这个\sqrt{d_{k}}并不是唯一值,经验所得)。然后再将得到的输出通过softmax函数标准化,使得最后的列表和为1。

       这个softmax的分数决定了当前单词在每个句子中每个单词位置的表示程度。很明显,当前单词对应句子中此单词所在位置的softmax的分数最高,但是,有时候attention机制也能关注到此单词外的其他单词,这很有用。

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

      第六步是 累加加权值的向量。 这会在此位置产生self-attention层的输出(对于第一个单词)。

       总结self-attention的计算过程,(单词级别)就是得到一个我们可以放到前馈神经网络的矢量。 然而在实际的实现过程中,该计算会以矩阵的形式完成,以便更快地处理。下面我们来看看Self-Attention的矩阵计算方式。

Matrix Calculation of Self-Attention

       第一步是去计算Query,Key和Value矩阵。我们将词嵌入转化成矩阵X中,并将其乘以我们训练的权值矩阵(W^{Q},W^{K},W^{V}

       X矩阵中的每一行对应于输入句子中的一个单词。 我们看到的X每一行的方框数实际上是词嵌入的维度,图中所示的和论文中是有差距的。X(图中的4个方框论文中为512个)和q / k / v向量(图中的3个方框论文中为64个)

     最后,由于我们正在处理矩阵,我们可以在一个公式中浓缩前面步骤2到6来计算self attention层的输出。

3.2、Multi-Head Attention

       本文通过使用“Multi-headed”的机制来进一步完善self attention层。“Multi-headed”主要通过下面2中方式改善了attention层的性能:

      1. 它拓展了模型关注不同位置的能力。在上面例子中可以看出,”The animal didn't cross the street because it was too tired”,我们的attention机制计算出“it”指代的为“animal”,这在对语言的理解过程中是很有用的。

       2.它为attention层提供了多个“representation subspaces”。由下图可以看到,在self attention中,我们有多个个Query / Key / Value权重矩阵(Transformer使用8个attention heads)。这些集合中的每个矩阵都是随机初始化生成的。然后通过训练,用于将词嵌入(或者来自较低Encoder/Decoder的矢量)投影到不同的“representation subspaces(表示子空间)”中。(类似于CNN的conv卷积层)。

       通过multi-headed attention,我们为每个“header”都独立维护一套Q/K/V的权值矩阵。然后我们还是如之前单词级别的计算过程一样处理这些数据。

        如果对上面的例子做同样的self attention计算,而因为我们有8头attention,所以我们会在八个时间点去计算这些不同的权值矩阵,但最后结束时,我们会得到8个不同的Z矩阵。如下图:

        瞧瞧,这会给我们后续工作造成什么问题?

       我们知道在self-attention后面紧跟着的是前馈神经网络,而前馈神经网络接受的是单个矩阵向量,而不是8个矩阵。所以我们需要一种办法,把这8个矩阵压缩成一个矩阵。

       我们将这8个矩阵连接在一起然后再与一个矩阵W^{O}相乘。步骤如下图所示:

        这样multi-headed self attention的全部内容就介绍完了。之前可能都是一些过程的图解,现在我将这些过程连接在一起,用一个整体的框图来表示一下计算的过程,希望可以加深理解。

        现在我们已经触及了attention的header,让我们重新审视我们之前的例子,看看例句中的“it”这个单词在不同的attention header情况下会有怎样不同的关注点。

       如图:当我们对“it”这个词进行编码时,一个注意力的焦点主要集中在“animal”上,而另一个注意力集中在“tired” 

但是,如果我们将所有注意力添加到图片中,那么事情可能更难理解:

3.3、位置向量

       我们可能忽略了去介绍一个重要的内容,就是怎么考虑输入序列中单词顺序的方法。为了解决这个问题,transformer为每个输入单词的词嵌入上添加了一个新向量-位置向量。这些位置编码向量有固定的生成方式,所以获取他们是很方便的,但是这些信息确是很有用的,他们能捕捉每个单词的位置,或者序列中不同单词之间的距离。将这些信息也添加到词嵌入中,然后与Q/K/V向量点击,获得的attention就有了距离的信息了。

       为了让模型捕捉到单词的顺序信息,我们添加位置编码向量信息(POSITIONAL ENCODING)-位置编码向量不需要训练,它有一个规则的产生方式。

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

        那么生成位置向量需要遵循怎样的规则呢?

        观察下面的图形,每一行都代表着对一个矢量的位置编码。因此第一行就是我们输入序列中第一个字的嵌入向量,每行都包含512个值,每个值介于1和-1之间。我们用颜色来表示1,-1之间的值,这样方便可视化的方式表现出来:

        这是一个20个字(行)的(512)列位置编码示例。你会发现它咋中心位置被分为了2半,这是因为左半部分的值是一由一个正弦函数生成的,而右半部分是由另一个函数(余弦)生成。然后将它们连接起来形成每个位置编码矢量。

        位置编码的公式在论文(3.5节)中有描述。你也可以在中查看用于生成位置编码的代码get_timing_signal_1d()。这不是位置编码的唯一可能方法。然而,它具有能够扩展到看不见的序列长度的优点(例如,如果我们训练的模型被要求翻译的句子比我们训练集中的任何句子都长)。

3.4、Layer-Normalization

        这一节介绍的是encoder过程中的每个self-attention层的左右连接情况,我们称这个为:layer-normalization 步骤。如下图所示:

       在进一步探索其内部计算方式,我们可以将上面图层可视化为下图:

     Decoder的子层也是同样的,如果我们想做堆叠了2个Encoder和2个Decoder的Transformer,那么它可视化就会如下图所示:

3.5、All Processes

       我们已经基本介绍完了Encoder的大多数概念,我们基本上也可以预知Decoder是怎么工作的。现在我们来仔细探讨下Decoder的数据计算原理,当序列输入时,Encoder开始工作,最后在其顶层的Encoder输出矢量组成的列表,然后我们将其转化为一组attention的集合(K,V)。(K,V)将带入每个Decoder的“encoder-decoder attention”层中去计算(这样有助于decoder捕获输入序列的位置信息)。

       完成encoder阶段后,我们开始decoder阶段,decoder阶段中的每个步骤输出来自输出序列的元素(在这种情况下为英语翻译句子)。上面实际上已经是应用的阶段了,那我们训练阶段是如何的呢?

      我们以下图的步骤进行训练,直到输出一个特殊的符号<end of sentence>,表示已经完成了。 The output of each step is fed to the bottom decoder in the next time step, and the decoders bubble up their decoding results just like the encoders did. 对于Decoder,和Encoder一样,我们在每个Decoder的输入做词嵌入并添加上表示每个字位置的位置编码。

       Decoder中的self attention与Encoder的self attention略有不同:

       在Decoder中,self attention只关注输出序列中的较早的位置。这是在self attention计算中的softmax步骤之前屏蔽了特征位置(设置为 -inf)来完成的。

     “Encoder-Decoder Attention”层的工作方式与"Multi-Headed Self-Attention"一样,只是它从下面的层创建其Query矩阵,并在Encoder堆栈的输出中获取Key和Value的矩阵。

       Decoder的输出是浮点数的向量列表。我们是如何将其变成一个单词的呢?这就是最终的线性层和softmax层所做的工作。线性层是一个简单的全连接神经网络,它是由Decoder堆栈产生的向量投影到一个更大,更大的向量中,称为对数向量。

        假设实验中我们的模型从训练数据集上总共学习到1万个英语单词(“Output Vocabulary”)。这对应的Logits矢量也有1万个长度-每一段表示了一个唯一单词的得分。在线性层之后是一个softmax层,softmax将这些分数转换为概率。选取概率最高的索引,然后通过这个索引找到对应的单词作为输出。

      至此,Attention已经讲解完毕,想知道更多的相关知识,请参考链接1链接2

  • 19
    点赞
  • 75
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值