《Transformer-XL模型浅析》


结合视频比较容易快速看懂:https://www.bilibili.com/video/av62047788/

Transformer-XL模型浅析

通过对Transformer的学习我们知道,这是一种非常强大的结合上下文的特征提取器,不过对于Transformer来说,还是存在着一些问题的,比如说它在语言建模中会受到固定长度上下文的限制,虽然理论上来说,Transformer可以将依赖关系的长度设置得很大,但是这样庞大的计算量是不切合实际的,因此原始的Transformer建模的依赖关系无法超过固定长度的片段,而且也没有考虑到句子的边界问题,这样的问题被叫做“context fragmentation”,而在实际中很多单词的确切含义需要在比较远的上下文中才能来体现出来,因此如果能够改进模型来学习超过固定长度的依赖关系,就能更好地发挥出Transformer的效果,这就是Transformer-XL的目的。

总的来说,Transformer-XL在两个地方做了改进:片段级递归机制(segment-level recurrence mechanism)和相对位置编码方案(relative positional encoding scheme)。

1.片段级递归机制

我们先来看最初的Transformer(Vanilla Transformer)在语言模型中是如何应用的,它需要即将一个长的文本序列截断为固定长度的字符片段,然后分别处理每个片段,如下图所示,该语言模型训练过程如下,为了预测某一位置的单词,将Transformer作用于该单词前面的segment,通过这个segment来预测该单词,预测完成后再将让Transformer向右移动一个位置,继续预测下一个单词。需要注意的是,每向右移动一步,都需要重新计算Transformer的隐层,所以这个训练过程可以并行,不过可以看出,Vanilla Transformer中单词的依赖关系只能局限在当前的segment中,没有办法在联系到更远的上文了,这就是context fragmentation问题。

Transformer-XL为了解决这个问题,就要在当前segment的计算中考虑上文信息,也就是说需要复用上一步的隐状态,形成循环递归。这样做既可以解决context fragmentation问题,还能复用上一步隐状态来加快模型速度,如下图所示,Transformer中更深的隐层会连接到由前一个segment得到的较浅的隐层,通过这样的方式来实现复用。

具体的计算过程如下:

其中 [公式] 代表Transformer在第个segment上、第n隐层(self-attention+feed-forward)的输出;SG()代表梯度停止,也就是不做更新; [公式] 代表两个向量的拼接。从公式可以看出,模型在当前segment中加入了前一个segment的浅隐层输出,然后将这些信息加入到Key和Value向量中,这样就在特征提取时就用到了上文的信息,从图中可以看出,最长的依赖长度随着隐层数和 segment长度呈线性增长,也就是O(N×L),这样就大大增加了依赖长度,解决了context fragmentation。

2.相对位置编码

我们知道,Transformer本身的attention机制对位置顺序并不敏感,因此需要在word embedding时额外加入位置编码,这对于Vanilla Transformer是适用的,但是对于Transformer-XL来说,由于它复用了上文的信息,这就导致位置编码出现重叠,因此需要进行改进,不再对单词在segment中的绝对位置进行编码,而是考虑两词之间的相对位置。

具体来说,原先的绝对位置编码如下:

其中代表位置的词向量,表示对于绝对位置的编码向量。

而相对位置编码如下:

可以看出,原公式中的绝对位置编码被替换为位置i和j的相对编码向量以及两个可训练的参数,通过这样来实现相对编码。

最终的计算过程如下:

这就是Transformer-XL的主要内容,在解决了context fragmentation问题以后,模型的性能也得到了很大的提升,Transformer-XL学到的依赖要原始Transformer 长 450%,而且由于它不需要对每一个segment都要重头开始计算隐层,所以速度也要比原始 Transformer 快超过1800倍。

参考资料

[1] Transformer-xl: Attentive language models beyond a fixed-length context

[2] 

发布于 2019-07-25

BERT模型浅析

Transformer模型自提出以来,已经在很多地方都取得了不错的成果,尤其是在nlp领域,它取得的成果是令人瞩目的,相比于之前的模型,transformer模型的attention机制更够更好地学习到句子当中单词与单词之间的联系,从而能够结合上下文语境来提高准确度。

更重要的是,transformer的这种attention机制是一种底层的方法,我们基于它可以构造出一些新的模型,而这些模型在nlp中往往有着出色的表现,下面介绍的BERT模型就是其中之一。

如果对Transformer模型还不熟悉的话,可以看我之前的文章:Transformer模型浅析

一、预训练过程

BERT是一种预训练语言模型,因此在介绍它之前,先简单说一下什么是nlp中的预训练过程。顾名思义,预训练就是在拿样本数据对模型进行训练之前做的训练,那么为什么要有预训练呢?其实在nlp的下游任务(比如机器翻译、阅读理解等)中,可以使用的样本数据是比较少的,因为这些任务需要的都是经过专门标注的数据,这样就使得拿这些样本数据直接训练出来的模型效果比较一般,因此就需要对模型进行预训练了。

预训练的目的就是,提前训练好这些下游任务中底层的、共性的部分模型,然后再用下游任务各自的样本数据来训练各自的模型,这样就可以极大地加快收敛速度。在这里举一个cv的例子,对于人脸识别和数字识别两种任务,显然它们有很多的区别,然而在用CNN进行训练的时候,其实比较浅的几层网络学习到的特征是很相似的,都是类似于边角线的这类基础特征(下图红色框部分),因此我们可以认为这几层网络可以共用,如果我们能够预先训练好这部分,那么模型的训练收敛速度将会大大加快。

用于人脸识别的神经网络

对于nlp的下游任务,尽管它们的最终目标各不相同,但是它们也有着共同的、也是必须首先要做的东西,那就是要让模型理解文档中的单词和句子,具体说就是将文本中的无法直接计算的单词转变为可以计算的向量或者矩阵等形式,并且这些数字化的向量要能够比较好地反映出对应单词在句子中的含义,这就nlp中预训练的目的。在nlp中采用的是语言模型来做预训练,从最初的word embedding到ELMO、再到GPT,以及现在的BERT,其实做的都是上面说的这件事,只不过效果变得越来越好。

二、语言模型和word embedding

我们知道,一句话不是由一些单词简单地组合就能得到的,它必须要符合人类的语言习惯,而语言模型就是用来判断一句话是不是符合人类的习惯,具体地说就是给定句子中在某一单词前面的单词(也可以是上下文),然后预测该单词出现的概率。如下图所示,对于一句话来说,如果把每一个位置的条件概率都相乘,那么概率越大越能说明它更像一句人说的话。如果我们想训练一个语言模型,只需要选择一个单词并通过它的上下文来预测该单词的概率,然后最大化这个概率就行了。

而语言模型除了能看句子是不是更符合人类习惯之外,是否还有别的作用呢?当然有,那就上面提到的word embedding,这其实是一个副产品,在模型的训练过程中,首先需要将句子中的前面的单词都转化为对应的向量,然后再通过隐层和softmax来预测后面接的单词概率,如果模型能够预测得很好的话,那么就可以认为第一步得到的向量能够很好地反应出对应单词的含义。这样就可以认为得到了一个单词表,每一个单词都可以在表中找到对应的向量,而这个过程就是word embedding,在这之后就可以让word embedding接到具体的下游任务中,这样通过语言模型就实现了对于nlp任务的预训练。

语言模型

可以看出,语言模型的训练不需要预先对数据进行标注,因此可以使用的语料库是相当庞大的,而这也是采用语言模型来做nlp预训练的原因,大量的训练数据使得word embedding的效果更佳,从而使得预训练后的下游任务模型更优。

然而在实际实验中,采用word embedding的预训练技术加到下游任务后,却并没有取得很大的提升,其主要原因是:对于word embedding,一个单词唯一对应一个向量,但是有些单词在不同的句子中往往对应着不同的含义,对于这类多义词,是无法只用一个向量来进行表示的,一个向量只能做到“四不像”——平均该单词的每一种含义,这样显然会对之后的模型学习造成影响,因此需要对预训练的语言模型进行改进,而BERT就是较好地解决了这个问题

三、从word embedding到BRET

BERT的全称是Bidirectional Encoder Representations from Transformer,也就是基于Transformer的双向编码器表征。顾名思义,BERT采用的是Transformer,并且在处理一个单词的时候,还能够考虑到该词前面的和后面的单词,得到它在上下文中的含义。我们知道,Transformer的attention机制在对语境中的单词进行特征抽取时有着很好的效果,而且从直觉上看,考虑上下文的双向编码要比只考虑上文(或下文)的单向效果更好,下面介绍BERT模型的具体内容。

1.输入表示

如下图所示,BERT的输入是token、segmentation和position embeddings的叠加,也就说每个单词的embedding是三个embedding的叠加。对于token embeddings使用WordPiece嵌入,也就是通过上面单词表来实现的;而positional embeddings用来表示句子中单词的位置信息;segement embeddings则是对于句子整体的,不同的句子有不同的项,它用来实现句子级别的任务。

2.遮蔽语言模型

前面提到了BERT是一种双向语言模型,它需要同时考虑单词的上文和下文,为了实现这一目的,模型采用了一种简单的方法:随机屏蔽(masking)部分输入token,然后训练模型去正确预测那些被屏蔽的token,用到的模型结构和Transformer基本一样。

具体来说,模型会随机选择语料中15%的单词,然后其中的80%会用[Mask]掩码代替原始单词,其中的10%会被随机换为另一个单词,剩下10%保持原单词不变,然后要求模型去正确预测被选中的单词。做这些处理的原因是,[Mask]只在训练中才会出现,如果选中单词都变为了[Mask],这会导致训练出来的模型可能会针对于[Mask],这与实际不符,所以才会有一些概率将[Mask]随机替换,而这个概率大小为10%×15%=1.5%,基本不会损害模型的语言理解能力,而模型为了预测正确,就需要保持每个输入token的分布式上下文表示,从而实现了对上下文进行特征提取的目的。

3.下一句预测

前面的遮蔽语言模型是针对于单词量级的训练,而nlp中有许多任务是在句子量级上的,这就需要语言模型还要理解句子之间的关系,这有助于下游句子关系判断任务,而BERT中的下一句预测(Next Sentence Prediction)就是为了这一目的。它的原理也很简单,就是预训练一个二值化的下一句预测任务,具体来说,选择句子A和B作为训练样本:A的下一句有50%的可能是B,另外50%的可能B是随机取自语料库的,模型需要正确预测B是不是A的下一句。

4.微调

通过上面的方法,我们就实现了BERT模型的预训练,对于特定下游任务的模型,可以通将BERT与一个额外的输出层结合而形成,这样需要重新学习的参数量就会比较小,如下图所示,只需要按BERT模型要求的格式输入训练数据,就可以完成不同的下游任务。

总结

从整体上看,BERT模型采用了目前流行的特征提取器Transformer,同时还实现了双向语言模型,采众家之长使得它具有更好的性能——在11项NLP任务中都刷新了性能表现记录。不过BERT也还有可以改进的地方,比如说BERT采用的是原始的Transformer,Transformer固然强大,不过现在已经也有了一些更强的改进版;另外就是BERT的Mask机制,虽然通过它可以实现双向语言模型的训练,但是这样会导致预训练和在下游任务上进行微调时的不一致(后者语料中没有[Mask]掩码),而如果能够在这些方面做出改进,那么相信模型的性能会有进一步的提高。

参考资料

[1] BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding

[2] 从Word Embedding到Bert模型—自然语言处理中的预训练技术发展史

发布于 2019-07-23

Transformer模型浅析

自Transformer模型提出之后,它以及它的众多衍生模型已经在nlp领域中占据了重要的位置,因此Transformer模型是非常值得我们去深入学习研究的,本文将会介绍transformer模型的架构以及它的主要步骤。

为了理解该模型的原理,先来看一个机器翻译的例子,对于句子 “The animal didn't cross the street because it was too tired.”,其中的it应该怎样翻译?显然如果只让机器关注单词本身而忽略上下文的话,翻译就不会准确。为了解决这个问题,transformer模型中采用了attention机制,其作用就是让机器在翻译时不止考虑原单词,还要考虑与其关联的其它单词,如下图所示。这样就相当于在对单词进行翻译时有了更多的特征信息,使得最后的结果更加准确。

在transformer模型出来之前,用于机器翻译的方法大多依赖于循环神经网络RNN,与RNN相比,transformer模型不需要通过循环来得到句子中词和词之间的关系,因此该方法可以并行地处理对句子每个单词,实验表明transformer在训练速度和翻译结果上都更优。

一、模型架构

Transformer模型架构

如上图所示,transformer模型在本质上还是由一个编码组件(左半部分)和一个解码组件(右半部分)构成的,如下图所示,输入源语言句子,经过编码和解码之后,可以得到目标语言的句子,完成机器翻译。而对于编码或解码组件,都是由Nx个编码或解码器堆叠而成的。

解码器与编码器

对于编码器,它由两个子层组成,分别是self-attention层和前馈神经网络;对于解码器,在self-attention层和前馈神经网络之间还多了另一个attention层,如下图所示。在transformer模型中,最重要的无疑是attention机制了,关于它的详细介绍将在后面给出。

编码器构成

二、流程介绍

1.词向量化

首先需要利用词嵌入技术(embedding algrithm),将句子中的每一个词映射为一个向量,两者之间一一对应,比如可以是一个长度为512的行向量,那么对于一个含有n个单词的句子,就是一个由n个行向量组成的列表(也可以认为是一个n×512的矩阵),为了后续的计算方便,将n设为训练集中最长句子的长度。

可是仅仅这样还不够,因为一个单词除了它本身的含义外,它在句子中的位置也同样会对翻译造成影响,而attention机制却没有办法直接考虑到单词之间的顺序(这也是attention可以并行的原因),如果对于一句话,改变其单词顺序对Transformer来说不会产生什么影响的话,这样显然是不行的,因此需要在词向量中加入位置信息,在这里采用的是位置编码的方法,计算方式如下,其中pos代表单词在句子中的位置,i代表该词向量的第i个维度,dmodel为512,与词向量的维度相同。

这一样就得到了关于位置信息的矩阵PE,可以看出来,该矩阵与具体的单词无关,对于相同长度的句子,其PE矩阵一样。将PE矩阵加到上述的矩阵中,就可以得到包含位置信息的句子矩阵。

2.编码流程

得到句子矩阵之后,就可以将其送入编码器进行编码,在这当中用到了attention机制,而也是transformer模型中的重点部分。编码器整体流程如下:

编码器中的第一个子层就是self-attention,下面介绍该子层的计算过程。

该层的输入是上文得到的n×512的矩阵X,矩阵的每一行代表一个单词,为了简单起见,先看n=2的情况,这时句子只由两个单词构成,词向量分别是x1和x2,self-attention子层首先令词向量乘以三个训练得到的、维度相同的矩阵 [公式] 分别得到Query、Key和Value三个向量,如下图所示。

这三个向量就是用来计算attention的,首先对于单词x1,需要计算x1与句子中单词的相关性,在这里可采用Query与Key向量点乘的方式来计算,则x1和自己的相关性为q1点乘k1,x1和x2之间的相关性为q1点乘k2。

然后将结果除以 [公式] (这样做的目的是得到更稳定的梯度),然后再做softmax处理,则得到的结果位于0-1之间,显然相关性越高的单词越接近1,将它看作权重,令它乘以相对应的Value向量后再求和,最后得到attention的输出结果,这样就可以减弱相关性低的单词影响,保留下来重要的部分,例如对于x1和x2,如果x1和x2相关性很低,那么v2对于输出结果的影响就很小。

其实上面的过程也可以这样理解,将Query看作查询向量,Key和Value看作键值对,对于一个单词x1,想找到句子和它相关性比较大的单词,通过查询向量q1计算数据库中与其相关性较大的键值k,再以相关性大小作为权重对相应的Value求和,最后得到的结果就应该是对于x1而言影响最大的,这样做就使得原向量不再只包含单词本身,还包含了该单词与上下文之间的关系,这样通过多层编码就可以提取出该单词包含上下文后的特征。

上述的过程可以通过矩阵计算简化,令X为最初的输入矩阵,Q、K和V分别为计算得到相应矩阵,则attention计算可以变为:

[公式]

这样就通过attention完成了翻译模型对于不同位置单词的关注,不过我们还可以通过Multi-Head Attention来进一步提高这种性能:把上述的计算过程是看作一个head,那么可以同时构造出多个head,它们之间相互独立(Q、K和V均不同),最后输出的结果也是独立的,如下图所示。这样做的好处就是能够得到词与词之间的更多的关系,从而令翻译更加准确。

每一个head都输出一个结果矩阵,将这些矩阵拼接成一个矩阵,再乘以一个权重矩阵WO,使得最终的矩阵大小与一个head的结果矩阵大小一致。

Multi-Head Attention

最后,将attention的结果经过正则化,输入到第二个子层——前馈神经网络,它是个全连接层,网络输入的是句子某一个位置上的单词的attention输出,对于不同的位置,网络参数是不变的,公式如下所示:

3.解码流程

首先明确一下解码组件的输入,解码层的输入来自于两处,如下图所示,一部分输入来自于编码组件的输出,需要注意的是这部分是直接输入到解码器中的编码-解码attention子层中的,编码-解码attention中的Key和Value就来自于这部分输入;另一部分则是将模型已经翻译出来的部分句子重新输入到解码层的self-attention中,这被叫做auto-regressive性质,也因为这样的性质,解码器一次只能生成一个位置的输出。

解码组件中一个解码器的流程如下,可以看出,相比于编码器,解码器主要是在self-attention和前馈神经网络子层之间多了一个编码-解码attention子层。

对于self-attention子层,它与编码器中的self-attention基本一致,唯一不同的是解码器中的是Masked Multi-Head attention,这是为了防止在翻译中出现未来信息。在训练过程中,解码组件输入的是已知的输出(目标语言句子),为了防止未来的翻译结果对当前翻译的影响,模型在attention中使用了masking,将后面位置的单词与当前位置单词的相关性变为0,从而避免了未来翻译对当前单词的影响。这样当前翻译就只能够attention到已翻译的前文。

对于编码-解码attention子层,它和self-attention有所不同,它的Query来自于之前的解码层,而Key和Value则来自于编码层的输出,这样就可以使得解码器中的每一个位置都能attention到初始句子中所有位置的单词,使得模型能够得到当前翻译与编码器提取出的特征向量之间的关系。

4. 输出预测值

最后就是输出翻译结果了,通过解码组件可以得到一个向量,经过线性层将它投影成为一个长度很大的向量,这个向量的长度就是词典的大小,向量的每一个维度都代表目标语言单词库中的一个单词,对向量做softmax后,向量中该维度的值越大,代表对应单词的概率越大,输出概率最大的单词作为翻译结果。

参考资料

[1] 论文:Attention is all you need

[2] 博客:The Illustrated Transformer

发布于 2019-07-22
原文地址:https://www.cnblogs.com/cx2016/p/12931933.html