"干翻芝麻街"
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 的两个方向的隐状态拼接到一起,来体现双向语言模型这个事情的。所以其本质上仍然是自回归语言模型
给定文本序列 $\mathbf {x}=[x_1,…,x_T]$,语言模型的目标是调整参数使得训练数据上的似然函数最大:
$$ \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'))} $$
记号 $\mathbf {x}_{<t}$ 表示 $t$ 时刻之前的所有 $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 任务到目前为止都做不太好
自编码语言模型(AutoEncoder LM)
BERT 通过将序列 $\mathbf {x}$ 中随机挑选 15% 的 Token 变成 [MASK] 得到带噪声版本的 $\hat {\mathbf {x}}$。假设被 Mask 的原始值为 $\bar {\mathbf {x}}$,那么 BERT 希望尽量根据上下文恢复(猜测)出原始值,也就是:
$$ \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'))} $$
上式中,若 $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 的角度,如何引入和双向语言模型等价的效果
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 的倒数(即 $\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 的目标是调整模型参数使得下面的似然概率最大:
$$ \underset{\theta}{max} \mathbb{E}_{z \sim \mathcal{Z}_T}[\sum_{t=1}^T\log p_\theta(x_{z_t}|\mathbf{x}_{z_{<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],假设我们需要预测的是 $z_3=4$,那么根据 Simple LM 的公式:
$$ 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))} $$
我们通常用大写的 $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$,那么:
$$ 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))} $$
我们先不管预测的真实值是什么,先假设 $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)$,如果我们使用普通的 Transformer,那么计算公式为:
$$ 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}}))} $$
根据前面的讨论,我们知道问题的关键是模型并不知道要预测的到底是哪个位置的词,为了解决这个问题,我们把预测的位置 $z_t$ 放到模型里:
$$ 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))} $$
上式中 $g_\theta (\mathbf {x}_{z_{<t}}, z_t)$ 表示这是一个新的模型 $g$,并且它的参数除了之前的词 $\mathbf {x}_{z_{<t}}$,还有要预测的词的位置 $z_t$
Two-Stream Self-Attention
接下来的问题是用什么模型来表示 $g_\theta (\mathbf {x}_{z_{<t}}, z_t)$。当然有很多种可选的函数(模型),我们需要利用 $\mathbf {x}_{z_{<t}}$,通过 Attention 机制提取需要的信息,然后预测 $z_t$ 位置的词。那么它需要满足如下两点要求:
- 为了预测 $\mathbf {x}_{z_t}$,$g_\theta (\mathbf {x}_{z_{<t}}, z_t)$ 只能使用位置信息 $z_t$ 而不能使用 $\mathbf {x}_{z_t}$。这是显然的:你预测一个词当然不能知道要预测的是什么词
- 为了预测 $z_t$ 之后的词,$g_\theta (\mathbf {x}_{z_{<t}}, z_t)$ 必须编码了 $\mathbf {x}_{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)$,也就是给定第一个位置的词为 "I",预测第三个位置为 "New" 的概率。显然我们不能使用 "New" 本身的信息,而只能根据第一个位置的 "I" 来预测。假设我们非常幸运的找到了一很好的函数 $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,也就是两个隐状态:
- 内容隐状态 $h_\theta (\mathbf {x}_{z_{<t}})$,简写为 $h_{z_t}$,它就和标准的 Transformer 一样,既编码上下文(context)也编码 $\mathbf {x}_{z_t}$ 的内容
- 查询隐状态 $g_\theta (\mathbf {x}_{z_{<t}}, z_t)$,简写为 $g_{z_t}$,它只编码上下文和要预测的位置 $z_t$,但是不包含 $\mathbf {x}_{z_t}$
下面我们介绍一下计算过程。我们首先把查询隐状态 $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 Stream: use $z_t$ but cannot see $\mathbf{x}_{z_t}$
- Content Stream: use both $z_t$ and $\mathbf{x}_{z_t}$
上面两个流分别使用自己的 Query 向量 $g_{z_t}$ 和 Content 向量 $h_{z_t}$;但是 Key 和 Value 向量都是用的 $h$。但是注意 Query 流不能访问 $z_t$ 的内容,因此 K 和 V 是 $h_{z_{<t}}^{(m-1)}$。而 Content 流的 KV 是 $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-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_\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)
Relative Positional Encoding
在 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_{i,j}^{abs}$ 相比,第一个改动是将 (b) 和 (d) 里的绝对位置编码 $U_j$ 都替换成相对位置编码向量 $R_{i-j}$。注意这里的 $R$ 是之前介绍的正弦函数的编码方式,它是固定的,不需要学习
- 在 (c) 中用可训练的 ${\color {red}{u}} \in R^d$ 替代原来的 $U_i^TW_q^T$。因为我们假设 Attention score 只依赖于 $i$ 和 $j$ 的相对位置,而与 $i$ 的绝对位置无关,所以这里对于所有的 $i$ 都相同。也就是 $U^TW_q^T$,所以可以用一个新的 $\color {red} u$ 来表示。同理,(d) 中的 ${\color {red}{v}}\in R^d$ 也一样
- 最后,我们把 Key 的变换矩阵 $W_k$ 拆分成 $W_{k,E}$ 和 $W_{k,R}$,分别给内容向量和相对位置向量用
在上面的新公式里,每一项的意义都非常清晰:(a) 表示内容的计算,也就是 $x_i$ 的 Embedding 乘以变换矩阵 $W_q$ 和 $x_j$ 的 Embedding 乘以 $W_{k,E}$ 的内积;(b) 表示基于内容的位置偏置,也就是 $i$ 的向量乘以相对位置编码;(c) 表示全局的内容偏置;(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,如果位置 $i$ 和 $j$ 的词属于同一个 segment,那么使用一个可以学习的 Embedding $s_{ij}=s_+$,否则 $s_{ij}=s_-$,也就是说,我们只关心它们是属于同一个 Segment 还是属于不同的 Segment。当我们从位置 $i$ attend to $j$ 的时候,我们会这样计算一个新的 attention score:
$$ a_{ij}=(q_i+b)^Ts_{ij} $$
其中 $q_i$ 是第 $i$ 个位置的 Query 向量,b 是一个可学习的 bias。最后我们会把这个 attention score 加到原来计算的 Attention score 里,这样它就能学到当 $i$ 和 $j$ 都属于某个 segment 的特征,以及 $i$ 和 $j$ 属于不同 segment 的特征
Conclusion
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 那么简单直白,并且给我一种拆东墙补西墙的感觉(引入了 Permutation,所以没有位置信息了,由此又引入了双流注意力)。XLNet 在 Transformer XL 的基础上引入了随机排列和双流注意力机制,因此使得整个模型变得非常复杂
XLNet 训练总共使用了 126GB 纯文本数据,而 BERT 训练只使用了 13GB 的数据。所以虽说最终成绩 XLNet 超过了 BERT,但究竟是因为数据的帮助,还是模型真的很好呢?
博主你好,请教一个困扰我许久的 xlnet 代码细节问题:使用 huggface 的 transformers 库,模型是 xlnet 哈工大 mid 预训练模型,调试的时候发现 xlnet 对数据分词的时候(tokenizer),数据的顺序好像是正常的,但是在 forward 里面走了一层 xlnet 后:outputs = self.xlnet (......) 后,这里返回的 outputs 好像又是被全排列打乱后的文本序列向量,我想问问博主知道 xlnet 在分词后数据顺序是乱的吗 在 forward 里面走了一遍 xlnet 层后是乱的吗 谢谢
xlnet 如你所知确实是 permutation language model,但实际上他不是通过直接打乱输入句子的方式实现的,而是通过对 mask 矩阵进行操作,我记得我这篇文章里好像讲到了
好的 谢谢。 那意思就是 forword 里面,只是在 xlnet 层内部是通过 mask 矩阵来实现打乱的,走完了 xlnet 层后输出是正常的数据顺序 对吧?
是的。(下次没有网站就别写百度的)
哈哈哈 好的 必填项不知道填啥 谢谢哈
我记得有篇论文说过,NSP 任务本质是在做一个分类任务,因为 Bert 构造负样本是在两篇不同的文本里抽句子的,而正样本是在同一篇文本里抽的。因此,模型只要判断两句话是否属于同一篇文本(很大概率这两篇文本的类别也不一样),即做分类任务。分类任务相较于真正的 NSP 任务更简单,学习到的信息也更少,所以 Bert 的 NSP 任务也逐渐被舍弃
也就是说其实所谓的乱序只是个思想,xlnet 实现方式就是两个 attention mask 矩阵,然后从中预测行中红点最多的 top 1/K 是吧?比如上诉例子中,要预测 2 个 token,而第一行和第四行红点最多,因此就预测这俩?
我愿称你为最强 @(吐舌)
Two-Stream Self-Attention 的 “但是上面两点要求对于普通的 Transformer 来说是矛盾的无法满足的。这里非常重要,所以我这里再啰嗦一点举一个例子” 的一段举例,其中公式是不是有错的,直接复制前一个公式没有改下标?这一段里面好几个 g 函数,读起来怪怪的
没事了,我自己的问题。看明白了
博主你好。我不是做 NLP 的,有一个问题关于似然函数公式里,公式 1, exp () 里为什么还要加上 e (x),即 x 的 Embedding 呢。
博主,这个怎么没有图片了呀?