本文主要为了做笔记,参考了以下文章(都讲的非常详细):
XLNet:运行机制及和Bert的异同比较
XLNet原理解读
XLNet原文链接
XLNet是一个类似BERT的模型,而不是完全不同的模型,总之,XLNet是一种通用的自回归预训练方法,它是CMU和Google Brain团队在2019年6月份发布的模型,最终,XLNet 在 20 个任务上超过了 BERT 的表现,并在 18 个任务上取得了当前最佳效果(state-of-the-art),包括机器问答、自然语言推断、情感分析和文档排序。
作者表示,BERT 这样基于去噪自编码器的预训练模型可以很好地建模双向语境信息,性能优于基于自回归语言模型的预训练方法。然而,由于需要 mask 一部分输入,BERT 忽略了被 mask 位置之间的依赖关系,因此出现预训练和微调效果的差异(pretrain-finetune discrepancy)。
基于这些优缺点,该研究提出了一种泛化的自回归预训练模型 XLNet。XLNet 可以:
通过最大化所有可能的因式分解顺序的对数似然,学习双向语境信息;
用自回归本身的特点克服 BERT 的缺点;
此外,XLNet 还融合了当前最优自回归模型 Transformer-XL 的思路。
在ELMO/BERT出来之前,大家通常讲的语言模型其实是根据上文内容预测下一个可能跟随的单词,就是常说的自左向右的语言模型任务,或者反过来也行,就是根据下文预测前面的单词,这种类型的LM被称为自回归语言模型。GPT 就是典型的自回归语言模型。ELMO尽管看上去利用了上文,也利用了下文,但是本质上仍然是自回归LM,这个跟模型具体怎么实现有关系。ELMO是做了两个方向(从左到右以及从右到左两个方向的语言模型),但是是分别有两个方向的自回归LM,然后把LSTM的两个方向的隐节点状态拼接到一起,来体现双向语言模型这个事情的。所以其实是两个自回归语言模型的拼接,本质上仍然是自回归语言模型。
自回归语言模型有优点有缺点:
缺点是只能利用上文或者下文的信息,不能同时利用上文和下文的信息,当然,貌似ELMO这种双向都做,然后拼接看上去能够解决这个问题,因为融合模式过于简单,所以效果其实并不是太好。
优点其实跟下游NLP任务有关,比如生成类NLP任务,比如文本摘要,机器翻译等,在实际生成内容的时候,就是从左向右的,自回归语言模型天然匹配这个过程。而Bert这种DAE模式,在生成类NLP任务中,就面临训练过程和应用过程不一致的问题,导致生成类的NLP任务到目前为止都做不太好。
自回归语言模型只能根据上文预测下一个单词,或者反过来,只能根据下文预测前面一个单词。相比而言,Bert通过在输入X中随机Mask掉一部分单词,然后预训练过程的主要任务之一是根据上下文单词来预测这些被Mask掉的单词,如果你对Denoising Autoencoder比较熟悉的话,会看出,这确实是典型的DAE的思路。那些被Mask掉的单词就是在输入侧加入的所谓噪音。类似Bert这种预训练模式,被称为DAE LM。
这种DAE LM的优缺点正好和自回归LM反过来,它能比较自然地融入双向语言模型,同时看到被预测单词的上文和下文,这是好处。缺点是啥呢?主要在输入侧引入[Mask]标记,导致预训练阶段和Fine-tuning阶段不一致的问题,因为Fine-tuning阶段是看不到[Mask]标记的。DAE吗,就要引入噪音,[Mask] 标记就是引入噪音的手段,这个正常。
XLNet的出发点就是:能否融合自回归LM和DAE LM两者的优点。就是说如果站在自回归LM的角度,如何引入和双向语言模型等价的效果;如果站在DAE LM的角度看,它本身是融入双向语言模型的,如何抛掉表面的那个[Mask]标记,让预训练和Fine-tuning保持一致。当然,XLNet还讲到了一个Bert被Mask单词之间相互独立的问题。
Bert的自编码语言模型也有对应的缺点,就是XLNet在文中指出的:
上面两点是XLNet在第一个预训练阶段,相对Bert来说要解决的两个问题。
其实思路也比较简洁,可以这么思考:XLNet仍然遵循两阶段的过程,第一个阶段是语言模型预训练阶段;第二阶段是任务数据Fine-tuning阶段。它主要希望改动第一个阶段,就是说不像Bert那种带Mask符号的Denoising-autoencoder的模式,而是采用自回归LM的模式。就是说,看上去输入句子X仍然是自左向右的输入,看到Ti单词的上文Context_before,来预测Ti这个单词。但是又希望在Context_before里,不仅仅看到上文单词,也能看到Ti单词后面的下文Context_after里的下文单词,这样的话,Bert里面预训练阶段引入的Mask符号就不需要了。
XLNet是这么做的,在预训练阶段,引入Permutation Language Model的训练目标。什么意思呢?就是说,比如包含单词Ti的当前输入的句子X,由顺序的几个单词构成,比如 x 1 x_1 x1, x 2 x_2 x2, x 3 x_3 x3, x 4 x_4 x4四个单词顺序构成。我们假设,其中,要预测的单词Ti是 x 3 x_3 x3,位置在Position 3,要想让它能够在上文Context_before中,也就是Position 1或者Position 2的位置看到Position 4的单词 x 4 x_4 x4。可以这么做:假设我们固定住 x 3 x_3 x3所在位置,就是它仍然在Position 3,之后随机排列组合句子中的4个单词,在随机排列组合后的各种可能里,再选择一部分作为模型预训练的输入X。
首先,需要强调一点,尽管上面讲的是把句子X的单词排列组合后,再随机抽取例子作为输入,但是,实际上你是不能这么做的,因为Fine-tuning阶段你不可能也去排列组合原始输入。所以,就必须让预训练阶段的输入部分,看上去仍然是 x 1 x_1 x1, x 2 x_2 x2, x 3 x_3 x3, x 4 x_4 x4这个输入顺序,但是可以在Transformer部分做些工作,来达成我们希望的目标。
具体而言,XLNet采取了Attention掩码的机制,你可以理解为,当前的输入句子是X,要预测的单词 T i T_i Ti是第 i i i个单词,前面1到 i − 1 i-1 i−1个单词,在输入部分观察,并没发生变化,该是谁还是谁。但是在Transformer内部,通过Attention掩码,从X的输入单词里面,也就是 T i T_i Ti的上文和下文单词中,随机选择 i − 1 i-1 i−1个,放到 T i T_i Ti的上文位置中,把其它单词的输入通过Attention掩码隐藏掉,于是就能够达成我们期望的目标。
为了提供一个完整的概览图,研究者展示了一个在给定相同输入序列 x(但因式分解顺序不同)时预测 token x 3 x_3 x3 的示例,如下图所示:比如图的左上,对应的分解方式是3→2→4→1,因此预测 x 3 x_3 x3是不能attend to任何其它词,只能根据之前的隐状态memmem来预测。而对于左下, x 3 x_3 x3可以attend to其它3个词。
我们先看个例子:
假设整句话为 [“我 1”, “今天 2”, “很 3”,「开心 4」],我们只采样出一个样本 ([“今天 2”, “很 3”, “开心 4”] → “我 1” ),XLNet 的做法和 BERT 有同有异。
和 BERT 一样,XLNet 同样是将目标词 “我 1” 替换成一个特殊字符 “MASK1”。和 BERT 不同,“MASK” 不会纳入表征的地址向量 k 以及内容向量 v 的计算,“MASK” 自始至终只充当了查询向量 q 的角色,因此所有词的表征中都不会拿到 “MASK” 的信息。这也杜绝了 “MASK” 的引入带来的预训练-微调差异 (Pretrain-Finetune Discrepancy) – 这个改动也可以直接应用到 BERT 上面。
在下图中记 “MASK” 对应的词向量为 G,X_2 - X_4为各自的词向量,G1, H1 - H4 为各自的表征。图中省略了位置编码 p。
上面只是讨论最简单的情况 – 即一句话只产生一个样本。但我们还希望保证训练效率 – 我们想和自回归语言模型一样,只进行一次整句的表征计算便可以获得所有样本的语境表征。这时所有词的表征就必须同时计算,此时便有标签泄露带来的矛盾:对于某个需要预测的目标词,我们既需要得到包含它信息以及位置的表征 h h h (用来进一步计算其他词的表征),又需要得到不包含它信息,只包含它位置的表征 g g g (用来做语境的表征)。
一个很自然的想法就是同时计算两套表征,这便是 XLNet 提出的双通道自注意力 (Two Stream Self-Attention),同时计算内容表征通道 (Content Stream) h h h 和语境表征通道 (Query Stream) g g g。
假设我们要计算第 1 个词在第 l 层的语境表征 g 1 l g_{1}^{l} g1l
和内容表征 h 1 l h_{1}^{l} h1l,我们只关注注意力算子查询向量 Q、地址向量 K 以及内容向量 V 的来源:
计算 g 1 l g_{1}^{l} g1l时用到了 h j ! = 1 l − 1 h_{j!=1}^{l-1} hj!=1l−1,表示第 l − 1 l-1 l−1 层除了第 1 个词外所有词的表征,这是为了保证标签不泄露;计算 h 1 l h_{1}^{l} h1l 时用到了 h : l − 1 h_{:}^{l-1} h:l−1,表示第 l − 1 l-1 l−1 层所有词的表征,这和标准的 Transformer 计算表征的过程一致。
但上述做法在堆叠多层自注意算子时仍然会带来标签泄露。
虽然计算 g 1 l g_{1}^{l} g1l时我们已经采取措施防止 h 1 l − 1 h_{1}^{l-1} h1l−1
的信息泄露到 g 1 l g_{1}^{l} g1l 中,但是考虑两层自注意力算子的计算:
我们看到第 l − 2 l-2 l−2 层第 1 个词的表征 h 1 l − 2 h_{1}^{l-2} h1l−2会通过第 l − 1 l-1 l−1 层的所有表征 h j l − 1 h_{j}^{l-1} hjl−1泄露给 g 1 l g_{1}^{l} g1l。和将 Transformer 应用到自回归语言模型的情况类似,我们还需要对每层的注意力使用注意力掩码 (Attention Mask),根据选定的分解排列 z z z,将不合理的注意力权重置零。我们记 z t z_{t} zt 为分解排列中的第 t 个词,那我们在词 z t z_{t} zt 的表征时, g t l g_{t}^{l} gtl 和 h t l h_{t}^{l} htl 分别只能看到排列中前 t − 1 t-1 t−1 个词 z 1 : t − 1 z_{1:t-1} z1:t−1 和前 t 个词 z 1 : t z_{1:t} z1:t,即:
在如此做完注意力掩码后,所有 g z t l g_{z{t}}^l gztl 便可以直接用来预测词 z t z_{t} zt,而不会有标签泄露的问题。
我们来看另一个例子:
假设输入的句子是”I like New York”,并且一种排列为z=[1, 3, 4, 2],假设我们需要预测 z 3 = 4 z_3=4 z3=4,即预测第3个位置的词是“York”,那么根据公式:
p θ ( X 4 = x ) pθ(X_4=x) pθ(X4=x)表示第4个词是York的概率。用自然语言描述:上面的概率是第一个词是I,第3个词是New的条件下第4个词是York的概率。
另外我们再假设一种排列为 z ’ = [ 1 , 3 , 2 , 4 ] z’=[1,3,2,4] z’=[1,3,2,4],我们需要预测 z 3 = 2 z_3=2 z3=2,即预测第3个位置的词是“like”,那么:
这上面是表示是第一个词是I,第3个词是New的条件下第2个词是York的概率。我们仔细对比一下公式会发现这两个概率是相等的。但是根据经验,显然这两个概率是不同的,而且上面的那个概率大一些,因为York跟在New之后是一个城市,而”York New”是什么呢?
上面的问题的关键是模型并不知道要预测的那个词在原始序列中的位置。注意:位置编码是和输入的Embedding加到一起作为输入的,因此 p θ ( X 4 = x ∣ x 1 x 3 ) pθ(X_4=x|x_1x_3) pθ(X4=x∣x1x3)里的x_1和x_3是带了位置信息的,模型(可能)知道(根据输入的向量猜测)I是第一个词,而New是第三个词,但是第四个词的向量显然这个是还不知道(知道了还要就不用预测了),因此就不可能知道它要预测的词到底是哪个位置的词,因此我们必须”显式”的告诉模型我要预测哪个位置的词。
为了后面的描述,我们再把上面的两个公式写出更加一般的形式。给定排列 z z z,我们需要计算 如果我们使用普通的Transformer,那么计算公式为:
根据前面的讨论,我们知道问题的关键是模型并不知道要预测的到底是哪个位置的词,为了解决这个问题,我们把预测的位置 z t z_t zt放到模型里:
上式中 g θ ( X z < t , z t ) g_{\theta}{(X_z
接下来的问题是用什么模型来表示 g θ ( X z < t , z t ) g_{\theta}{(X_z
为了预测 X z t X_{zt} Xzt, g θ ( X z < t , z t ) g_{\theta}{(X_z
为了预测 z t z_t zt之后的词, g θ ( X z < t , z t ) g_{\theta}{(X_z
为了解决这个问题,论文引入了两个Stream,也就是两个隐状态:
下面我们介绍一下计算过程。我们首先把查询隐状态 g i ( 0 ) g_i^{(0)} gi(0)初始化为一个变量w,把内容隐状态 h i ( 0 ) h_i^{(0)} hi(0)初始化为词的Embedding e ( x i ) e(x_i) e(xi) 。这里的上标0表示第0层(不存在的层,用于计算第一层)。因为内容隐状态可以编码当前词,因此初始化为词的Embedding是比较合适的。
接着从m=1一直到第M层,我们逐层计算:
上面两个流分别使用自己的Query向量 g z t g_{zt} gzt和 h z t h_{zt} hzt;但是Key和Value向量都是用的h,因为h是内容。但是注意Query流不能访问 z t z_t zt的内容,因此KV是 h z < t ( m − 1 ) h_{z
上面的梯度更新和标准的self-attention是一样的。在fine-tuning的时候,我们可以丢弃掉Query流而只用Content流。最后在计算公式的时候我们可以用最上面一层的Query向量 g z t M g_{zt}^M gztM。
下面我们通过下图直观的了解计算过程。
图的左上是Content流和Attention流的计算,假设排列为 3 → 2 → 4 → 1 3 \rightarrow2\rightarrow4\rightarrow1 3→2→4→1,并且我们现在预测的第1个位置词的概率。根据排列,我们可以参考所有4个词的信息,因此 K V = [ h 1 ( 0 ) , h 2 ( 0 ) , h 3 ( 0 ) , h 4 ( 0 ) ] KV=[h_1^{(0)},h_2^{(0)},h_3^{(0)},h_4^{(0)}] KV=[h1(0),h2(0),h3(0),h4(0)],而 Q = h 1 ( 0 ) Q=h_1^{(0)} Q=h1(0)。
左下是Query流的计算,因为不能参考自己的内容,因此 K V = [ h 2 ( 0 ) , h 3 ( 0 ) , h 4 ( 0 ) ] KV=[h_2^{(0)},h_3^{(0)},h_4^{(0)}] KV=[h2(0),h3(0),h4(0)],而 Q = g 1 ( 0 ) Q=g_1^{(0)} Q=g1(0)。
上图的右边是完整的计算过程,我们通过下图更清楚的分析,从上往下看,首先 h h h和 g g g分别被初始化为 e ( x i ) e(x_i) e(xi)和 W W W,然后Content Mask和Query Mask计算第一层输出 h ( 1 ) h^{(1)} h(1)和 g ( 1 ) g^{(1)} g(1),然后计算第二层…。最右边的Content Mask和Query Mask,我们先把坐标添上去,这是原始的词序列(我们看到的输入),对于Content Mask,它的第一行全是红点,表示第一个词可以attend to所有的词,第二个词可以attend to它自己和第三个词,…。而Query Mask和Content Mask的区别就是不能attend to自身,因此对角线都是白点。
虽然排列语言模型有很多点,但是它的计算量和大(排列很多),很难优化。因此我们只预测一个句子的一些词,为什么不预测前面的词?因为前面的词的上下文比较少,上下文信息相对较少。比如句子“I like New York”。预测I的时候没有任何上下文,因此可能的选择很多。而到最后一个词York的时候,如果New已经知道了,那么York的概率就非常大了。
因此我们把一个排列 z z z分成两个子序列 z ≤ c z\leq c z≤c和 z > c z>c z>c,分别叫做non-target序列和target序列,其中 c c c是切分点。我们会使用一个超参数K,表示 1 / K 1/K 1/K的token会被预测,因此根据公式:
∣ z ∣ − c ∣ z ∣ = 1 K \frac{|z|-c}{|z|}=\frac{1}{K} ∣z∣∣z∣−c=K1。可以计算出 K ≈ ∣ z ∣ − c ∣ z ∣ K\approx \frac{|z|-c}{|z|} K≈∣z∣∣z∣−c,约等于的原因是因为 K K K是整数。前面 c c c个不用预测的Token,我们不需要计算其Query流,从而节省计算时间。
Al-Rfou等人基于Transformer提出了一种训练语言模型的方法,来根据之前的字符预测片段中的下一个字符。例如,它使用 x 1 , x 2 , . . . , x n − 1 x_1,x_2,...,x_n−1 x1,x2,...,xn−1预测字符 x n x_n xn,而在 xn 之后的序列则被mask掉。论文中使用64层模型,并仅限于处理 512个字符这种相对较短的输入,因此它将输入分成段,并分别从每个段中进行学习,如下图所示。 在测试阶段如需处理较长的输入,该模型会在每一步中将输入向右移动一个字符,以此实现对单个字符的预测。
该模型在常用的数据集如enwik8和text8上的表现比RNN模型要好,但它仍有以下缺点:
Transformer-XL架构在vanilla Transformer的基础上引入了两点创新:循环机制(Recurrence Mechanism)和相对位置编码(Relative Positional Encoding),以克服vanilla Transformer的缺点。与vanilla Transformer相比,Transformer-XL的另一个优势是它可以被用于单词级和字符级的语言建模。
这两个输入会被拼接,然后用于计算当前段的Key和Value矩阵。该方法可以利用前面更多段的信息,测试阶段也可以获得更长的依赖。在测试阶段,与vanilla Transformer相比,其速度也会更快。在vanilla Transformer中,一次只能前进一个step,并且需要重新构建段,并全部从头开始计算;而在Transformer-XL中,每次可以前进一整个段,并利用之前段的数据来预测当前段的输出。
论文对于这个问题,提出了一种新的位置编码的方式,即会根据词之间的相对距离而非像Transformer中的绝对位置进行编码。从另一个角度来解读公式的话,可以将attention的计算分为如下四个部分:
详细公式见:Transformer-XL解读(论文 + PyTorch源码)
或者博客:XLNet代码分析(三)
文章最后的分析很好地证明了乱序语言模型和Transformer-XL主干网络带来的提升。这部分实验采用和BERT一致的训练数据。以BERT为基础,将BERT的主干网络从Transformer换成Transformer-XL后,在需要建模较长上下文的阅读理解任务RACE和SQuAD2.0均有比较明显地提升(对比1&2行)。而在此基础上加上乱序语言模型后,在所有任务上都有不同程度的提升 (对比 2&3 行)。
自词向量到如今以XLNet为代表的预训练语言模型,他们的主要区别在于对语境的不同粒度的建模:
XLNet成功的因素: