写在前面:本文主要参考了Jay Alammar等大神的文章,这里对其相关文章进行了更精简的总结整理和扩充,更多内容请详见文末的参考文献,尊重原创。
——口天丶木乔

1. Attention

这小节我是打算初步总结一下attention模型或者说是机制,嫌麻烦的朋友可以跳过这小节,因为在下一节我会更加形象具体的介绍attetion模型。
原论文解释如下:
截屏2020-02-15上午11.53.19.png
咱们把它图像化一下:
v2-24927f5c33083c1322bc16fa9feb38fd_r.jpg
即,注意力机制(函数)就是将一个query和一系列key-value对儿映射为一个输出(这些全部为向量的形式)。通过对不同的key赋予不同的权重(注意力),从而按权相加计算出某一query的attention value(向量)。
深度学习中的注意力机制从本质上讲和人类的选择性视觉注意力机制类似,核心目标也是从众多信息中选择出对当前任务目标更关键的信息。大多数情况下,attention模型都是附着在encodere-decoder框架下。Encoder-Decoder框架可以看作是一种深度学习领域的研究模式,应用场景异常广泛。NLP(自然语言处理)领域中的encode-decoder框架如下:
v2-a5093fc7c0c4942b1d47e7cd2e65ea3b_r.jpg
该图展示的框架是没有体现出注意力模型的,可以把它看作是注意力不集中的分心模型,因为无论哪个单词的生成都是基于语义编码C的,对X1,X2并没有差别对待,所以它眼里并没有注意焦点,全都一样对待。而注意力机制的引入会在每个目标单词生成时计算不同输入单词的相应权重。以上就是大体的attention机制的简单介绍啦,那么具体的权重是如何运算的?attention机制又是如何附着在encoder-decoder中的?而attention机制的encoder-decoder从头到尾又是如何训练的?你现在肯定心里很混乱吧,别急,这小节只是给你普及一下attention到底是啥,让你初步了解心里有个数,至于一系列细节就往下看吧,下一小节,我会从transformer原论文入手(这篇也是提出self-attention的论文),一点一点带你揭开谜团,彻底理解它。

2. Transformer

2.1 概念

有人会说了,不是讲attention机制么,怎么多出来transformer这个没听过的名词,其实transfomer就是带有attention机制的一种encoder-decoder模型,其中还有一些增加的小细节。不过你先不用管这么多,心里记住transformer就是上节说的那个encoder-decoder框架的改进版(全部采用attention)就够了。
当然transformer的论文题目为attention is all your need,意思就是transformer整个结构中全部用的attention机制,并没有用到任何一个比如CNN,RNN等其他结构。这里也需要说明一下该论文之前就有许多引入attention的例子,不过并没有全部用attention机制,都是将RNN或LSTM与attention机制相融合,比如Google于2016年部署到线上的基于神经网络的机器翻译系统,相对传统模型翻译效果有大幅提升,翻译错误率降低了60%,其架构就是加上Attention机制的Encoder-Decoder框架,主要区别无非是其Encoder和Decoder使用了8层叠加的LSTM模型,咱们也可以把它看作是非常典型的使用RNN来解决NLP任务的通用框架技术大礼包,而我们后面将介绍的Transformer则又与这个有些不同:
截屏2020-02-16上午11.25.27.png
下面给出transformer原论文中采用的框架:
截屏2020-02-15下午1.01.31.png
看的是不是感觉脑袋要爆炸,思维混乱,别急就,接下来咱们一部分一部分对它进行剖析和消化,接下来的内容就比较专业化一点了,有基础的同学看起来会有一种恍然大悟的感觉,没基础的同学也可以看懂,只不过其中的细节你可能会有些疑惑,我相信看完下面的讲解,你一定会长进不少。
话不多说,接下来分别从transformer的整体俯瞰、encode模块和decoder模块三部分讲起,之后还会进一步结合实际例子从整体过一遍transformer的训练流程和损失函数的相关细节。

2.2 整体俯瞰

我们先把它看做是一个黑盒,以机器翻译为例,它的输入为一种语言的句子,输出为翻译过来的另一种语言的句子:
截屏2020-02-15下午1.16.44.png
打开黑盒,我们可以看到一个encoding(编码)模块,一个decoding(解码)模块,还有他俩之间的一个连接:
截屏2020-02-15下午1.19.03.png
encoder模块包含一系列串联的encoders(原论文是6个,数目多少并不是一定的),decoder模块有同样的结构:
截屏2020-02-15下午1.22.26.png

每个encoder有两层,self-attention层和FFNN层。ecoder的输入向量先流入self-attention层,这层的作用是帮助encoder在编码一个句子中的特定单词时查看比较该句子中的其他单词,咱们再之后再详细讲述这个self-attention。self-attention层输出向量再流入FFNN层。decoder几乎和encoder的架构一样,只不过它中间多了个encoder-decoder层,这层是帮助decoder聚焦于输入句子中的相关部分,这里可能有点绕,大家可能要花点时间理解,不过没理解没关系,往下看之后再回来也许你能更加恍然大悟。
截屏2020-02-15下午1.33.25.png

2.3 向量引入

上节整体介绍了transformer的结构,下面咱将实际向量引入进来,看看输入向量到底是如何通过这些组件一步步转换为输出向量的。以NLP领域为例,咱们先利用embedding算法将输入的单词转化为向量,假设向量大小为512,用下图表示这个512 size的向量:
截屏2020-02-15下午2.31.32.png

embedding向量列表作为第一层(最底层)encoder的输入,其他层encoder接受的是它前一层encoder的输出向量列表,向量列表长度(窗口)为超参数,一般为训练集中最长句子的长度。每个单词embedding向量作为独立链路输入进encoder,并在self-attention层相互关联作用后输出对应独立的self-attention向量,随后输入进FFNN层,在FFNN层每个链路并没有相互关联作用,所以在FFNN层可以对各个输入向量并行化处理。
截屏2020-02-15下午2.51.26.png
小小总结一下,一个encoder接受一个向量列表作为输入,先通过self-attention层对其处理,再进入FFNN层,最后将输出向量输送至下一个encoder。

2.4 self-attention导论

这一小节咱们对self-attention进行一下亲民的了解,毕竟这样咱们才能通俗易懂的理解它,至于更细节的self-attention的东西咱们下一小节再开始挖它,废话不多说,开整。
咱们现在要翻译一个句子:“The animal didn’t cross the street because it was too tired”,那么句子中的“it”代表啥?street还是animal?咱们人类可能很容易判断,不过算法就会想:“我太难了”,开个玩笑,有这个想法的话算法就无敌了,不过算法的确不容易解决这个问题。所以,self-attention应运而生,在一定程度上帮助算法解决这一问题,将“it”与“animal”联系起来。
当模型在处理句子中的每个单词时,self-attention允许它在句子中的其他单词中寻找线索,以便生成该单词的最佳encoding向量。如果你对RNN比较熟悉的话,可以想想RNN结构是如何保持隐藏层状态来将过去处理的向量与目前的向量相结合的,与之相似self-attention是transformer的一种方法,它将句子中与目前处理单词相关的单词信息考虑进来,综合形成当前处理的单词的向量。看到这,思维灵敏的同学会发现,那RNN(LSTM)与self-attention有啥不同呢?别急,这个问题先记下,在文末咱们再好好总结一番,别打扰到咱的正事,继续向前!下图是一个比较直观的例子,可以看出,对“it”的encoder更聚焦于“The”和“animal”这两个词。
总之,self Attention不仅可以得到源端与目标端词与词之间的依赖关系,同时还可以有效获取源端或目标端自身词与词之间的依赖关系,这是它比其他attention模型效果好的主要原因(soft/hard/global/local attention)。
截屏2020-02-15下午3.21.07.png

2.5 self-attention细节

在这一小节,咱们先看看如何根据输入向量来计算生成self-attention向量,随后再看看如何使用矩阵进行整体批量运算。

  1. 为每个encoder输入向量创建三个新向量:Query向量,Key向量和Value向量。这里你肯定会问这仨向量是啥?咋获得?别急,这仨向量的意义在之后步骤中你会深深体会到,现在先卖个关子。先说说怎么或得这仨,咱们可以通过将输入向量乘以我们在训练过程中训练的三个矩阵来创建这仨向量,下图即X1乘review Attention机制及Transformer - 图13就得到review Attention机制及Transformer - 图14了,以此类推,其中review Attention机制及Transformer - 图15为训练的参数矩阵;
  2. 为每个input计算它与句子中其他单词的分数,这个分数决定了当咱们对某个单词进行encoding时,将注意力集中在输入句子的其他部分上的程度。分数由Query向量与Key向量的点积计算得出;
  3. 将分数除以8(Key向量的维度的平方根,论文中维度为64),这样做会是训练有更稳定的收敛(more stable gradients);
  4. 随后将结果softmax一下,使分数均为正数,而且加和为1;
  5. 将每个softmax值与相应的Value向量相乘,这样的直觉是能聚焦于softmax值大的单词;
  6. 把赋权的Value向量相加,作为self-attention层中该词对应的输出向量。

截屏2020-02-15下午4.10.42.png
在实际操作中,上述一系列操作其实会通过矩阵来一起批量计算,这样就和原论文的表达形式一致了,原论文如下:
截屏2020-02-15下午4.13.22.png
咱们把它形象化一下:
截屏2020-02-15下午4.14.27.png 截屏2020-02-15下午4.14.54.png
其中,X矩阵的行数就代表输入的单词向量的个数,这样通过一个矩阵就可以批量并行计算全部的输入,甚是方便,但如果你要是非转不过来这个弯,我建议你还是从上面单个的进行理解,等理解透彻了自然而然就会过渡到这个矩阵阶段,祝你成功。

2.6 Multi-Head Attention

论文中还提到了一个改进,即Multi-Head Attention,这样做有两个好处:

  1. 它能够增强模型聚焦于不同位置的能力;
  2. 使self-attention层有多个“表示空间”;Multi-Head Attention会有多个Query/Key/Value矩阵,他们都是随机初始化,在训练后,每个都会将输入向量列表映射到不同的表示空间上。

假如咱们做8次相同的self-attention计算(原论文head数目),仅仅是每次的参数矩阵不同,最后我们仍会得到8个不同的输出矩阵,但FFNN希望的输入是一个单独矩阵大小,因此咱们需要将这个8个矩阵转换,转换方法就是乘以一个额外权重矩阵review Attention机制及Transformer - 图20,这样最终得到的矩阵就和原来一样大小,而且捕捉了全部attention head的信息:
截屏2020-02-18上午7.32.19.png
咱们将之前可视化的图像基于2个multihead重新看一下,可以看出其中一个attention head聚焦于“the animal”,而另一个聚焦于“tired”,所以最终模型表达的“it”(向量)会主要聚焦于这两部分,当然咱们把8个multihead复现一下就会发现比较难解释了,不过毕竟效果才是王道,大家可以自己根据任务进行head数量的调参,毕竟对于AI深度学习领域,经验(黑盒)先于可解释性早已是世人皆知了:
截屏2020-02-18上午7.35.39.png 截屏2020-02-18上午7.40.53.png

2.7 Transformer中的attention

Transformer中共有三处不同的地方(场景)使用Multi-head attention:
截屏2020-02-22下午9.50.39.png

  • encoder部分的self attention
  • decoder部分的self attention
  • 连接encoder stack和decoder stack的“encoder-decoder attention”

    2.7 Positional Encoding

    因为输入句子中每个单词的位置信息也极为重要,所以transformer对每个输入词向量(encoding)添加了一个位置向量(encoding):
    截屏2020-02-18上午7.46.32.png
    原论文给出的位置向量计算公式如下:
    截屏2020-02-18上午8.26.26.png
    进一步可以通过get_timing_signal_1d 查看源码,这里咱就不再细述了。

    2.8 Residuals

    transformer还需要注意的一个细节就是,每个子层(self-attention和FFNN)都会采用残差连接(residual connection),然后进行层归一化操作(layer-normalization),这里也不再细述了,想回顾这些基础的同学可以参考我的另两篇博文。
    截屏2020-02-18上午7.57.35.png截屏2020-02-18上午7.59.00.png
    到这encoder的整体结构和细节就讲完啦,现在再看一看原论文给出的encoders架构是不是清楚了一些:
    截屏2020-02-18上午8.18.10.png

    2.9 Decoder Side

    注意encoder输入进decoder的矩阵是encoder的参数矩阵K和V,而不是最终encoder输出词向量矩阵Z。
    最后一层encoder的输出转化为一系列的attention向量K和V,他们进一步被应用在每个decoder的“encoder-decoder attention”层。
    下面动图形象化一下,咱们看一看句子的第一个词是如何翻译过来的:
    transformer_decoding_1.gif
    进一步,咱们动图看一看第一个词之后其他词是如何翻译过来的,这里需要注意仅仅上述第一个词的过程与其他的略有不同:
    transformer_decoding_2.gif
    decoder的self-attention层的操作与encoder略有不同,从上图也可以看出,decoder的该层的计算只关注它之前输入的单词,它之后的单词则通过设置为-inf被mask掉,这也是为啥原论文命名deocer的第一层为Masked Muti-head Attention的原因。
    decoder的“Encoder-Decoder Attention”层与multiheaded self-attention几乎一样,只不过它的Q来源于上一层,而K和V来源于encoders模块,所以原论文直接用multiheaded self-attention命名它,而这里咱们为了更细节的区分它,就用Encoder-Decoder Attention嘞,所以说不要迷信权威,要根据自己量身定做最容易最适合自己学习的架构,哪怕仅仅是一个名称的命名,明白这一点你也就离你眼中的大神不远了,记住永远不要死读书,读死书。
    而对于decoder中的FFNN层,文章很早的一开始就说过了,transformer的所有FFNN层采用相同的构造,原论文描述如下:
    截屏2020-02-18上午8.25.54.png

    2.10 Final Linear and Softmax Layer

    decoder的输出为向量的形式,如何将其转化为单词?这就需要在最顶层deocder后加一个Linear层和一个Softmax层。
    Linear层是一个全连接神经网络,负责将向量映射到一个非常大的空间向量(logits vector)中,假设咱们的模型知道10000个独立的英语单词,那么这个logits vector就会有10000的长度,每个单元对应每个单词对应的分数(和one-hot表示有些相像)。
    Softmax则将这些分数转化为相应的概率(全部为正,相加为1),转化后选取概率最大的单元对应的单元作为这一步最终的输出。
    截屏2020-02-18上午8.37.40.png
    其实这种全连接层加softmax的形式在其他很多框架中都有应用,而且其中涉及到很多优化的trick,是极其重要的,不然这个部分的复杂度就太高了,实际应用起来不太现实,这里就不再细述了,想深挖的同学可以参考我的另一篇博文
    到这儿decoder模块的细节也全部阐述完毕嘞,现在回头看一看原论文的transformer框架是不是更清楚了:
    截屏2020-02-18上午8.36.11.png

    2.11 Loss Function

    Screen Shot 2020-05-26 at 7.30.36 AM.png

    3. 不同attention机制

    soft-attention hard-attention self-attention multihead-self-attention global-attention local-attention

soft/hard-attention

软性注意力,其选择的信息是所有输入信息在注意力 分布下的期望。
硬性注意力,只关注到某一个位置上的信息。有两种方式:1. 选取最高概率的输入信息。2.通过在注意力分布式上随机采样的方式实现

软注意力又分为两种:
普通模式(K=V=X)和键值对模式(K!=V),而self-attention(Q=K=V),即self-attention是对X自身的变换,而传统soft-attention的Q来自于外部。

硬性注意力的一个缺点是基于最大采样或随机采样的方式来选择信息。因此最终的损失函数与注意力分布之间的函数关系不可导,因此无法使用在反向传播算法进行训练。为了使用反向传播算法,一般使用软性注意力来代替硬性注意力。硬性注意力需要通过强化学习来进行训练。
所以一般神经网络都采用软性注意力机制

global/local-attention

细节问题思考

1. Transformer中的并行化

encoder和decoder在训练阶段均并行化。 encoder依赖于self-attention机制。 decoder依赖于teacher-forcing和masked self attention机制。 但decoder在预测阶段是非并行的,每个的输入依赖前一个的输出的正常方式。

参考文献

  1. The Illustrated Transformer
  2. 深度学习中的注意力模型(2017版)
  3. 放弃幻想,全面拥抱Transformer
  4. Attention is all you need
  5. The Annotated Transformer
  6. nlp中的Attention注意力机制+Transformer详解
  7. 多种Attention之间的对比(上)
  8. 浅析Transformer训练时并行问题