理解Transformer的三层境界(待续...)


@author by xubing

0x00、先导知识

0.1 Seq2Seq

0.2 Attention


0x01.第一层境界

高屋建瓴,会当凌绝顶


  • 文章题目:《Attention is all you need》
  • 发表时间:2017.06
  • 作者:Google(几位作者全部是一作)

理解Transformer的三层境界(待续...)

  • 传统的编码器解码器一般使用RNN,这也是在机器翻译中最经典的模型。但RNN难以处理长序列的句子,无法实现并行,并且面临对齐问题。
  • Attention模型就是在Encoder和Decoder之间增加了Attention层,解决了长距离依赖的问题。

(长距离依赖信息”可以这么来理解:[1]一个词其实是一个可以表达多样性语义信息的符号(歧义问题)。[2]一个词的语义确定,要依赖其所在的上下文环境。(根据上下文消岐)[3]有的词可能需要一个范围较小的上下文环境就能确定其语义(短距离依赖现象),有的词可能需要一个范围较大的上下文环境才能确定其语义(长距离依赖现象)。)

  • 而本文提出的Transformer抛弃了传统的CNN和RNN,只使用Attention 机制。
  • 并且在Attention的基础增加了位置向量,保留了单词序列信息。
  • 还提出了两个新的Attention机制,Sacled Dot-Product Attention和Multi-head Attention。
  • Attention是Seq2Seq的升级版,Transformer是Attention的升级版。

总结

  • 本文的创新点:抛弃了之前传统的Encoder-Decoder模型必须结合CNN或RNN的固有模式,只用了Attention。提出了两个新的attention 机制,分别叫做Scaled Dot-Product Attention和Multi-head Attention。
  • 与之前的模型的对比: 减少了计算量、提高了并行效率、任务效果提升。
  • 模型的应用:机器翻译、文档摘要、对话系统…

0x02、第二层境界

由浅入深,由表及里,渐入佳境


宏观视角(A High-Level Look)

我们先把Transformer想象成一个黑盒子,在机器翻译的领域中,这个黑盒子的功能就是输入一种语言然后将它翻译成其他语言。如下图:
理解Transformer的三层境界(待续...)
打开这个黑盒子,可以看到,这个黑盒子由两个部分组成:Encoders和Decoders。(注意结尾加s)
理解Transformer的三层境界(待续...)
再进行进一步的剖析,可以看到Encoders由6个Encoder组成,Decoders由6个Decoder组成。
理解Transformer的三层境界(待续...)
打破砂锅拆到底,每一个Encoder由两部分组成,每一个Decoder由三部分组成。
理解Transformer的三层境界(待续...)
还可以再细分吗? - 可以,不过这里我们先解释一下每个层的功能。

  • 每个Encoder的输入首先会通过一个self-attention层,通过self-attention层帮助Endcoder在编码单词的过程中查看输入序列中的其他单词。
  • Self-attention的输出会被传入一个全连接的前馈神经网络,每个encoder的前馈神经网络参数个数都是相同的,但是他们的作用是独立的。
  • 每个Decoder也同样具有这样的层级结构,但是在这之间有一个Attention层,帮助Decoder专注于与输入句子中对应的那个单词(类似与seq2seq models的结构)

将张量引入图片(Bringing The Tensors Into The Picture)

用一个具体的例子来解释,观察从输入到输出的过程中这些数据在各个网络结构中的流动。
首先,进行embedding
理解Transformer的三层境界(待续...)
每个单词被映射到一个512维的向量。词嵌入(embedding)的过程只发生在最底层的一个Encoder。
理解Transformer的三层境界(待续...)

在每个单词进入Self-Attention层后都会有一个对应的输出。Self-Attention层中的输入和输出是存在依赖关系的,而前馈层则没有依赖,所以在前馈层,我们可以用到并行化来提升速率。(重点:并行在前馈层

开始“编码”(Now We’re Encoding! )

理解Transformer的三层境界(待续...)
如上图所示,每个位置的单词首先会经过一个self attention层,然后每个单词都通过一个独立的前馈神经网络(这些神经网络结构完全相同)。

从宏观视角看自注意力机制(Self-Attention at a High Level)

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正是transformer中设计的一种通过其上下文来理解当前词的一种办法。你会很容易发现…相较于RNNs,transformer具有更好的并行性。
    理解Transformer的三层境界(待续...)
    如上图,是我们第五层Encoder针对单词’it’的图示,可以发现,我们的Encoder在编码单词‘it’时,部分注意力机制集中在了‘animl’上,这部分的注意力会通过权值传递的方式影响到’it’的编码。

从微观视角看自注意力机制(Self-Attention in Detail)

计算self attention的第一步是从每个Encoder的输入向量上创建3个向量(在这个情况下,对每个单词做词嵌入)。所以,对于每个单词,我们创建一个Query向量,一个Key向量和一个Value向量。
如何产生QKV:这些向量是通过词嵌入乘以我们训练过程中创建的3个训练矩阵而产生的。(矩阵最开始是随机的)
以下是两个词的演示:
理解Transformer的三层境界(待续...)

  • 第1步:为每个单词生成qkv
    q1=X1WQ,q2=X2WQ,q_1 = X_1 * W^Q,q_2 = X_2 * W^Q,
    k1=X1WK,q2=X2WK,k_1 = X_1 * W^K,q_2 = X_2 * W^K,
    v1=X1WV,q2=X2WV,v_1 = X_1 * W^V,q_2 = X_2 * W^V,
  • 第2步:计算得分。将 query向量与每个key向量进行点乘,如下图。
    理解Transformer的三层境界(待续...)
  • 第3步:将第二步的得分除以dk\sqrt{d_k},(论文中使用key向量的维度是64维,其平方根=8,这样可以使得训练过程中具有更稳定的梯度。这个并不是唯一值,经验所得)。
  • 第4步:将得到的输出通过softmax函数标准化,使得最后的列表和为1。
    理解Transformer的三层境界(待续...)
    这个softmax的分数决定了当前单词在每个句子中每个单词位置的表示程度。很明显,当前单词对应句子中此单词所在位置的softmax的分数最高,但是,有时候attention机制也能关注到此单词外的其他单词,这很有用。
  • 第5步:将每个Value向量乘以softmax后的得分。这里实际上的意义在于保存对当前词的关注度不变的情况下,降低对不相关词的关注。
  • 第6步:累加加权值的向量。 这会在此位置产生self-attention层的输出(对于第一个单词)。
    理解Transformer的三层境界(待续...)
    总结self-attention的计算过程(单词级别),就是得到一个我们可以放到前馈神经网络的矢量。 然而在实际的实现过程中,该计算会以矩阵的形式完成,以便更快地处理。下面我们来看看Self-Attention的矩阵计算方式。

通过矩阵运算实现自注意力机制(Matrix Calculation of Self-Attention)

第一步是去计算Query,Key和Value矩阵。我们将词嵌入转化成矩阵X中,并将其乘以我们训练的权值矩阵WQ,WK,WVW^Q,W^K,W^V
理解Transformer的三层境界(待续...)
X矩阵中的每一行对应于输入句子中的一个单词。 我们看到的X每一行的方框数实际上是词嵌入的维度,图中所示的和论文中是有差距的。X(图中的4个方框论文中为512个)和q / k / v向量(图中的3个方框论文中为64个)
最后,由于我们正在处理矩阵,我们可以在一个公式中浓缩前面步骤2到6来计算self attention层的输出。
理解Transformer的三层境界(待续...)

“多头怪”(The Beast With Many Heads)

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

  • 1、它扩展了模型专注于不同位置的能力。在上面的例子中,虽然每个编码都在z1z_1中有或多或少的体现,但是它可能被实际的单词本身所支配。如果我们翻译一个句子,比如“The animal didn’t cross the street because it was too tired”,我们会想知道“it”指的是哪个词,这时模型的“多头”注意机制会起到作用。

  • 2、它给出了注意力层的多个“表示子空间”(representation subspaces)。接下来我们将看到,对于“多头”注意机制,我们有多个查询/键/值权重矩阵集(Transformer使用八个注意力头,因此我们对于每个编码器/解码器有八个矩阵集合)。这些集合中的每一个都是随机初始化的,在训练之后,每个集合都被用来将输入词嵌入(或来自较低编码器/解码器的向量)投影到不同的表示子空间中。

理解Transformer的三层境界(待续...)
通过multi-headed attention,我们为每个“header”都独立维护一套Q/K/V的权值矩阵。然后我们还是如之前单词级别的计算过程一样处理这些数据。
    如果对上面的例子做同样的self attention计算,而因为我们有8头attention,所以我们会在八个时间点去计算这些不同的权值矩阵,但最后结束时,我们会得到8个不同的ZZ矩阵。如下图:
理解Transformer的三层境界(待续...)

我们知道在self-attention后面紧跟着的是前馈神经网络,而前馈神经网络接受的是单个矩阵向量,而不是8个矩阵。所以我们需要一种办法,把这8个矩阵压缩成一个矩阵。我们将这8个矩阵连接在一起然后再与一个矩阵WOW^O相乘。步骤如下图所示:
理解Transformer的三层境界(待续...)
这样multi-headed self attention的全部内容就介绍完了。之前可能都是一些过程的图解,现在我将这些过程连接在一起,用一个整体的框图来表示一下计算的过程,希望可以加深理解。
理解Transformer的三层境界(待续...)
现在我们已经触及了attention的header,让我们重新审视我们之前的例子,看看例句中的“it”这个单词在不同的attention header情况下会有怎样不同的关注点。
理解Transformer的三层境界(待续...)
如图:当我们对“it”这个词进行编码时,一个注意力的焦点主要集中在“animal”上,而另一个注意力集中在“tired”,如果我们将所有注意力添加到图片中,那么事情可能更难理解:
理解Transformer的三层境界(待续...)

使用位置编码表示序列的顺序( Representing The Order of The Sequence Using Positional Encoding)

考虑输入序列中单词顺序。

  • 为了解决这个问题,transformer为每个输入单词的词嵌入上添加了一个新向量-位置向量。
  • 变换器为每个输入嵌入添加了一个向量。这些位置编码向量有固定的生成方式,所以获取他们是很方便的,但是这些信息确是很有用的,他们能捕捉到每个单词的位置,或者序列中不同单词之间的距离。将这些信息也添加到词嵌入中,然后与Q/K/V向量进行点积,获得的attention就有了距离的信息了。
    理解Transformer的三层境界(待续...)
    为了让模型捕捉到单词的顺序信息,我们添加位置编码向量信息(POSITIONAL ENCODING)-位置编码向量不需要训练,它有一个规则的产生方式。

如果我们的嵌入维度为4,那么实际上的位置编码就如下图所示:
理解Transformer的三层境界(待续...)

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

观察下面的图形,每一行都代表着对一个矢量的位置编码。因此第一行就是我们输入序列中第一个字的嵌入向量,每行都包含512个值,每个值介于1和-1之间。我们用颜色来表示1,-1之间的值,这样方便可视化的方式表现出来:
理解Transformer的三层境界(待续...)
这是一个20个字(行)的(512)列位置编码示例。你会发现它从中心位置被分为了2半,这是因为左半部分的值是一由一个正弦函数生成的,而右半部分是由另一个函数(余弦)生成。然后将它们连接起来形成每个位置编码矢量。
位置编码的公式在论文(3.5节)中有描述。你也可以在中查看用于生成位置编码的代码 get_timing_signal_1d().这不是位置编码的唯一可能方法。然而,它具有能够扩展到看不见的序列长度的优点(例如,如果我们训练的模型被要求翻译的句子比我们训练集中的任何句子都长)。

残差模块 (The Residuals)

这一节介绍的是encoder过程中的每个self-attention层的左右连接情况,我们称这个为:layer-normalization 步骤。如下图所示:
理解Transformer的三层境界(待续...)
再进一步探索其内部的计算方式,可以得到下图:
理解Transformer的三层境界(待续...)

Decoder的子层也是同样的,如果我们想做堆叠了2个Encoder和2个Decoder的Transformer,那么它可视化就会如下图所示:
理解Transformer的三层境界(待续...)

解码侧(The Decoder Side)

Decoder 和 Encoder基本上差不多,不过还是需要仔细探讨一下Decoder的数据计算原理。
编码器通过处理输入序列开启工作。顶端编码器的输出之后会变转化为一个包含向量K(键向量)和V(值向量)的注意力向量集 。这些向量将被每个解码器用于自身的“编码-解码注意力层”,而这些层可以帮助解码器关注输入序列哪些位置合适:
理解Transformer的三层境界(待续...)
在完成编码阶段后,则开始解码阶段。解码阶段的每个步骤都会输出一个输出序列(在这个例子里,是英语翻译的句子)的元素

接下来的步骤重复了这个过程,直到到达一个特殊的终止符号,它表示transformer的解码器已经完成了它的输出。每个步骤的输出在下一个时间步被提供给底端解码器,并且就像编码器之前做的那样,这些解码器会输出它们的解码结果 。另外,就像我们对编码器的输入所做的那样,我们会嵌入并添加位置编码给那些解码器,来表示每个单词的位置。

理解Transformer的三层境界(待续...)

而那些解码器中的自注意力层表现的模式与编码器不同:在解码器中,自注意力层只被允许处理输出序列中更靠前的那些位置。在softmax步骤前,它会把后面的位置给隐去(把它们设为-inf)。

这个“编码-解码注意力层”工作方式基本就像多头自注意力层一样,只不过它是通过在它下面的层来创造查询矩阵,并且从编码器的输出中取得键/值矩阵。

最终的线性变换和Softmax层(The Final Linear and Softmax Layer)

解码组件最后会输出一个实数向量。我们如何把浮点数变成一个单词?这便是线性变换层要做的工作,它之后就是Softmax层。

线性变换层是一个简单的全连接神经网络,它可以把解码组件产生的向量投射到一个比它大得多的、被称作对数几率(logits)的向量里。

不妨假设我们的模型从训练集中学习一万个不同的英语单词(我们模型的“输出词表”)。因此对数几率向量为一万个单元格长度的向量——每个单元格对应某一个单词的分数。

接下来的Softmax 层便会把那些分数变成概率(都为正数、上限1.0)。概率最高的单元格被选中,并且它对应的单词被作为这个时间步的输出。
理解Transformer的三层境界(待续...)
这张图片从底部以解码器组件产生的输出向量开始。之后它会转化出一个输出单词。

训练部分总结(Recap Of Training)

既然我们已经过了一遍完整的transformer的前向传播过程,那我们就可以直观感受一下它的训练过程。

在训练过程中,一个未经训练的模型会通过一个完全一样的前向传播。但因为我们用有标记的训练集来训练它,所以我们可以用它的输出去与真实的输出做比较。

为了把这个流程可视化,不妨假设我们的输出词汇仅仅包含六个单词:“a”, “am”, “i”, “thanks”, “student”以及 “”(end of sentence的缩写形式)。
理解Transformer的三层境界(待续...)
我们模型的输出词表在我们训练之前的预处理流程中就被设定好。

一旦我们定义了我们的输出词表,我们可以使用一个相同宽度的向量来表示我们词汇表中的每一个单词。这也被认为是一个one-hot 编码。所以,我们可以用下面这个向量来表示单词“am”:
理解Transformer的三层境界(待续...)
理解Transformer的三层境界(待续...)
接下来我们讨论模型的损失函数——这是我们用来在训练过程中优化的标准。通过它可以训练得到一个结果尽量准确的模型。

损失函数(The Loss Function)

比如说我们正在训练模型,现在是第一步,一个简单的例子——把“merci”翻译为“thanks”。

这意味着我们想要一个表示单词“thanks”概率分布的输出。但是因为这个模型还没被训练好,所以不太可能现在就出现这个结果。
理解Transformer的三层境界(待续...)
为模型的参数(权重)都被随机的生成,(未经训练的)模型产生的概率分布在每个单元格/单词里都赋予了随机的数值。我们可以用真实的输出来比较它,然后用反向传播算法来略微调整所有模型的权重,生成更接近结果的输出。

你会如何比较两个概率分布呢?我们可以简单地用其中一个减去另一个。更多细节请参考交叉熵和KL散度。
交叉熵:https://colah.github.io/posts/2015-09-Visual-Information/

KL散度:https://www.countbayesie.com/blog/2017/5/9/kullback-leibler-divergence-explained

但注意到这是一个过于简化的例子。更现实的情况是处理一个句子。例如,输入“je suis étudiant”并期望输出是“i am a student”。那我们就希望我们的模型能够成功地在这些情况下输出概率分布:

每个概率分布被一个以词表大小(我们的例子里是6,但现实情况通常是3000或10000)为宽度的向量所代表。

第一个概率分布在与“i”关联的单元格有最高的概率

第二个概率分布在与“am”关联的单元格有最高的概率

以此类推,第五个输出的分布表示“”关联的单元格有最高的概率
理解Transformer的三层境界(待续...)
依据例子训练模型得到的目标概率分布

在一个足够大的数据集上充分训练后,我们希望模型输出的概率分布看起来像这个样子:
理解Transformer的三层境界(待续...)

我们期望训练过后,模型会输出正确的翻译。当然如果这段话完全来自训练集,它并不是一个很好的评估指标(参考:交叉验证,链接https://www.youtube.com/watch?v=TIgfjmp-4BA)。注意到每个位置(词)都得到了一点概率,即使它不太可能成为那个时间步的输出——这是softmax的一个很有用的性质,它可以帮助模型训练。

因为这个模型一次只产生一个输出,不妨假设这个模型只选择概率最高的单词,并把剩下的词抛弃。这是其中一种方法(叫贪心解码)。另一个完成这个任务的方法是留住概率最靠高的两个单词(例如I和a),那么在下一步里,跑模型两次:其中一次假设第一个位置输出是单词“I”,而另一次假设第一个位置输出是单词“me”,并且无论哪个版本产生更少的误差,都保留概率最高的两个翻译结果。然后我们为第二和第三个位置重复这一步骤。这个方法被称作集束搜索(beam search)。在我们的例子中,集束宽度是2(因为保留了2个集束的结果,如第一和第二个位置),并且最终也返回两个集束的结果(top_beams也是2)。这些都是可以提前设定的参数。

再进一步(Go Forth And Transform)

我希望通过上文已经让你们了解到Transformer的主要概念了。如果你想在这个领域深入,我建议可以走以下几步:阅读Attention Is All You Need,Transformer博客和Tensor2Tensor announcement,以及看看Łukasz Kaiser的介绍,了解模型和细节。

Attention Is All You Need:https://arxiv.org/abs/1706.03762

Transformer博客:https://ai.googleblog.com/2017/08/transformer-novel-neural-network.html

Tensor2Tensor announcement:https://ai.googleblog.com/2017/06/accelerating-deep-learning-research.html

Łukasz Kaiser的介绍:https://colab.research.google.com/github/tensorflow/tensor2tensor/blob/master/tensor2tensor/notebooks/hello_t2t.ipynb

接下来可以研究的工作:

Depthwise Separable Convolutions for Neural Machine Translation

https://arxiv.org/abs/1706.03059

One Model To Learn Them All

https://arxiv.org/abs/1706.05137

Discrete Autoencoders for Sequence Models

https://arxiv.org/abs/1801.09797

Generating Wikipedia by Summarizing Long Sequences

https://arxiv.org/abs/1801.10198

Image Transformer

https://arxiv.org/abs/1802.05751

Training Tips for the Transformer Model

https://arxiv.org/abs/1804.00247

Self-Attention with Relative Position Representations

https://arxiv.org/abs/1803.02155

Fast Decoding in Sequence Models using Discrete Latent Variables

https://arxiv.org/abs/1803.03382

Adafactor: Adaptive Learning Rates with Sublinear Memory Cost

https://arxiv.org/abs/1804.04235

0x03.第三层境界

纸上得来终觉浅,绝知此事要躬行

参考文章:

1.https://www.jianshu.com/p/3f2d4bc126e6
2.https://www.imooc.com/article/51468
3.https://kexue.fm/archives/4765
4.https://blog.csdn.net/weixin_42446330/article/details/86710838
5.https://blog.csdn.net/qq_41664845/article/details/84969266
6.https://jalammar.github.io/illustrated-transformer/
7.[https://blog.csdn.net/longxinchen_ml/article/details/86533005] (https://blog.csdn.net/longxinchen_ml/article/details/86533005)