"干翻芝麻街"
2018 年,谷歌发布了基于双向 Transformer 的大规模预训练语言模型BERT,刷新了 11 项 NLP 任务的最优性能记录,为 NLP 领域带来了极大的惊喜。很快,BERT 就在圈内普及开来,也陆续出现了很多与它相关的新工作
BERT 带来的震撼还未平息,来自卡耐基梅隆大学与谷歌大脑的研究者又提出新型预训练语言模型 XLNet,在 SQuAD、GLUE、RACE 等 20 个任务上全面超越 BERT
作者表示,BERT 这样基于去噪自编码器的预训练模型可以很好地建模双向语境信息,性能优于基于自回归语言模型的预训练方法。然而,由于需要 mask 一部分输入,BERT 忽略了被 mask 位置之间的依赖关系,因此出现预训练和微调效果的差异(pretrain-finetune discrepancy)
基于这些优缺点,该研究提出了一种泛化的自回归预训练模型 XLNet。XLNet 可以:1)通过最大化所有可能的因式分解顺序的对数似然,学习双向语境信息;2)用自回归本身的特点克服 BERT 的缺点。此外,XLNet 还融合了当前最优自回归模型 Transformer-XL 的思路
最终,XLNet 在 20 个任务上超过了 BERT 的表现,并在 18 个任务上取得了当前最佳效果(state-of-the-art),包括机器问答、自然语言推断、情感分析和文档排序
以前超越 BERT 的模型很多都在它的基础上做一些修改,本质上模型架构和任务都没有太大变化。但是在这篇新论文中,作者从自回归(autoregressive)和自编码(autoencoding)两大范式分析了当前的预训练语言模型,并发现它们虽然各自都有优势,但也都有难以解决的困难。为此,研究者提出 XLNet,并希望结合大阵营的优秀属性
在ELMO/BERT出来之前,大家通常讲的语言模型其实是根据上文内容预测下一个可能跟随的单词,就是常说的自左向右的语言模型任务,或者反过来也行(就是根据下文预测前面的单词)。这种类型的LM被称为自回归语言模型。GPT 就是典型的自回归语言模型。ELMO尽管看上去利用了上文,也利用了下文,但是本质上仍然是自回归LM,这个跟模型具体怎么实现有关系。ELMO是分别做了两个方向的自回归LM(从左到右以及从右到左两个方向的语言模型),然后把LSTM的两个方向的隐状态拼接到一起,来体现双向语言模型这个事情的。所以其本质上仍然是自回归语言模型
给定文本序列\mathbf{x}=[x_1,…,x_T],语言模型的目标是调整参数使得训练数据上的似然函数最大:
记号\mathbf{x}_{<t}t时刻之前的所有\mathbf{x},也就是\mathbf{x}_{1:t-1}。h_\theta(\mathbf{x}_{1:t-1})是RNN或者Transformer(注:Transformer也可以用于语言模型,比如在OpenAI GPT)编码的t时刻之前的隐状态。e(x)是词x的embedding
自回归语言模型的缺点是无法同时利用上下文的信息,貌似ELMO这种双向都做,然后拼接看上去能够解决这个问题,但其实融合方法过于简单,所以效果其实并不是太好。它的优点跟下游NLP任务有关,比如生成类NLP任务,比如文本摘要,机器翻译等,在实际生成内容的时候,就是从左向右的,自回归语言模型天然匹配这个过程。而Bert这种DAE(Denoise AutoEncoder)模式,在生成类NLP任务中,面临训练过程和应用过程不一致的问题,导致生成类的NLP任务到目前为止都做不太好
BERT通过将序列\mathbf{x}中随机挑选15%的Token变成[MASK]得到带噪声版本的\hat{\mathbf{x}}。假设被Mask的原始值为\bar{\mathbf{x}},那么BERT希望尽量根据上下文恢复(猜测)出原始值,也就是:
上式中,若m_t=1表示t时刻是一个Mask,需要恢复。H_\theta是一个Transformer,它把长度为T的序列\mathbf{x}映射为隐状态的序列H_\theta(\mathbf{x})=[H_\theta(\mathbf{x})_1, H_\theta(\mathbf{x})_2, ..., H_\theta(\mathbf{x})_T]。注意:前面的语言模型的RNN在t时刻只能看到之前的时刻,因此记号是h_\theta(\mathbf{x}_{1:t-1});而BERT的Transformer(不同与用于语言模型的Transformer)可以同时看到整个句子的所有Token,因此记号是H_\theta(\mathbf{x})
这种AE LM的优缺点正好和AR LM反过来,它能比较自然地融入双向语言模型,同时看到被预测单词的上文和下文,这是好处。缺点是啥呢?主要在输入侧引入[Mask]标记,导致预训练阶段和Fine-tuning阶段不一致的问题,因为Fine-tuning阶段是看不到[Mask]标记的
XLNet的出发点就是:能否融合自回归LM和DAE LM两者的优点。具体来说就是,站在AR的角度,如何引入和双向语言模型等价的效果
作者们发现,只要在 AR 以及 AE 方式中再加入一个步骤,就能够完美地将两者统一起来,那就是Permutation
具体实现方式是,通过随机取一句话排列的一种,然后将末尾一定量的词给“遮掩”(和 BERT 里的直接替换"[MASK]" 有些不同)掉,最后用 AR 的方式来按照这种排列方式依此预测被“遮掩”掉的词
这里我稍微解释下,为什么是"遮掩"末尾的一些词,以及随机打乱句子的顺序有什么用?输入句子正常的顺序是"1 2 3 4 5 6 7",常规的自回归LM无法同时考虑上下文信息。如果能够同时考虑上下文信息,那"3"这个词,需要有"1 2 4 5 6 7"这些信息,换句话说,在预测"3"之前,我们需要保证模型已经看过"1 2 4 5 6 7"(无所谓顺序)。而打乱句子的顺序之后(比方说上图的例子),3这个词就来到了句子的末尾,此时按照自回归LM预测"3"的时候,模型已经看过了"1 2 4 5 6 7",由此便考虑到了"3"的上下文信息。当然,句子到底怎么打乱是无所谓的,因为我们的目标不是具体要预测哪个词,而是谁在最后,就预测谁
这里再谈一个有意思的点,到底该挑选最后几个做遮掩呢?作者这里设了一个超参数 K,K 等于总长度除以需要预测的个数。拿上面的例子,总长为 7 而需要预测为 2,于是 K = 7/2。而论文中实验得出的最佳 K 值介于 6 和 7 (更好)之间,其实如果我们取 K 的倒数(即\frac{1}{6},\frac{1}{7}),然后转为百分比,就会发现最佳的比值介于 14.3% 到 16.7% 之间,还记得 BERT 论文的同学肯定就会开始觉得眼熟了。因为 BERT 里将 Token 遮掩成 “[MASK]” 的百分比就是 15%,正好介于它们之间,我想这并不只是偶然,肯定有更深层的联系
对于一个长度为T的句子,我们可以遍历T!种排列,然后学习语言模型的参数,但是这个计算量非常大(10个词的句子就有10!=3628800种组合)。因此实际我们只随机的采样T!里的部分排列,为了用数学语言描述,我们引入几个记号。\mathcal{Z}_T表示长度为T的序列的所有排列组成的集合,则z \in \mathcal{Z}_T是其中一种排列方法。我们用z_t表示排列的第t个元素,而z_{<t}z的第1到第t-1个元素
举个例子,假设T=3,那么\mathcal{Z}_T共有6个元素,我们假设其中之一z=[1,3,2],则z_3=2,而z_{<3}=[1,3]
有了上面的记号,则Permutation LM的目标是调整模型参数使得下面的似然概率最大:
上面的公式看起来有点复杂,细读起来其实很简单:从所有的排列中采样一种,然后根据这个排列来分解联合概率成条件概率的乘积,然后加起来
论文中 Permutation 具体的实现方式不是打乱输入句子的顺序,而是通过对 Transformer 的 Attention Mask 进行操作
比如说序号依次为 1234 的句子,先随机取一种排列3241。根据这个排列我们就做出类似上图的 Attention Mask,先看第1行,因为在新的排列方式中 1 在最后一个,根据从左到右 AR 方式,1 就能看到 234 全部,于是第一行的 234 位置是红色的(没有遮盖掉,会用到),以此类推,第2行,因为 2 在新排列是第二个,只能看到 3 于是 3 位置是红色,第 3 行,因为 3 在第一个,看不到其他位置,所以全部遮盖掉...
上面的思想很简单,但是如果我们使用标准的Transformer实现时会有问题。下面举个例子
假设输入的句子是”I like New York”,并且一种排列为z=[1, 3, 4, 2],假设我们需要预测的是z_3=4,那么根据Simple LM的公式:
我们通常用大写的X表示随机变量,比如X_4,而小写的x表示某一个具体取值,比如假设x是"York",则p_\theta(X_4=x)表示第4个词是York的概率。用自然语言描述:p_\theta(X_4=x|x_1 x_3)表示的是第一个词是I,第3个词是New的条件下第4个词是York的概率
另外我们再假设一种排列为z’=[1,3,2,4],我们需要预测z_3=2,那么:
我们先不管预测的真实值是什么,先假设x是"York"时的概率,则p_\theta(X_2=x|x_1x_3)表示的是第一个词是I,第3个词是New的条件下第2个词是York的概率
我们仔细对比一下上面两个公式会发现它们是相等的。但是根据经验,显然这两个概率是不同的,而且上面的那个概率大一些,因为York跟在New之后是一个城市,而”York New”是什么呢?
上面问题的关键是模型并不知道要预测的那个词在原始序列中的位置。了解Transformer的读者可能会问:不是输入了位置编码吗?位置编码的信息不能起作用吗?注意:位置编码是和输入的Embedding加到一起作为输入的,因此p_\theta(X_4=x \vert x_1x_3)里的x_1,x_3是带了位置信息的,模型(可能)知道(根据输入的向量猜测)"I"是第一个词,而New是第三个词,但是第四个词的向量显然还不知道(知道了就不用预测了),因此就不可能知道它要预测的词到底是哪个位置的词,所以我们必须"显式"的告诉模型我要预测哪个位置的词
为了后面的描述,我们再把上面的两个公式写出更加一般的形式。给定排列z,我们需要计算p_\theta(X_{z_t} \vert \mathbf{x}_{z_{<t}}=x)
根据前面的讨论,我们知道问题的关键是模型并不知道要预测的到底是哪个位置的词,为了解决这个问题,我们把预测的位置z_t放到模型里:
上式中g_\theta(\mathbf{x}_{z_{<t}}, z_t)g,并且它的参数除了之前的词\mathbf{x}_{z_{<t}}z_t
接下来的问题是用什么模型来表示g_\theta(\mathbf{x}_{z_{<t}}, z_t)\mathbf{x}_{z_{<t}}z_t位置的词。那么它需要满足如下两点要求:
但是上面两点要求对于普通的Transformer来说是矛盾的无法满足的。这里非常重要,所以我这里再啰嗦一点举一个例子
假设输入的句子还是”I like New York”,并且一种排列为z=[1, 3, 4, 2],假设t=2(即z_t=z_2=3),我们现在要计算g_\theta(\mathbf{x}_{z_{<t}}, z_t)g,它可以能够比较好的预测这个概率g_\theta(x_1, z_2)。现在我们轮到计算t=3(即z_3=4),也就是根据g_\theta(x_1, z_2)和z_t来预测"York"。显然,知道第三个位置是"New"对于预测第四个位置是"York"会非常有帮助,但是g_\theta(x_1, z_2)并没有New这个词的信息。读者可能会问:你不是说g可以比较好的根据第一个词"I"预测第三个词"New"的概率吗?这里有两点:"I"后面出现"New"的概率并不高;在预测"York"时我们是知道第三个位置是New的,只不过由于模型的限制,我们无法重复利用这个信息
为了解决这个问题,论文引入了两个Stream,也就是两个隐状态:
下面我们介绍一下计算过程。我们首先把查询隐状态g_i^{(0)}初始化为一个变量w,把内容隐状态h_i^{(0)}初始化为词的Embedding e(x_i)。这里的上标0表示第0层(不存在的层,用于计算第一层)。因为内容隐状态可以编码当前词,因此初始化为词的Embedding是比较合适的
接着从m=1一直到第M层,逐层计算:
$$ \begin{split} g_{z_t}^{(m)} & \leftarrow Attention(Q=g_{z_t}^{(m-1)},KV=h_{\color{red} {z_{<t}}}^{(m-1)};\theta) \\ h_{z_t}^{(m)} & \leftarrow Attention(Q=h_{z_t}^{(m-1)},KV=h_{\color{red} {z_{\le t}}}^{(m-1)};\theta) \end{split} $$
上面两个流分别使用自己的Query向量g_{z_t}和Content向量h_{z_t};但是Key和Value向量都是用的h。但是注意Query流不能访问z_t的内容,因此K和V是h_{z_{<t}}^{(m-1)}h_{z_{\le t}}^{(m-1)},它包含\mathbf{x}_{z_t}
上面的梯度更新和标准的Self Attention是一样的。在fine-tuning的时候,我们可以丢弃掉Query流而只用Content流。最后在计算公式的时候我们可以用最上面一层的Query向量g_{z_t}^{(M)}
我们可以通过下图来直观的了解计算过程
左上图是Content流的计算,假设排列为3→2→4→1,并且我们现在预测第1个位置的词的概率。根据排列,我们可以参考所有4个词的Content,因此K\&V=[h_1^{(0)},h_2^{(0)},h_3^{(0)},h_4^{(0)}]Q=h_1^{(0)}
左下图是Query流的计算,因为不能参考自己的内容,因此K\&V=[h_2^{(0)},h_3^{(0)},h_4^{(0)}]Q=g_1^{(0)}
图的右边是完整的计算过程,我们从下往上看。首先h和g分别被初始化为e(x_i)和W,然后Content Mask和Query Mask计算第一层的输出h^{(1)}和g^{(1)},然后计算第二层……。注意最右边的两个Mask,我们先看Content Mask。它的第一行全是红点,表示第一个词可以attend to所有的词(根据3→2→4→1),第二个词可以attend to它自己和第三个词……。而Query Mask和Content Mask的区别就是不能attend to自己,因此对角线都是白点
到此为止,XLNet的核心思想已经比较清楚了。主要使用LM,但是为了解决上下文的问题,引入了Permutation LM。Permutation LM在预测时需要target的位置信息,因此通过引入Two-Stream,Content流编码到当前时刻的内容,而Query流只参考之前的历史以及当前要预测位置。最后为了解决计算量过大的问题,对于一个句子,我们只预测后\frac{1}{K}个词
接下来XLNet借鉴了Transformer-XL的优点,它对于很长的上下文的处理是要优于传统的Transformer的。我这里只是简单的介绍Transformer-XL,有兴趣的读者可以参考Transformer-XL论文
尽管Transformer最初是为翻译任务而构建的,但最近的趋势表明,它在语言建模上的应用也可以带来显著的效果。但是,为了获得最佳应用,需要对其架构进行一些修改
为什么?Transformer有什么问题?与RNN相比,Transformer的一项重大改进是其捕获长期依赖关系的能力。但是,Transformer需要存储的中间步骤(梯度)信息比RNN要多的多,并且随着序列长度的增加而增加。换句话说,如果你试图一次输入整个文档,内存可能会爆炸(BOOOOM!)
为了防止出现此问题,早期有些做法是将文档分成固定大小的文本段(Segment),一次训练一段。这虽然解决了内存问题,但是破坏了模型捕获长期依赖关系的能力。例如句子"The daughter had a nice umbrella | that her mother gave her",如果"daughter"和"her"属于不同段。那么在编码"her时将无法知晓"daughter"的信息
如何解决这个问题呢?下面就轮到Transformer-XL出场了
Transformer-XL的重要组件之一,Segment Recurrence Mechanism(段循环机制)想做的就是,能不能在前一段计算完后,将它计算出的隐状态都保存下来,存到一个Memeory中,之后在计算当前段的时候,将之前存下来的隐状态和当前段的隐状态拼起来,作为Attention机制的K和V,从而获得更长的上下文信息
根据之前的思路,我们用cache缓存部分历史的状态。计算梯度的时候只使用本segment的信息,但是在forward的时候其实用到了之前的segment(甚至很久以前的segment)的信息,因此它又有点类似于RNN。下面我们用数学语言来描述状态重用的过程。假设两个相邻的segment为s_\tau=[x_{\tau,1}, x_{\tau,2}, …, x_{\tau,L}]和s_{\tau+1}=[x_{\tau+1,1}, x_{\tau+1,2}, …, x_{\tau+1,L}]。假设segment s_\tau第n层的隐状态序列为h_\tau^n \in R^{L \times d},那么计算segment s_{\tau+1}的隐状态的过程如下:
$$ \begin{split} & \tilde{h}_{\tau+1}^{n-1}=[SG(h_{\tau}^{n-1}) \circ h_{\tau+1}^{n-1}] \\ & q_{\tau+1}^n,\ k_{\tau+1}^n,\ v_{\tau+1}^n=h_{\tau+1}^{n-1}W_q^T,\ \tilde{h}_{\tau+1}^{n-1}W_k^T,\ \tilde{h}_{\tau+1}^{n-1}W_v^T \\ & h_{\tau+1}^n=\text{Transformer-Layer}(q_{\tau+1}^n, k_{\tau+1}^n, v_{\tau+1}^n) \end{split} $$
其中,SG(h_{\tau}^{n-1})函数代表h_{\tau}^{n-1}不参与梯度计算。[h_{u} \circ h_{v}]表示向量拼接,W_q^T,W_k^T.W_v^T是模型参数。计算Query的时候用的是本段的前一层信息h_{\tau+1}^{n-1},而计算Key和Value用的是\tilde{h}_{\tau+1}^{n-1}
原则上只要GPU内存允许,该方法可以利用前面更多段的信息,测试阶段也可以获得更长的依赖(类似于DenseNet)
在Transformer中,一个重要的地方在于其考虑了序列的位置信息。在分段的情况下,如果仅仅对于每个段仍直接使用Transformer中的位置编码,即每个不同段在同一个位置上的表示使用相同的位置编码,就会出现问题。比如,第i-2段和第i−1段的第一个位置将具有相同的位置编码,但它们对于第i段的建模重要性显然并不相同(例如第i-2段中的第一个位置重要性可能要低一些)
因此Transformer-XL提出了一种相对位置编码,不再关心句中词的绝对信息,而是相对的,比如说两个词之间隔了多少个词这样的相对信息
在标准的Transformer里,同一个Segment的q_i和k_j的attention score这样分解
$$ \begin{split} A_{i,j}^{abs} & = (W_q(E_{x_i}+U_i))^T ·(W_k(E_{x_j}+U_j)) \\ & = (E_{x_i}+U_i)^TW_q^TW_k(E_{x_j}+U_j) \\ & = E_{x_i}^TW_q^TW_k(E_{x_j}+U_j) + U_i^TW_q^TW_k(E_{x_j}+U_j)\\ & = \underbrace{E^T_{x_i}W_q^TW_kE_{x_j}}_{(a)}+\underbrace{E^T_{x_i}W_q^TW_kU_j}_{(b)} \\ & + \underbrace{U_i^TW_q^TW_kE_{x_j}}_{(c)}+\underbrace{U_i^TW_q^TW_kU_j}_{(d)} \end{split} $$
其中,E_{x_i}是词i的词向量,U_i是词i的位置向量
(a)(b)(c)(d)四项各有各的意义:(a)表示纯基于内容之间的寻址;(b)和(c)则分别是i位置的内容和位置信息分别相对于j位置的位置和内容信息进行的寻址;(d)则是纯基于位置之间的寻址。于是要改进的话,就需要对后三个和位置信息相关的项进行改进
Transformer-XL给出的改进方案是这样:
$$ \begin{split} A_{i,j}^{rel} & = \underbrace{E^T_{x_i}W_q^TW_{k,E}E_{x_j}}_{(a)}+\underbrace{E^T_{x_i}W_q^TW_{k,R}\color{blue}{R_{i-j}}}_{(b)} \\ & + \underbrace{{\color{red}{u^T}}W_{k,E}E_{x_j}}_{(c)} + \underbrace{{\color{red}{v^T}}W_{k,R}\color{blue}{R_{i-j}}}_{(d)} \end{split} $$
在上面的新公式里,每一项的意义都非常清晰:(a)表示内容的计算,也就是x_i的Embedding乘以变换矩阵W_q和x_j的Embedding乘以W_{k,E}的内积;(b)表示基于内容的位置偏置,也就是i的向量乘以相对位置编码;(c)表示全局的内容偏置;(d)表示全局的位置偏置
由于很多下游NLP任务中都包含了多个句子的情况,比如问答任务。下面我们讨论怎么在自回归框架下怎么预训练两个segment。和BERT一样,我们选择两个句子,它们有50%的概率是连续的句子(前后语义相关),有50%的概率是不连续(无关)的句子。我们把这两个句子拼接后当成一个句子来学习Permutation LM。输入和BERT是类似的:[A, SEP, B, SEP, CLS],这里SEP和CLS是特殊的两个Token,而A和B代表两个Segment。与BERT稍微不同,这里把CLS放到了最后。原因是因为对于BERT来说,Self-Attention能够感知位置是因为我们把位置信息编码到输入向量了,Self-Attention的计算本身不考虑位置信息。而前面我们讨论过,为了减少计算量,这里的排列语言模型通常只预测最后1/K个Token。我们希望CLS编码所有两个Segment的语义,因此希望它是被预测的对象,而放到最后肯定是会被预测的
但是和BERT不同,XLNet并没有增加一个预测下一个句子的Task,原因是通过实验分析这个Task加进去后并不是总有帮助。【注:其实很多做法都是某些作者的经验,后面很多作者一看某个模型好,那么所有的Follow,其实也不见得就一定好。有的时候可能只是对某个数据集有效果,或者效果好是其它因素带来的,一篇文章修改了5个因素,其实可能只是某一两个因素是真正带来提高的地方,其它3个因素可能并不有用甚至还是有少量副作用】
BERT使用的是绝对的Segment编码,也就是第一个句子对于的Segment id是0,而第二个句子是1。这样如果把两个句子换一下顺序,那么输出是不一样的。XLNet使用的是相对的Segment编码,它是在计算Attention的时候判断两个词是否属于同一个Segment,如果位置i和j的词属于同一个segment,那么使用一个可以学习的Embedding s_{ij}=s_+,否则s_{ij}=s_-,也就是说,我们只关心它们是属于同一个Segment还是属于不同的Segment。当我们从位置i attend to j的时候,我们会这样计算一个新的attention score:
其中q_i是第i个位置的Query向量,b是一个可学习的bias。最后我们会把这个attention score加到原来计算的Attention score里,这样它就能学到当i和j都属于某个segment的特征,以及i和j属于不同segment的特征
Transformer-XL的计算全过程如下:
$$ \begin{split} \hat{h}_{\tau}^{n-1} & = [SG(m_{\tau}^{n-1} \circ h_{\tau}^{n-1})] \\ q_{\tau}^n, k_{\tau}^n, v_{\tau}^n & = h_{\tau}^{n-1}{W_q^n}^T, \hat{h}_{\tau}^{n-1} {W_{k,E}^n}^T, \hat{h}_{\tau}^{n-1} {W_{v}^n}^T \\ A_{\tau, i,j}^n & = {q_{\tau,i}^n}^T k_{\tau,j}^n + {q_{\tau,i}^n}^T W_{k,R}^nR_{i-j} \\ & + u^Tk_{\tau,j}^n +v^T W_{k,R}^nR_{i-j} \\ a_\tau^n & = \text{Mask-Softmax}(A_\tau^n)v_\tau^n \\ o_\tau^n & = \text{LayerNorm}(\text{Linear}(a_\tau^n)+h_\tau^{n-1}) \\ h_\tau^n & = \text{Positionwise-Feed-Forward}(o_\tau^n) \end{split} $$
关于训练值得一说的是,和 BERT 一样也是同时构建正例(正确的连续句子)和负例(随机下一句的例子),之后分别对每段进行 Permutation 处理,然后预测,对于正例,后一段会用前一段的信息,而对于负例就不用
关于训练 loss,XLNet 只用了 PLM 的 loss,却没有像 BERT 一样用 Next Sentence Prediction (下句预测)loss,但是它在句子级别任务表现却不差,对于这个现象感觉非常神奇,按理说应该是会有帮助的
以下内容仅代表个人观点
XLNet整体看起来非常恶心,不如Transformer那么简单直白。XLNet在Transformer XL的基础上引入了随机排列和双流注意力机制,因此使得整个模型变得非常复杂
XLNet训练总共使用了126GB纯文本数据,而BERT训练只使用了13GB的数据。所以虽说最终成绩XLNet超过了BERT,但究竟是因为数据的帮助,还是模型真的很好呢?