作者 | 周俊贤 整理 | NewBeeNLP
最近又重新读了Transformer-XL和XLNet的论文和代码,又有很多新的感悟。其中,要想搞懂XLNet的同学一定要首先明白Transofrmer-XL,因为XLNet是基于Transformer-XL进行改进的。
- tips:Transformer-XL投稿是被ICLR 2019拒稿的,作者基于Transformer-XL改进提出了XLNet,被NeurIPS 2019接收
Transformer-XL
论文全称及连接:《Transformer-xl: Attentive language models beyond a fixed-length context[1]》
项目地址:https://github.com/kimiyoung/transformer-xl
Vanilla Transformer语言模型
Transformer结构第一次提出来是用于机器翻译任务,提出后自然有个想法:「Transformer结构能不能用于训练语言模型?」 答案是肯定的。RNN、LSTM等网络结构训练语言模型的最大缺点在于容易梯度消失,难以学习长期依赖,但是Transformer没有合格缺点,因为它没有沿时间线进行横向传播的过程,这样来说,Transformer很适合训练语言模型。
怎么训练?很简单,举个例子,假如现在我们有一句话包含100个token,在预测第一个token的时候,我们把其它的99个token进行attention mask掉,只输入一个开始标记,让模型预测第一个token;在预测第二个token的时候,把剩下的98个token attention mask掉,只让模型看见开始标记和第一个token;在预测第三个token的时候,把剩下的97个token attention mask掉,只让模型看见开始标记和前两个token。。。损失函数为真实token和预测token的交叉熵,这样模型不断学习,不断更新参数,就训练得到一个语言模型了。
想想这样有个什么问题?问题就是真实的文本可没有只包含100个token这么少。举个例子,假设我现在手头有个文章是包含4000个token的,那我就要把这4000个token都作为输入,原理上可以这么做,但实际上,输入的token很多的情况下,中间层attention的计算资源会爆炸。
Vanilla Transformer的做法是「切片」,如把4000个token切成8段,每段包含500个token,再对每段进行独立的训练,也即在第二段训练的时候,是看不到第一段的信息的,这样导致的问题就是「上下文碎片问题(context fragmentation problem)」,由于切片长度限制,模型并不能学习到足够常的依赖信息,这样会大大损害学习出来的语言模型的性能。
在评估时,为了能利用训练时的最长上下文信息,是每个时间步向右移动一个token,导致的结构是评估耗费的计算资源很大。
改进点
「改进点一:循环机制」
在计算每个segment的时候,通过缓存上一个segment的信息,把前面segment的信息也考虑进正向传播过程中(上一个segment的信息虽然参与正向传播,但上一个segment是不会参与反向传播的)。具体的看,下面的式子,
这里的
指的是Transformer第n-1层的第
个segmetn的Encoder的输出,把它与Transformer第n层的第
的encoder的输出沿着时间轴拼接在一起,SG表示stop-gradient,即前一个segmetn的信息只参与正向传播,并不会用到反向传。另外,在计算查询矩阵q时,只会用当前segmetn来计算得到;只有在计算键值向量时,才会用到拼接的segmetn向量。
这样的做法,能大大缓解上下文碎片的问题,举个例子,假设Transformer的encoder一共有4层,每个segment为500个token。根据循环机制的原理,第4层的第
个segmetn的输出不仅考虑了第三层encoder的第
个encoder的输出,也考虑了第
个encdoer的输出,而第
个encdoer的输出,不仅考虑了第二层encoder的第
个encdoer的输出,也考虑了第
个encdoer的输出,也即,上下文的最大依赖长度是线性增加的,如O(N*L),如这里所说的例子,虽然,一个segmetn是500个token,但其实在最后输出时,足足考虑了4 * 500 = 2000个token这么多!上下文碎片的问题也就自然得到了大大的缓解了。
另外,在评估时,由于采用了循环机制,我们就不必每次只向右移动一步了,而且可以采用同训练时候差不多的片段机制,从而大大提高了评估效率。
「改进点二:相对位置编码」
上面所说的循环机制还有个问题待解决,就是位置编码,我们知道,原生的Transformer使用的是绝对位置编码,但绝对位置编码跟循环机制结合会导致一个问题,举个例子,我们在计算第
的输出时,不仅考虑了第
个片段的输入,还考虑了第
个片段的输入,假设我们采用绝对位置编码,那第
个片段和
个片段的第1个token的位置编码是一样的,但这是明显的不合理的。因此,作者提出了一种相对位置编码的思想。
具体的,原生的绝对位置编码在计算attention时,如下式所示,
采用相对位置编码,
- 将绝对位置embedding
用相对位置
代替,注意,这里的
是用原生Transformer的三角函数编码模式,是不需要学习的;
- 用
去代替
,理由是由于相对编码的思想,无论query在哪个位置,他们对于自身的位置编码信息应该都是一样的,同理,用
去代替
;
分解成
和
,分别表示基于内容的健向量和基于位置的健向量。
通常这样的变换,上面的每一项都有其相应的意义
- (a)项为基于内容的寻址;
- (b)项为基于内容的位置偏置;
- (c)项为全局的内容偏置;
- (d)为全局的位置偏置。
把循环机制和相对位置编码的信息合并后,Transformer-XL的最终形态
Transformer-XL不足及与BERT的对比
Transformer-XL这篇论文为什么没有被ICLR接受,我认为主要是因为并没有与当前一些基于Transformer的模型,如BERT等对比,并没有在具体的NLP任务如分类、QA等应用进行实践。论文里只是简单提了Transformer-XL在文本生成(由于Transformer-XL是语言模型,所以用于文本生成很自然)、无监督特征学习等都有前景,并没有给出在某些具体的GLUE等的表现,所以略显单薄。
不少人都说Transformer-XL能有效解决BERT的长度限制问题,确实,Transformer-XL是不限制文本长度的,它的预训练模式是语言模型的训练目标,通过循环机制和相对编码,可以接受无限长的输入文本。应用到下游任务也很简单,如文本分类可以用最后一个token的输出再接一些前连接层来做分类,序列标注任务也可以用每个token的输出再接一些网络。
为什么BERT是有长度限制?因为在预训练的时候,就把输入长度限制在512,BERT会把1~512位置映射到一个position embedding,没有512以上的position embedding。我们当然也可以重头预训练一个最大长度为1000的BERT,但会很耗资源。
Transformer-XL输入是没有position embedding的,相对位置信息是加在每个层encoder的attention计算中。除此之外,Transformer-XL预训练是只利用了单项信息,BERT是利用了双向的上下文编码,所以可以期待对于短文本,Transformer-XL是打不过BERT的,长文本的话还有一点可能。
XLNet
论文全称及连接:《XLNet: Generalized Autoregressive Pretraining for Language Understanding[2]》
项目地址:https://github.com/huggingface/transformers/blob/master/src/transformers/models/xlnet/modeling_xlnet.py
XLNet的源码我没有看作者发的tensorflow版本,而是看的huggingface的pytorch版本。
AR & AE
「自回归语言模型(Autoregressive LM)」
- 如Transformer-XL,GPT等等,任务为根据上文内容预测下一个单词,预训练任务为单项的语言模型任务
- 优点:可以完成生成类的NLP任务
- 缺点:只能利用单向信息
「自编码语言模型(Autoencoder LM)」
- 如BERT,根据上下文单词来预测被【MASK】掉的单词(噪声);
- 优点:能利用到双向信息;
- 缺点:预训练阶段和Fine-tuning阶段不一样,预训练时输入有【MASK】,但在应用到具体下游任务时,输入是没有【MASK】的。
Permutation Language Modeling
XLNET的终极目的就是结合自回归语言模型和自编码语言模型的优点,提出了排列语言模型。具体的,看下图
假设我们现在要预测第3个token。正常的语言模型的序列是1 -> 2 -> 3 -> 4,我们对其做「排列组合」,排列方式应该有4!=24种,上图举例了其中的4种,如第一种3 -> 2 -> 4 -> 1,这样排列的话,3是看不到2和4和1的,只能看到上一个segment的输入(这里可以先不不用理,下面会说,XLNet利用了Transformer-XL的思想,所以会有上一个segment作为mem的概念),通过排列,在预测token3的时候,总会有一些排列能考虑到1、2、4,穷举排列,一起训练,这样的话,模型能将3上下文的信息都能学到。
注意的是,实际在预训练时,并非真的排列,如2 -> 4 -> 3 -> 1,并不是排成这个序列,而是利用attention mask的思想来近似作用这种排列,就是把token1 mask掉,因为按照这个排列,token3应该是看不到token1的,利用这种attention mask的思想来近似实现排列。
双流自注意力
根据上面的思想,又引出了一个问题,举个例子I love New York这四个token,现在有两个序列1-> 2 -> 3 ->4和1-> 2 -> 4 ->3,现在假设已经知道前面两个token是I love,要预测下一个token,很明显,在两个序列中,下一个token为New的概率都是一样的,这是非常不合理的,因为下一个token的位置是不一样的,并没有把位置信息考虑进去。因此,作者提出了一种双流自注意力。
- 当token用于预测后面的字符,这时候,它应该能看到自己的内容信息和位置信息;
- 当token用于预测自己到底是哪个字符,这时候,它应该只能看到自己的位置信息。
借此,提出两种自注意流
- The content representaion
;
- The query representaion
。
加入位置作为预测信息后,预测下一个token的概率变成
初始化第一层
,其中
为一个可训练的向量,
为相应的词嵌入。两种Attention在接下来每层的构造
在fine tuning应用于下游任务时,把
挪走,只保留
。
与Transformer-XL的结合
这里就是把Transformer-XL的循环机制和相对位置编码引进XLNet中,值得注意的是,Permutation只争对当前的segment,上一个segment的排序是原文本的顺序排列。
多个Segments的输入
像BERT之类的模型,输入是可以是一对文本,这样的预训练方式可以适应输入为不止一个句子的下游任务。XLNet采用了一个相对Segment编码的方式来解决输入多个句子的问题。分别用两个segment编码
和
,其中
和
是每层endoer可以学习的参数,把他们也加进去attention的计算中,
,这里
为查询向量,
为可学习的编制,当两个token不是来源于同一句时,
=
,当来自同一句时,
。
想想这样做的好处?好处就是XLNet不再限制输入的为一对句子对,而是可以超过两个以上句子作为输入,因为XLNet不像BERT一样,输入时的segment embedding只能为0或1再映射到一个向量加入到input中,XLNet不是在输入层映射到0和1,而是在每一层encoder通过引入segmetn编码,来处理多个句子输入的问题。
实验结果
值得注意的是,XLNet相对于BERT使用了更多的训练预料,由于是晚于BERT出来的模型,自然效果是比BERT要好的,与BERT 的比较
与RoBERTa的比较