B站视频讲解

“干翻芝麻街”

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,并希望结合大阵营的优秀属性

AR与AE两大阵营

自回归语言模型(AutoRegressive LM)

在ELMO/BERT出来之前,大家通常讲的语言模型其实是根据上文内容预测下一个可能跟随的单词,就是常说的自左向右的语言模型任务,或者反过来也行(就是根据下文预测前面的单词)。这种类型的LM被称为自回归语言模型。GPT 就是典型的自回归语言模型。ELMO尽管看上去利用了上文,也利用了下文,但是本质上仍然是自回归LM,这个跟模型具体怎么实现有关系。ELMO是分别做了两个方向的自回归LM(从左到右以及从右到左两个方向的语言模型),然后把LSTM的两个方向的隐状态拼接到一起,来体现双向语言模型这个事情的。所以其本质上仍然是自回归语言模型

给定文本序列x=[x1,…,xT]\mathbf{x}=[x_1,…,x_T]x=[x1,,xT],语言模型的目标是调整参数使得训练数据上的似然函数最大:
maxθlogpθ(x)=∑t=1Tlogpθ(xt∣x<t)=∑t=1Tlogexp(hθ(x1:t−1)Te(xt))∑x′exp(hθ(x1:t−1)Te(x′))\underset{\theta}{max}\; log p_\theta(\mathbf{x})=\sum_{t=1}^T log p_\theta(x_t \vert \mathbf{x}_{<t})=\sum_{t=1}^T log \frac{exp(h_\theta(\mathbf{x}_{1:t-1})^T e(x_t))}{\sum_{x'}exp(h_\theta(\mathbf{x}_{1:t-1})^T e(x'))} θmaxlogpθ(x)=t=1Tlogpθ(xtx<t)=t=1Tlogxexp(hθ(x1:t1)Te(x))exp(hθ(x1:t1)Te(xt))
记号x<t\mathbf{x}_{<t}x<t表示ttt时刻之前的所有xxx,也就是x1:t−1\mathbf{x}_{1:t-1}x1:t1hθ(x1:t−1)h_\theta(\mathbf{x}_{1:t-1})hθ(x1:t1)是RNN或者Transformer(注:Transformer也可以用于语言模型,比如在OpenAI GPT)编码的t时刻之前的隐状态。e(x)e(x)e(x)是词xxx的embedding

自回归语言模型的缺点是无法同时利用上下文的信息,貌似ELMO这种双向都做,然后拼接看上去能够解决这个问题,但其实融合方法过于简单,所以效果其实并不是太好。它的优点跟下游NLP任务有关,比如生成类NLP任务,比如文本摘要,机器翻译等,在实际生成内容的时候,就是从左向右的,自回归语言模型天然匹配这个过程。而Bert这种DAE(Denoise AutoEncoder)模式,在生成类NLP任务中,面临训练过程和应用过程不一致的问题,导致生成类的NLP任务到目前为止都做不太好

自编码语言模型(AutoEncoder LM)

BERT通过将序列x\mathbf{x}x中随机挑选15%的Token变成[MASK]得到带噪声版本的x^\hat{\mathbf{x}}x^。假设被Mask的原始值为xˉ\bar{\mathbf{x}}xˉ,那么BERT希望尽量根据上下文恢复(猜测)出原始值,也就是:
maxθlogpθ(xˉ∣x^)≈∑t=1Tmtlogpθ(xt∣x^)=∑t=1Tmtlogexp(Hθ(x)tTe(xt))∑x′exp(Hθ(x)tTe(x′))\underset{\theta}{max}\;log p_\theta(\bar{\mathbf{x}} | \hat{\mathbf{x}}) \approx \sum_{t=1}^Tm_t log p_\theta(x_t | \hat{\mathbf{x}})=\sum_{t=1}^T m_t log \frac{exp(H_\theta(\mathbf{x})_{t}^T e(x_t))}{\sum_{x'}exp(H_\theta(\mathbf{x})_{t}^T e(x'))} θmaxlogpθ(xˉx^)t=1Tmtlogpθ(xtx^)=t=1Tmtlogxexp(Hθ(x)tTe(x))exp(Hθ(x)tTe(xt))
上式中,若mt=1m_t=1mt=1表示ttt时刻是一个Mask,需要恢复。HθH_\thetaHθ是一个Transformer,它把长度为TTT的序列x\mathbf{x}x映射为隐状态的序列Hθ(x)=[Hθ(x)1,Hθ(x)2,...,Hθ(x)T]H_\theta(\mathbf{x})=[H_\theta(\mathbf{x})_1, H_\theta(\mathbf{x})_2, ..., H_\theta(\mathbf{x})_T]Hθ(x)=[Hθ(x)1,Hθ(x)2,...,Hθ(x)T]。注意:前面的语言模型的RNN在ttt时刻只能看到之前的时刻,因此记号是hθ(x1:t−1)h_\theta(\mathbf{x}_{1:t-1})hθ(x1:t1);而BERT的Transformer(不同与用于语言模型的Transformer)可以同时看到整个句子的所有Token,因此记号是Hθ(x)H_\theta(\mathbf{x})Hθ(x)

这种AE LM的优缺点正好和AR LM反过来,它能比较自然地融入双向语言模型,同时看到被预测单词的上文和下文,这是好处。缺点是啥呢?主要在输入侧引入[Mask]标记,导致预训练阶段和Fine-tuning阶段不一致的问题,因为Fine-tuning阶段是看不到[Mask]标记的

XLNet的出发点就是:能否融合自回归LM和DAE LM两者的优点。具体来说就是,站在AR的角度,如何引入和双向语言模型等价的效果

Permutation Language Model

作者们发现,只要在 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 的倒数(即16,17\frac{1}{6},\frac{1}{7}61,71),然后转为百分比,就会发现最佳的比值介于 14.3% 到 16.7% 之间,还记得 BERT 论文的同学肯定就会开始觉得眼熟了。因为 BERT 里将 Token 遮掩成 “[MASK]” 的百分比就是 15%,正好介于它们之间,我想这并不只是偶然,肯定有更深层的联系

对于一个长度为TTT的句子,我们可以遍历T!T!T!种排列,然后学习语言模型的参数,但是这个计算量非常大(10个词的句子就有10!=3628800种组合)。因此实际我们只随机的采样T!T!T!里的部分排列,为了用数学语言描述,我们引入几个记号。ZT\mathcal{Z}_TZT表示长度为TTT的序列的所有排列组成的集合,则z∈ZTz \in \mathcal{Z}_TzZT是其中一种排列方法。我们用ztz_tzt表示排列的第ttt个元素,而z<tz_{<t}z<t表示zzz的第1到第t−1t-1t1个元素

举个例子,假设T=3T=3T=3,那么ZT\mathcal{Z}_TZT共有6个元素,我们假设其中之一z=[1,3,2]z=[1,3,2]z=[1,3,2],则z3=2z_3=2z3=2,而z<3=[1,3]z_{<3}=[1,3]z<3=[1,3]

有了上面的记号,则Permutation LM的目标是调整模型参数使得下面的似然概率最大:
maxθEz∼ZT[∑t=1Tlogpθ(xzt∣xz<t)]\underset{\theta}{max} \mathbb{E}_{z \sim \mathcal{Z}_T}[\sum_{t=1}^Tlog p_\theta(x_{z_t}|\mathbf{x}_{z_{<t}})] θmaxEzZT[t=1Tlogpθ(xztxz<t)]
上面的公式看起来有点复杂,细读起来其实很简单:从所有的排列中采样一种,然后根据这个排列来分解联合概率成条件概率的乘积,然后加起来

论文中 Permutation 具体的实现方式不是打乱输入句子的顺序,而是通过对 Transformer 的 Attention Mask 进行操作

比如说序号依次为 1234 的句子,先随机取一种排列3241。根据这个排列我们就做出类似上图的 Attention Mask,先看第1行,因为在新的排列方式中 1 在最后一个,根据从左到右 AR 方式,1 就能看到 234 全部,于是第一行的 234 位置是红色的(没有遮盖掉,会用到),以此类推,第2行,因为 2 在新排列是第二个,只能看到 3 于是 3 位置是红色,第 3 行,因为 3 在第一个,看不到其他位置,所以全部遮盖掉…

没有目标(target)位置信息的问题

上面的思想很简单,但是如果我们使用标准的Transformer实现时会有问题。下面举个例子

假设输入的句子是”I like New York”,并且一种排列为z=[1, 3, 4, 2],假设我们需要预测的是z3=4z_3=4z3=4,那么根据Simple LM的公式:
pθ(Xz3=x∣xz1z2)=pθ(X4=x∣x1x3)=exp(e(x)Thθ(x1x3))∑x′exp(e(x′)Thθ(x1x3))p_\theta(X_{z_3}=x|x_{z_1z_2})=p_\theta(X_4=x|x_1x_3)=\frac{exp(e(x)^Th_\theta(x_1x_3))}{\sum_{x'}exp(e(x')^Th_\theta(x_1x_3))} pθ(Xz3=xxz1z2)=pθ(X4=xx1x3)=xexp(e(x)Thθ(x1x3))exp(e(x)Thθ(x1x3))
我们通常用大写的XXX表示随机变量,比如X4X_4X4,而小写的xxx表示某一个具体取值,比如假设xxx是"York",则pθ(X4=x)p_\theta(X_4=x)pθ(X4=x)表示第4个词是York的概率。用自然语言描述:pθ(X4=x∣x1x3)p_\theta(X_4=x|x_1 x_3)pθ(X4=xx1x3)表示的是第一个词是I,第3个词是New的条件下第4个词是York的概率

另外我们再假设一种排列为z’=[1,3,2,4],我们需要预测z3=2z_3=2z3=2,那么:
pθ(Xz3=x∣xz1z2)=pθ(X2=x∣x1x3)=exp(e(x)Thθ(x1x3))∑x′exp(e(x′)Thθ(x1x3))p_\theta(X_{z_3}=x|x_{z_1z_2})=p_\theta(X_2=x|x_1x_3)=\frac{exp(e(x)^Th_\theta(x_1x_3))}{\sum_{x'}exp(e(x')^Th_\theta(x_1x_3))} pθ(Xz3=xxz1z2)=pθ(X2=xx1x3)=xexp(e(x)Thθ(x1x3))exp(e(x)Thθ(x1x3))
我们先不管预测的真实值是什么,先假设xxx是"York"时的概率,则pθ(X2=x∣x1x3)p_\theta(X_2=x|x_1x_3)pθ(X2=xx1x3)表示的是第一个词是I,第3个词是New的条件下第2个词是York的概率

我们仔细对比一下上面两个公式会发现它们是相等的。但是根据经验,显然这两个概率是不同的,而且上面的那个概率大一些,因为York跟在New之后是一个城市,而”York New”是什么呢?

上面问题的关键是模型并不知道要预测的那个词在原始序列中的位置。了解Transformer的读者可能会问:不是输入了位置编码吗?位置编码的信息不能起作用吗?注意:位置编码是和输入的Embedding加到一起作为输入的,因此pθ(X4=x∣x1x3)p_\theta(X_4=x \vert x_1x_3)pθ(X4=xx1x3)里的x1,x3x_1,x_3x1,x3是带了位置信息的,模型(可能)知道(根据输入的向量猜测)"I"是第一个词,而New是第三个词,但是第四个词的向量显然还不知道(知道了就不用预测了),因此就不可能知道它要预测的词到底是哪个位置的词,所以我们必须"显式"的告诉模型我要预测哪个位置的词

为了后面的描述,我们再把上面的两个公式写出更加一般的形式。给定排列z,我们需要计算pθ(Xzt∣xz<t=x)p_\theta(X_{z_t} \vert \mathbf{x}_{z_{<t}}=x)pθ(Xztxz<t=x),如果我们使用普通的Transformer,那么计算公式为:
pθ(Xzt=x∣xz<t)=exp(e(x)Thθ(xz<t))∑x′exp(e(x′)Thθ(xz<t))p_\theta(X_{z_t}=x \vert \mathbf{x}_{z_{<t}})=\frac{exp(e(x)^Th_\theta(\mathbf{x}_{z_{<t}}))}{\sum_{x'}exp(e(x')^Th_\theta(\mathbf{x}_{z_{<t}}))} pθ(Xzt=xxz<t)=xexp(e(x)Thθ(xz<t))exp(e(x)Thθ(xz<t))
根据前面的讨论,我们知道问题的关键是模型并不知道要预测的到底是哪个位置的词,为了解决这个问题,我们把预测的位置ztz_tzt放到模型里:
pθ(Xzt=x∣xz<t)=exp(e(x)Tgθ(xz<t,zt))∑x′exp(e(x′)Tgθ(xz<t,zt))p_\theta(X_{z_t}=x \vert \mathbf{x}_{z_{<t}})=\frac{exp(e(x)^Tg_\theta(\mathbf{x}_{z_{<t}}, z_t))}{\sum_{x'}exp(e(x')^Tg_\theta(\mathbf{x}_{z_{<t}}, z_t))} pθ(Xzt=xxz<t)=xexp(e(x)Tgθ(xz<t,zt))exp(e(x)Tgθ(xz<t,zt))
上式中gθ(xz<t,zt)g_\theta(\mathbf{x}_{z_{<t}}, z_t)gθ(xz<t,zt)表示这是一个新的模型ggg,并且它的参数除了之前的词xz<t\mathbf{x}_{z_{<t}}xz<t,还有要预测的词的位置ztz_tzt

Two-Stream Self-Attention

接下来的问题是用什么模型来表示gθ(xz<t,zt)g_\theta(\mathbf{x}_{z_{<t}}, z_t)gθ(xz<t,zt)。当然有很多种可选的函数(模型),我们需要利用xz<t\mathbf{x}_{z_{<t}}xz<t,通过Attention机制提取需要的信息,然后预测ztz_tzt位置的词。那么它需要满足如下两点要求:

  • 为了预测xzt\mathbf{x}_{z_t}xztgθ(xz<t,zt)g_\theta(\mathbf{x}_{z_{<t}}, z_t)gθ(xz<t,zt)只能使用位置信息ztz_tzt而不能使用xzt\mathbf{x}_{z_t}xzt。这是显然的:你预测一个词当然不能知道要预测的是什么词
  • 为了预测ztz_tzt之后的词,gθ(xz<t,zt)g_\theta(\mathbf{x}_{z_{<t}}, z_t)gθ(xz<t,zt)必须编码了xzt\mathbf{x}_{z_t}xzt的信息(语义)

但是上面两点要求对于普通的Transformer来说是矛盾的无法满足的。这里非常重要,所以我这里再啰嗦一点举一个例子

假设输入的句子还是”I like New York”,并且一种排列为z=[1,3,4,2]z=[1, 3, 4, 2]z=[1,3,4,2],假设t=2t=2t=2(即zt=z2=3z_t=z_2=3zt=z2=3),我们现在要计算gθ(xz<t,zt)g_\theta(\mathbf{x}_{z_{<t}}, z_t)gθ(xz<t,zt),也就是给定第一个位置的词为"I",预测第三个位置为"New"的概率。显然我们不能使用"New"本身的信息,而只能根据第一个位置的"I"来预测。假设我们非常幸运的找到了一很好的函数ggg,它可以能够比较好的预测这个概率gθ(x1,z2)g_\theta(x_1, z_2)gθ(x1,z2)。现在我们轮到计算t=3t=3t=3(即z3=4z_3=4z3=4),也就是根据gθ(x1,z2)g_\theta(x_1, z_2)gθ(x1,z2)ztz_tzt来预测"York"。显然,知道第三个位置是"New"对于预测第四个位置是"York"会非常有帮助,但是gθ(x1,z2)g_\theta(x_1, z_2)gθ(x1,z2)并没有New这个词的信息。读者可能会问:你不是说ggg可以比较好的根据第一个词"I"预测第三个词"New"的概率吗?这里有两点:"I"后面出现"New"的概率并不高;在预测"York"时我们是知道第三个位置是New的,只不过由于模型的限制,我们无法重复利用这个信息

为了解决这个问题,论文引入了两个Stream,也就是两个隐状态:

  • 内容隐状态hθ(xz<t)h_\theta(\mathbf{x}_{z_{<t}})hθ(xz<t),简写为hzth_{z_t}hzt,它就和标准的Transformer一样,既编码上下文(context)也编码xzt\mathbf{x}_{z_t}xzt的内容
  • 查询隐状态gθ(xz<t,zt)g_\theta(\mathbf{x}_{z_{<t}}, z_t)gθ(xz<t,zt),简写为gztg_{z_t}gzt,它只编码上下文和要预测的位置ztz_tzt,但是不包含xzt\mathbf{x}_{z_t}xzt

下面我们介绍一下计算过程。我们首先把查询隐状态gi(0)g_i^{(0)}gi(0)初始化为一个变量www,把内容隐状态hi(0)h_i^{(0)}hi(0)初始化为词的Embedding e(xi)e(x_i)e(xi)。这里的上标0表示第0层(不存在的层,用于计算第一层)。因为内容隐状态可以编码当前词,因此初始化为词的Embedding是比较合适的

接着从m=1一直到第M层,逐层计算:
gzt(m)←Attention(Q=gzt(m−1),KV=hz<t(m−1);θ)hzt(m)←Attention(Q=hzt(m−1),KV=hz≤t(m−1);θ)\begin{aligned} 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{aligned} gzt(m)hzt(m)Attention(Q=gzt(m1),KV=hz<t(m1);θ)Attention(Q=hzt(m1),KV=hzt(m1);θ)

  • Query Stream: use ztz_tzt but cannot see xzt\mathbf{x}_{z_t}xzt
  • Content Stream: use both ztz_tzt and xzt\mathbf{x}_{z_t}xzt

上面两个流分别使用自己的Query向量gztg_{z_t}gzt和Content向量hzth_{z_t}hzt;但是Key和Value向量都是用的hhh。但是注意Query流不能访问ztz_tzt的内容,因此K和V是hz<t(m−1)h_{z_{<t}}^{(m-1)}hz<t(m1)。而Content流的KV是hz≤t(m−1)h_{z_{\le t}}^{(m-1)}hzt(m1),它包含xzt\mathbf{x}_{z_t}xzt

上面的梯度更新和标准的Self Attention是一样的。在fine-tuning的时候,我们可以丢弃掉Query流而只用Content流。最后在计算公式的时候我们可以用最上面一层的Query向量gzt(M)g_{z_t}^{(M)}gzt(M)

我们可以通过下图来直观的了解计算过程

左上图是Content流的计算,假设排列为3→2→4→13→2→4→13241,并且我们现在预测第1个位置的词的概率。根据排列,我们可以参考所有4个词的Content,因此K&V=[h1(0),h2(0),h3(0),h4(0)]K\&V=[h_1^{(0)},h_2^{(0)},h_3^{(0)},h_4^{(0)}]K&V=[h1(0),h2(0),h3(0),h4(0)],而Q=h1(0)Q=h_1^{(0)}Q=h1(0)

左下图是Query流的计算,因为不能参考自己的内容,因此K&V=[h2(0),h3(0),h4(0)]K\&V=[h_2^{(0)},h_3^{(0)},h_4^{(0)}]K&V=[h2(0),h3(0),h4(0)],而Q=g1(0)Q=g_1^{(0)}Q=g1(0)

图的右边是完整的计算过程,我们从下往上看。首先hhhggg分别被初始化为e(xi)e(x_i)e(xi)WWW,然后Content Mask和Query Mask计算第一层的输出h(1)h^{(1)}h(1)g(1)g^{(1)}g(1),然后计算第二层……。注意最右边的两个Mask,我们先看Content Mask。它的第一行全是红点,表示第一个词可以attend to所有的词(根据3→2→4→13→2→4→13241),第二个词可以attend to它自己和第三个词……。而Query Mask和Content Mask的区别就是不能attend to自己,因此对角线都是白点

到此为止,XLNet的核心思想已经比较清楚了。主要使用LM,但是为了解决上下文的问题,引入了Permutation LM。Permutation LM在预测时需要target的位置信息,因此通过引入Two-Stream,Content流编码到当前时刻的内容,而Query流只参考之前的历史以及当前要预测位置。最后为了解决计算量过大的问题,对于一个句子,我们只预测后1K\frac{1}{K}K1个词

接下来XLNet借鉴了Transformer-XL的优点,它对于很长的上下文的处理是要优于传统的Transformer的。我这里只是简单的介绍Transformer-XL,有兴趣的读者可以参考Transformer-XL论文

Transformer-XL

Segment Recurrence Mechanism

尽管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τ=[xτ,1,xτ,2,…,xτ,L]s_\tau=[x_{\tau,1}, x_{\tau,2}, …, x_{\tau,L}]sτ=[xτ,1,xτ,2,,xτ,L]sτ+1=[xτ+1,1,xτ+1,2,…,xτ+1,L]s_{\tau+1}=[x_{\tau+1,1}, x_{\tau+1,2}, …, x_{\tau+1,L}]sτ+1=[xτ+1,1,xτ+1,2,,xτ+1,L]。假设segment sτs_\tausτ第n层的隐状态序列为hτn∈RL×dh_\tau^n \in R^{L \times d}hτnRL×d,那么计算segment sτ+1s_{\tau+1}sτ+1的隐状态的过程如下:
h~τ+1n−1=[SG(hτn−1)∘hτ+1n−1]qτ+1n,kτ+1n,vτ+1n=hτ+1n−1WqT,h~τ+1n−1WkT,h~τ+1n−1WvThτ+1n=Transformer-Layer(qτ+1n,kτ+1n,vτ+1n)\begin{aligned} & \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{aligned} h~τ+1n1=[SG(hτn1)hτ+1n1]qτ+1n,kτ+1n,vτ+1n=hτ+1n1WqT,h~τ+1n1WkT,h~τ+1n1WvThτ+1n=Transformer-Layer(qτ+1n,kτ+1n,vτ+1n)
其中,SG(hτn−1)SG(h_{\tau}^{n-1})SG(hτn1)函数代表hτn−1h_{\tau}^{n-1}hτn1不参与梯度计算。[hu∘hv][h_{u} \circ h_{v}][huhv]表示向量拼接,WqT,WkT.WvTW_q^T,W_k^T.W_v^TWqT,WkT.WvT是模型参数。计算Query的时候用的是本段的前一层信息hτ+1n−1h_{\tau+1}^{n-1}hτ+1n1,而计算Key和Value用的是h~τ+1n−1\tilde{h}_{\tau+1}^{n-1}h~τ+1n1

原则上只要GPU内存允许,该方法可以利用前面更多段的信息,测试阶段也可以获得更长的依赖(类似于DenseNet)

Relative Positional Encoding

在Transformer中,一个重要的地方在于其考虑了序列的位置信息。在分段的情况下,如果仅仅对于每个段仍直接使用Transformer中的位置编码,即每个不同段在同一个位置上的表示使用相同的位置编码,就会出现问题。比如,第i−2i-2i2段和第i−1i−1i1段的第一个位置将具有相同的位置编码,但它们对于第iii段的建模重要性显然并不相同(例如第i−2i-2i2段中的第一个位置重要性可能要低一些)

因此Transformer-XL提出了一种相对位置编码,不再关心句中词的绝对位置信息,而是相对的,比如说两个词之间隔了多少个词这样的相对信息

在标准的Transformer里,同一个Segment的qiq_iqikjk_jkj的attention score这样分解
Ai,jabs=(Wq(Exi+Ui))T⋅(Wk(Exj+Uj))=(Exi+Ui)TWqTWk(Exj+Uj)=ExiTWqTWk(Exj+Uj)+UiTWqTWk(Exj+Uj)=ExiTWqTWkExj⏟(a)+ExiTWqTWkUj⏟(b)+UiTWqTWkExj⏟(c)+UiTWqTWkUj⏟(d)\begin{aligned} 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{aligned} Ai,jabs=(Wq(Exi+Ui))T(Wk(Exj+Uj))=(Exi+Ui)TWqTWk(Exj+Uj)=ExiTWqTWk(Exj+Uj)+UiTWqTWk(Exj+Uj)=(a)

ExiTWqTWkExj+(b)

ExiTWqTWkUj
+(c)

UiTWqTWkExj
+(d)

UiTWqTWkUj

其中,ExiE_{x_i}Exi是词iii的词向量,UiU_iUi是词iii的位置向量

(a)(b)©(d)四项各有各的意义:(a)表示纯基于内容之间的寻址;(b)和©则分别是iii位置的内容和位置信息分别相对于jjj位置的位置和内容信息进行的寻址;(d)则是纯基于位置之间的寻址。于是要改进的话,就需要对后三个和位置信息相关的项进行改进

Transformer-XL给出的改进方案是这样:
Ai,jrel=ExiTWqTWk,EExj⏟(a)+ExiTWqTWk,RRi−j⏟(b)+uTWk,EExj⏟(c)+vTWk,RRi−j⏟(d)\begin{aligned} 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{aligned} Ai,jrel=(a)

ExiTWqTWk,EExj+(b)

ExiTWqTWk,RRij
+(c)

uTWk,EExj
+(d)

vTWk,RRij

  • 和前面的Ai,jabsA_{i,j}^{abs}Ai,jabs相比,第一个改动是将(b)和(d)里的绝对位置编码UjU_jUj都替换成相对位置编码向量Ri−jR_{i-j}Rij。注意这里的RRR是之前介绍的正弦函数的编码方式,它是固定的,不需要学习
  • 在©中用可训练的u∈Rd{\color{red}{u}} \in R^duRd替代原来的UiTWqTU_i^TW_q^TUiTWqT。因为我们假设Attention score只依赖于iiijjj的相对位置,而与iii的绝对位置无关,所以这里对于所有的iii都相同。也就是UTWqTU^TW_q^TUTWqT,所以可以用一个新的u\color{red}uu来表示。同理,(d)中的v∈Rd{\color{red}{v}}\in R^dvRd也一样
  • 最后,我们把Key的变换矩阵WkW_kWk拆分成Wk,EW_{k,E}Wk,EWk,RW_{k,R}Wk,R,分别给内容向量和相对位置向量用

在上面的新公式里,每一项的意义都非常清晰:(a)表示内容的计算,也就是xix_ixi的Embedding乘以变换矩阵WqW_qWqxjx_jxj的Embedding乘以Wk,EW_{k,E}Wk,E的内积;(b)表示基于内容的位置偏置,也就是iii的向量乘以相对位置编码;©表示全局的内容偏置;(d)表示全局的位置偏置

Relative Segment Encoding

由于很多下游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,如果位置iiijjj的词属于同一个segment,那么使用一个可以学习的Embedding sij=s+s_{ij}=s_+sij=s+,否则sij=s−s_{ij}=s_-sij=s,也就是说,我们只关心它们是属于同一个Segment还是属于不同的Segment。当我们从位置iii attend to jjj的时候,我们会这样计算一个新的attention score:
aij=(qi+b)Tsija_{ij}=(q_i+b)^Ts_{ij} aij=(qi+b)Tsij

其中qiq_iqi是第iii个位置的Query向量,b是一个可学习的bias。最后我们会把这个attention score加到原来计算的Attention score里,这样它就能学到当iiijjj都属于某个segment的特征,以及iiijjj属于不同segment的特征

Conclusion

Transformer-XL的计算全过程如下:
h^τn−1=[SG(mτn−1∘hτn−1)]qτn,kτn,vτn=hτn−1WqnT,h^τn−1Wk,EnT,h^τn−1WvnTAτ,i,jn=qτ,inTkτ,jn+qτ,inTWk,RnRi−j+uTkτ,jn+vTWk,RnRi−jaτn=Mask-Softmax(Aτn)vτnoτn=LayerNorm(Linear(aτn)+hτn−1)hτn=Positionwise-Feed-Forward(oτn)\begin{aligned} \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{aligned} h^τn1qτn,kτn,vτnAτ,i,jnaτnoτnhτn=[SG(mτn1hτn1)]=hτn1WqnT,h^τn1Wk,EnT,h^τn1WvnT=qτ,inTkτ,jn+qτ,inTWk,RnRij+uTkτ,jn+vTWk,RnRij=Mask-Softmax(Aτn)vτn=LayerNorm(Linear(aτn)+hτn1)=Positionwise-Feed-Forward(oτn)
关于训练值得一说的是,和 BERT 一样也是同时构建正例(正确的连续句子)和负例(随机下一句的例子),之后分别对每段进行 Permutation 处理,然后预测,对于正例,后一段会用前一段的信息,而对于负例就不用

关于训练 loss,XLNet 只用了 PLM 的 loss,却没有像 BERT 一样用 Next Sentence Prediction (下句预测)loss,但是它在句子级别任务表现却不差,对于这个现象感觉非常神奇,按理说应该是会有帮助的

以下内容仅代表个人观点

XLNet整体看起来非常恶心,不如Transformer那么简单直白。XLNet在Transformer XL的基础上引入了随机排列和双流注意力机制,因此使得整个模型变得非常复杂

XLNet训练总共使用了126GB纯文本数据,而BERT训练只使用了13GB的数据。所以虽说最终成绩XLNet超过了BERT,但究竟是因为数据的帮助,还是模型真的很好呢?

Reference

  • XLNet运行机制及和Bert的异同比较

  • XLNet原理-李理的博客

  • XLNet代码分析(三)-李理的博客

  • Transformer-XL解读

  • 飞跃芝麻街:XLNet详解

XLNet 详解(看不懂你来骂我)相关推荐

  1. 平衡二叉树的左旋右旋详解 看不懂你打我

    平衡二叉树的左旋右旋 看不懂你打我 左旋右旋的操作 为什么要左旋右旋 左旋右旋能保持排序二叉排序树的性质吗 下次写平衡二叉树的LL.RR.LR.RL. 左旋右旋的操作 1.左旋:对X节点左旋,即以X的 ...

  2. 最火的几个全网络预训练模型梳理整合(BERT、ALBERT、XLNet详解)

    前言 过去两年可谓是NLP领域的高光年,自从18年BERT提出,狂刷了11项下游任务的记录之后,全网络预训练模型开启了NLP嵌入表示的新时代,真正意义上实现了自然语言处理的迁移学习. 作为一个刚入坑没 ...

  3. 大端模式小端模式详解(不懂你打我,略略~~)

    大端模式小端模式详解(不懂你打我,略略~~) 先看概念 大端模式(Big-Endian),是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作 ...

  4. 代理后台中间件_Golang Gin 实战(十三)| 中间件详解看这一篇就够了

    6000字大章带你死磕Golang Gin中间件 在Gin的整个实现中,中间件可谓是Gin的精髓.一个个中间件组成一条中间件链,对HTTP Request请求进行拦截处理,实现了代码的解耦和分离,并且 ...

  5. 计算机网络参数怎么看,电脑显示器参数详解 看完秒懂! 显示器参数怎么看?...

    显示器参数怎么看?无论是显示器的宣传.购买页面,还是专业媒体的显示器评测中,我们都会看到一些用来描述显示器性能的参数.小编觉着,显示器就像是电脑的一张面孔,显示着它的各种表情,也负责与用户的沟通,而显 ...

  6. JNI详解---从不懂到理解

     Chap1:JNI完全手册... 3 Chap2:JNI-百度百科... 11 Chap 3:javah命令帮助信息... 16 Chap 4:用javah产生一个.h文件... 17 Chap ...

  7. 查看计算机配置讲解,教您如何看显示器参数!电脑显示器常见参数详解,看完秒懂!...

    由于显示器可选的范围比较广,同一个尺寸大小的显示器,不同品牌不同型号不同参数会有上百种可供选择,其实我们只需明确了预算与用途,才可以正确选择合适自己的显示器.那么如何看显示器参数?下面装机之家分享一下 ...

  8. Java-方法的详解( 看完之后,我不允许还有人不懂!)

    文章目录 一.什么是方法 二.方法定义 三.方法调用 四.方法重载 1.什么是方法重载? 2.为什么会出现方法重载? 3.被称为方法重载的必要条件是什么? 4.编译器是如何识别出相同名字的方法的呢? ...

  9. 各类IP地址的划分范围详解(看完不懂算我输)

    A类IP地址的范围 一个A类IP地址由1字节的网络地址和3字节主机地址组成,网络地址的最高位必须是"0", 地址范围从1.x.x.x 到126.x.x.x. 全0和全一不能用的原因 ...

最新文章

  1. Python外壳:代码结构!(IF WHILE FOR RANGE...)
  2. android正则表达式隐藏邮箱地址中间字符
  3. bool类型头文件_[C++基础入门] 2、数据类型
  4. Web前端开发笔记——第二章 HTML语言 第八节 表单标签
  5. Python中的类属性和实例属性以及静态方法和类方法
  6. 古代的酒到底多少度,为何古人动不动喝好几坛都不会醉呢?
  7. 面试—每日一题(4)
  8. nil 作比较时应该加上双引号
  9. 算法在岗一年的经验总结
  10. 《Visual Studio Hacks 》读书笔记 (七)
  11. 深入浅出设计模式之工厂模式
  12. web压力测试的几个指标
  13. mysql的R树,GIS空间数据库(17)R+树索引
  14. 软件定义 硬件驱动,云计算的Hybrid时代
  15. 【备忘】LAMP兄弟连李明老师讲Linux[更新完毕-共享完毕]
  16. CSP 201903-2 二十四点 python (python有如神助)
  17. 作为一名女程序员是什么体验?
  18. recovery之刷机脚本自定义(解决刷zip文件时出现Status 6错误)
  19. 10月更新!又一波新功能上线,升级后的EasyOps®简直神了!
  20. COM Interop

热门文章

  1. 十一大排序算法的实现
  2. 张爱玲的 因为懂得,所以慈悲 如何理解
  3. Java全栈学习路线-拭去心尘
  4. 读书百客:《送客之江西》赏析
  5. 4872: [Shoi2017]分手是祝愿
  6. 五年级上册《桂花雨》教案
  7. 金秋杭州游 只为桂花香
  8. 概述纵横制电话交换机
  9. raft协议对网络分区的处理
  10. 狼牙月下----纳兰的、执子之手,生死勿离