NLP-NMT-Transformer图解及论文笔记:Attention Is All You Need

前言

机器翻译技术的发展一直与计算机技术、信息论、语言学等学科的发展紧密相随。从早期的词典匹配,到词典结合语言学专家知识的规则翻译,再到基于语料库的统计机器翻译,随着计算机运算能力的提升和多语言信息资源的爆发式增长,机器翻译技术逐渐走出象牙塔,开始为普通用户提供实时便捷的翻译服务。

Attention机制最早在视觉领域提出,2014年Google Mind发表了《Recurrent Models of Visual Attention》,使Attention机制流行起来,这篇论文采用了RNN模型,并加入了Attention机制来进行图像的分类。

205年,Bahdanau等人在论文《Neural Machine Translation by Jointly Learning to Align and Translate》中,将attention机制首次应用在nlp领域,其采用Seq2Seq+Attention模型来进行机器翻译,并且得到了效果的提升,Seq2Seq With Attention中进行了介绍。

2017 年,Google 机器翻译团队发表的《Attention is All You Need》中,完全抛弃了RNN和CNN等网络结构,而仅仅采用Attention机制来进行机器翻译任务,并且取得了很好的效果,注意力机制也成为了大家近期的研究热点。

此篇文章介绍2017年提出的机器翻译领域的重大突破框架Transformer。该框架发表在NIPS 2017论文《Attention is All You Need》上

论文链接:https://arxiv.org/abs/1706.03762
github:https://github.com/jadore801120/attention-is-all-you-need-pytorch
参考:https://jalammar.github.io/illustrated-transformer/
https://blog.csdn.net/qq_42208267/article/details/84967446
https://blog.csdn.net/Zhangbei_/article/details/85036948
https://zhuanlan.zhihu.com/p/46990010
https://zhuanlan.zhihu.com/p/34781297
https://yq.aliyun.com/articles/342508?utm_content=m_39938

1. 框架总览

机器翻译任务顾名思义就是进行翻译,通过输入A语言通过计算获得尽可能相似的B语言输出,整个框架简单来说可以概括成如下所示:
在这里插入图片描述
其中THE TRANSFORMER可以进一步抽象成编码器和解码器。因为两种语言通过嵌入成向量表示后,由于语言本身的特性,即使两个向量高度相似但是标识出来的意思可能相差很大,因此需要对A语言输入嵌入到更高维度的向量化表示,进而通过解码器输出目标语言B
在这里插入图片描述
编码组件是由编码器堆叠起来组成的(论文里是由6个编码器堆成,选择6这个数字没什么特别的原因,你当然可以试试其他方案)。解码组件也是用同样数量的解码器组成。
在这里插入图片描述
**每个编码器结构相同,但是不共享权重。每一个编码器由两个子层组成。**在这里编码器的设计主要考虑了翻译问题的特点,比如翻译一个词的时候要综合考虑整个句子的上下文信息,在后文会详细说明为了解决此问题提出的注意力机制
在这里插入图片描述
编码器输入首先经过一个自注意力层(self-attention layer),自注意力层可以帮助编码器在编码具体单词时查看句子中的其他单词。自注意力层的输出会进入到一个前馈神经网络。完全相同的前馈神经网络独立作用于每个位置。

解码器也有同样的两个层,但是在自注意层和前馈网络层之间有一个注意力层,注意力层可以帮助解码器注意输入句子的其他部分

2. 向量化

由于计算机只能处理数字数据,我们需要将每种语言的不同单词以向量化的形式表示。具体来说就是通过词嵌入技术将单词变为向量
在这里插入图片描述
嵌入(embedding)只会在编码器最底层使用。概括的说就是所有编码器都接收一个由很多512大小的向量组成的列表,在最底层的编码器接收的是词嵌入层的输出(就是词转为向量,接收向量),但是在其他层的编码器直接接收前一层的输出。这个列表长度(大小)是一个可以设置的超参数,一般是我们训练集中最长句子的长度

当我们句子中的词经过嵌入层变成向量后,它们都会经过编码器的两个层。

在这里插入图片描述
这里我们可以看到Transformer的一个关键性质——每个位置的词经过编码器时都有自己的路径。路径在自注意力层中相互依赖,但是在前馈层中没有依赖关系,因此各个路径通过前馈层时可以并行执行。

3. Transformer Architecture

在这里插入图片描述
论文中的形式
在这里插入图片描述
其中论文中给出的Multi-Head Attention其实就是Self-Attention的进阶形式,两者在本质上是一样的

3.1 Encoder

  • 输入:词向量加上了positional embedding
  • 输入的序列长度是n,embedding维度是d,所以输入是n*d的矩阵
  • N=6,6个重复一样的结构,由两个子层组成
  • 子层1: O u t p u t = L N ( x + s u b l a y e r ( x ) ) Output = LN (x+sublayer(x)) Output=LN(x+sublayer(x))
  • 子层2:Position-wise fc层(跟卷积很像),对n*d的矩阵的每一行进行操作(相当于把矩阵每一行铺平,接一个FC),同一层的不同行FC层用一样的参数,不同层用不同的参数(对于全连接的节点数目,先从512变大为2048,再缩小为512),这里的max表示使用relu激活函数: F F N ( x ) = m a x ( 0 , x W 1 + b 1 ) W 2 + b 2 FFN(x)=max(0,xW_1+b_1)W_2+b_2 FFN(x)=max(0,xW1+b1)W2+b2
  • 整个encoder的输出也是n*d的矩阵

3.1.1Positional Encoding

因为模型不包括recurrence/convolution,因此是无法捕捉到序列顺序信息的,例如将K、V按行进行打乱,那么Attention之后的结果是一样的。但是序列信息非常重要,代表着全局的结构,因此必须将序列的token相对或者绝对position信息利用起来。

为了标记位置,transformer对每一个输入的嵌入层(embedding)都加了一个向量。这些向量遵循模型学习的特定模式(意思就是向量的维度和模型要的维度一样。)。这有助于决定每个单词的位置,或者句子中不同单词的距离。这里的逻辑是,将这些值添加到嵌入层,嵌入层向量会被投影到Q/K/V向量中,Q/K/V在注意力期间进行点积计算时,就有了位置信息。

给位置1,2,3,4…n等编码(也用一个embedding表示)。然后在编码的时候可以使用正弦和余弦函数,使得位置编码具有周期性,并且有很好的表示相对位置的关系的特性(对于任意的偏移量k,PE[pos+k]可以由PE[pos]表示):
在这里插入图片描述
其中 p o s pos pos 表示位置index, i i i 表示dimension index。

Position Embedding本身是一个绝对位置的信息,但在语言中,相对位置也很重要,Google选择前述的位置向量公式的一个重要原因是:由于我们有
在这里插入图片描述
这表明位置p+k的向量可以表示成位置p的向量的线性变换,这提供了表达相对位置信息的可能性。

在其他NLP论文中,大家也都看过position embedding,通常是一个训练的向量,但是position embedding只是extra features,有该信息会更好,但是没有性能也不会产生极大下降,因为RNN、CNN本身就能够捕捉到位置信息,但是在Transformer模型中,Position Embedding是位置信息的唯一来源,因此是该模型的核心成分,并非是辅助性质的特征。

也可以采用训练的position embedding,但是试验结果表明相差不大,因此论文选择了sin position embedding,因为

  1. 这样可以直接计算embedding而不需要训练,减少了训练参数
  2. 这样允许模型将position embedding扩展到超过了training set中最长position的position,例如测试集中出现了更大的position,sin position embedding依然可以给出结果,但不存在训练到的embedding。

3.1.2 Self-Attention and Multi-Head Attention

第一步,自注意力机制会将编码器输入向量(这个例子中,输入向量是每个词经过embedding后的向量)变为三个向量。所以对于每个词,我们会创建一个Query向量,一个Key向量,一个Value向量。这三个向量是由词嵌入的向量乘以我们训练得出的三个矩阵得到的。

注意,这些新向量维度小于词嵌入的向量。当嵌入层和编码器输入和输出的向量维度是512时,这些向量的维度是64。但是他们并非一定要比原向量更小,这是一种架构选择,它可以使多头注意力(multiheaded attention)(大多数的多头注意力)计算保持不变。
在这里插入图片描述
X1乘以WQ得到q1,X2乘以WQ得到q2,(WQ是我们训练出来得到的)以此类推,最后得到所有的Query,Key,Value.。

那究竟什么是“query”, “key”, and “value” 向量呢?

他们就是抽象出来方便理解和计算注意力的。看下文你就知道他们是什么意思了。

第二步,计算注意力的第二步是打分。假设我们计算例子中的第一个单词“Think”。我们需要根据这个单词给输入句子中的每一个词打分。当我们编码当前位置的单词的时候,分数决定了我们给其他位置的单词多少关注(权重)。

分数是通过点乘(dot) 当前位置单词的query 向量 和 需要打分位置单词的key 向量 得到的。所以如果我们在位置#1使用注意力给其他部分打分,第一个分数是(qi dot k1),第二个分数是(q1 dot k2)。

第三步和第四步,第三步和第四步是将维度除以8(key 向量维度的平方根,论文里key维度是64。目的是得到更稳定的梯度。当然可以是其他值,不过这个是默认的),然后将结果通过softmax处理。softmax标准化(softmax normalizes)可以将值映射(压缩、标准化)到0-1之间。

**第五步,第五步是将每个 value 向量乘以 softmax处理后的得分(为相加做准备)。**这一步的目的是保留重要的词(想要关注的词)的完整性,去除不相关的词。(如给不相干的词乘以一个极小的数-0.001)

**第六步将权重值向量相加。**结果就是自注意力层(self-attention layer)在这个位置的输出(本例子里是第一个词)

在这里插入图片描述
这就是自注意力的整个计算过程。计算后的结果我们会传递给前向反馈神经网络。然而在实际应用中,计算都是以矩阵形式而不是以向量,因为矩阵运算速度快。

让我们在梳理一遍

  1. 我们获得了有多个单词组成矩阵的词嵌入矩阵,基于此我们可以计算出Query,Key,和Value矩阵。他们是通过嵌入层(embedding)输入的矩阵X和我们训练出来的权重矩阵(WQ,WK,WV)相乘得到的
    在这里插入图片描述
  2. 每个单词代表的Query向量与其他所有单词(包括自己)的Key向量相乘得到自身与其他所有单词的(包括自己)的关联程度,也可以认为是注意力评分
  3. 将注意力评分矩阵除以key 向量维度的平方根,论文里key维度是64。目的是得到更稳定的梯度。当然可以是其他值,不过这个是默认的。然后将结果通过softmax处理。softmax标准化(softmax normalizes)可以将值映射(压缩、标准化)到0-1之间。
  4. 将每个 value 向量乘以 softmax处理后的得分(为相加做准备)。这一步的目的是保留重要的词(想要关注的词)的完整性,去除不相关的词。(如给不相干的词乘以一个极小的数0.001)
  5. 将权重值向量相加。结果就是自注意力层(self-attention layer)在这个位置的输出
    在这里插入图片描述

3.1.3 Self-Attention-MultiHead

上文我们说过了,最终的计算形式是句子中的单词矩阵,但是给出的图例中出现了两个Z,这是因为在论文中提出了“多头注意力”(multi-headed attention)的机制进一步提高了自注意力层的性能。多头注意力机制从两个方面提高注意力层的性能:

  1. 它提高了模型关注不同位置的能力。的确,上面的例子中,Z1包含了一些其他位置的编码信息,但是实际上它却是由实际的词本身决定。我们翻译一个句子像 “The animal didn’t cross the street because it was too tired”,我们想知道“it”指代的是什么词时,多头注意力很有用。

  2. 它给予注意力层多个“表示子空间”。正如我们接下来看到的那样,多头注意力可以让我们拥有多组Q/K/V矩阵(Transformer使用8个注意力头,所以最终我们的编码/解码器有8组)。每组都是随机初始化的。经过训练后,每组将嵌入层的输出(或者底层编码器/解码器的输出)投射到不同的表示子空间中。

因为我们有多组Query,Key,和Value矩阵。将每组的计算结果进行拼接然后乘上训练出来的矩阵WO得到最终的输出Z
在这里插入图片描述

这就是Self-Attention的形象化表示,论文中的表述形式如下
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

3.1.4 Add&Norm

每个编码模块的子层(自注意力层和前馈网络层)中间都有残差(residual)连接,然后紧跟着一个层标准化步骤。
在这里插入图片描述
在论文中
Encoder有N=6层,每层包括两个sub-layers:

  1. Sub-L1是multi-head self-attention mechanism,用来计算输入的self-attention
  2. Sub-L2是简单的全连接网络。

在每个sub-layer我们都模拟了残差网络,每个sub-layer的输出都是
在这里插入图片描述
其中Sublayer(x) 表示Sub-layer对输入 x x x 做的映射,为了确保连接,所有的sub-layers和embedding layer输出的维数都相同,维度为512.

3.2 Decoder

  • 输入:假设已经翻译出k个词,向量维度还是d
  • 同样使用N=6个重复的层,依然使用残余连接和LN
  • 3个子层,比encoder多一个attention层,是Decoder端去attend encoder端的信息的层:
  • Sub-L1:self-attention,同encoder,但要Mask掉未来的信息,得到k*d的矩阵
  • Sub-L2:和encoder做attention的层,输出k*d的矩阵
  • Sub-L3:全连接层,输出k*d的矩阵,用第k行去预测输出y

在这里插入图片描述

Decoder也是N=6层,每层包括3个sub-layers:

1.Sub-L1是Masked multi-head self-attention,也是计算输入的self-attention,但是因为是生成过程,因此在时刻 t t t 的时候,大于 t t t 的时刻都没有结果,只有小于 t t t 的时刻有结果,因此需要做Mask
2. Sub-L2是全连接网络,与Encoder相同
3. Sub-L3是对encoder的输入进行attention计算。

同时Decoder中的self-attention层需要进行修改,因为只能获取到当前时刻之前的输入,因此只对时刻 t t t 之前的时刻输入进行attention计算,这也称为Mask操作。

3.2.1 process

编码模块从处理输入的句子开始,由最顶端的编码器输出是由注意力向量K和V组成的集合。解码器会在每个“注意力编-解码层(encoder-decoder attention)”使用,它们能让解码器关注输入句子中恰当的地方。
在这里插入图片描述
编码阶段结束后就是解码阶段。解码阶段每一步会输出目标句子的一个元素。(比如翻译出来的句子中的一个词)

下面这些步骤会循环,直到解码器收到一个特殊的标记(EOS),这就代表解码结束。
解码模块每一步输出都会作为下一步的输入,解码模块内部动作和编码器一致,而且也会将每个词的位置嵌入并添加到输入向量中。
在这里插入图片描述
解码模块的自注意力层和编码模块的有点不一样。

在解码模块中(有些人将解码模块翻译为解码端),自注意力层只关注输入句子中之前位置的单词。这个操作是在自注意力计算中的softmax层之前,通过屏蔽未来的位置(将它们设置成 -inf)来实现的。

“编码-解码注意力”层的计算方式和多头自注意力是一样的。只不过它是从它下面的层创建Q矩阵,同时从编码模块的输出中获得K和V。

3.3 Norm-Layer normalization(LN)

batch normalization是对一个每一个节点,针对一个batch,做一次normalization,即纵向的normalization:
在这里插入图片描述
layer normalization(LN),是对一个样本,同一个层网络的所有神经元做normalization,不涉及到batch的概念,即横向normalization:
在这里插入图片描述
BN适用于不同mini batch数据分布差异不大的情况,而且BN需要开辟变量存每个节点的均值和方差,空间消耗略大;而且 BN适用于有mini_batch的场景。

LN只需要一个样本就可以做normalization,可以避免 BN 中受 mini-batch 数据分布影响的问题,也不需要开辟空间存每个节点的均值和方差。

但是,BN 的转换是针对单个神经元可训练的——不同神经元的输入经过再平移和再缩放后分布在不同的区间,而 LN 对于一整层的神经元训练得到同一个转换——所有的输入都在同一个区间范围内。如果不同输入特征不属于相似的类别(比如颜色和大小,scale不一样),那么 LN 的处理可能会降低模型的表达能力。

3.4 Linear and Softmax

解码模块输出的是一个向量或者小数,我们怎么把他们变成一个词呢?这就是最后的线性层和softmax层要做的工作。

线性层是个简单的全连接神经网络,它将解码模块输出的一堆向量投影成为一个炒鸡炒鸡大的向量,这个向量成为logits vector(对数向量)。

假设我们模型从训练集中获得10,000个不同的英文单词(就是字典中的单词),线性层会生成10,000个单元宽度的logits向量,每个单元代表一个词的分数。这就是为什么模型后面紧跟着线性层的原因。

softmax层会将这些分数转为概率(全为正,加起来和是1)。最高概率的单元会被选出来,这个单元代表的词就是这一步的输出。

在这里插入图片描述

3.5 Train

现在我们已经了解了Transformer的整个前向训练过程。

训练期间,未经训练的模型会进行相同的前向训练,但是因为我们是有监督学习,所以是有标签可以对比训练输出的结果来判断正确与否。

为了方便理解,我们假设输出词典只有六个词(“a”, “am”, “i”, “thanks”, “student”, and “” ( ‘end of sentence’缩写)).

在这里插入图片描述
一旦我们定义了单词表(字典),我们就可以用同样宽度的向量表示每个词,这也叫作one-hot编码。比如,我们可以用下面的向量表示“am”
在这里插入图片描述

3.5.1 Loss Function

假设我们在训练模型,而且是我们训练阶段的第一波。我们用个简单的例子训练-----把“merci”翻译成“thanks”。

什么意思呢?就是我们想让模型输出是“thanks”的概率分布,但是模型还没有开始训练,所以它还没能力输出。

在这里插入图片描述
模型的初始化参数(权重)是随机的,也就是会在每个单元(词)上产生一个随机的概率值。我们把初始的概率值和真正的概率比较,通过反向传播调整初始值,努力让模型输出的概率分布和真正的概率分布相同。

但是注意,上面的只是个超级简化的例子。更实际点,我们用一个句子代替一个词来举例。比如,输入“je suis étudiant”,然后期望输出“i am a student”。也就是说,我们想要我们的模型成功的输出如下要求的概率分布:

  • 每个概率分布都是单词表长度大小的向量(我们的例子中是6个,但是实际上单词表3000-10,000,向量也这么大)
  • 第一个概率分布中概率最高的单元应该是“i”的单元
  • 第二个概率分布的重概率最高的单元是“am”的单元
  • 以此类推,直到第五个输出了“‘”符号,这个符号也是单词表(词典)里的。

在这里插入图片描述

这是我们样本概率分布(目标的概率分布)。

在足够大的数据集上训练模型足够次数后,我们希望模型会生成如下概率分布。

在这里插入图片描述
这只是我们训练后期望的输出,每个单元都有一个很小的概率,即使它完全不可能是这个时间步的输出—这就是softmax的效果,这种方式有助于模型训练.
现在,因为模型一次产生一个输出,我们可以假设模型选择了概率分布中概率最高的词并且把其他词和概率丢弃了。这种选择方式称为贪婪解码(greedy decoding)。另一种选择方式是保留,比如,保留概率最高的两个单词(假设是“i”和“me”),然后下一步,模型运行两次:一次是假设第一个位置输出的是单词“i”,第二次假设第一个位置输出的单词是“me”,然后模型考虑位置#1和#2,保留错误更少的作为第一个位置(#1)的输出。重复这个步骤在位置#2和#3…等等。(束搜索和贪婪搜索)。这种方式称为“束搜索”(beam search),我们的例子中,束搜索的大小(beam_size)是2(因为我们只比较了#1和#2两个位置),束搜索的数量(top_beams)也是2(“i”和"me",我们只比较了两个词)。这两个参数都是可以随你需要而设置的。

4.不足

  1. 无法对位置信息进行很好地建模,这是硬伤。尽管可以引入 Position Embedding,但我认为这只是一个缓解方案,并没有根本解决问题。举个例子,用这种纯 Attention 机制训练一个文本分类模型或者是机器翻译模型,效果应该都还不错,但是用来训练一个序列标注模型(分词、实体识别等),效果就不怎么好了。
    那为什么在机器翻译任务上好?我觉得原因是机器翻译这个任务并不特别强调语序,因此 Position Embedding 所带来的位置信息已经足够了,此外翻译任务的评测指标 BLEU 也并不特别强调语序。
  2. 并非所有问题都需要长程的、全局的依赖的,也有很多问题只依赖于局部结构,这时候用纯 Attention 也不大好。事实上,Google 似乎也意识到了这个问题,因此论文中也提到了一个 restricted 版的 Self-Attention(不过论文正文应该没有用到它)。它假设当前词只与前后 r 个词发生联系,因此注意力也只发生在这 2r+1 个词之间,这样计算量就是 𝒪(nr),这样也能捕捉到序列的局部结构了。但是很明显,这就是卷积核中的卷积窗口的概念。 通过以上讨论,我们可以体会到,把 Attention 作为一个单独的层来看,跟 CNN、RNN 等结构混合使用,应该能更充分融合它们各自的优势
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值