一文详解Google最新NLP模型XLNet
语言模型和BERT各自的优缺点
在论文里作者使用了一些术语,比如自回归(Autoregressive, AR)语言模型和自编码(autoencoding)模型等,这可能让不熟悉的读者感到困惑,因此我们先简单的解释一下。
自回归是时间序列分析或者信号处理领域喜欢用的一个术语,我们这里理解成语言模型就好了:一个句子的生成过程如下:首先根据概率分布生成第一个词,然后根据第一个词生成第二个词,然后根据前两个词生成第三个词,……,直到生成整个句子。
而所谓的自编码器是一种无监督学习输入的特征的方法:我们用一个神经网络把输入(输入通常还会增加一些噪声)变成一个低维的特征,这就是编码部分,然后再用一个 Decoder 尝试把特征恢复成原始的信号。我们可以把 BERT 看成一种 AutoEncoder,它通过 Mask 改变了部分 Token,然后试图通过其上下文的其它 Token 来恢复这些被 Mask 的 Token。如果读者不太理解或者喜欢这两个 jargon,忽略就行了。
给定文本序列
x
=
[
x
1
,
…
,
x
T
]
\mathrm x = [x_1,\dots, x_T]
x=[x1,…,xT],语言模型的目标是调整参数使得训练数据上的似然函数最大:
max
θ
log
p
θ
(
x
)
=
∑
t
=
1
T
log
p
θ
(
x
t
∣
x
<
t
)
=
∑
t
=
1
T
log
exp
(
h
θ
(
x
1
:
t
−
1
)
⊤
e
(
x
t
)
)
∑
x
′
exp
(
h
θ
(
x
1
:
t
−
1
)
⊤
e
(
x
′
)
)
(
1
)
\max_{\theta} \log{p_{\theta}(\mathrm{x})} = \sum_{t=1}^{T}\log{p_{\theta}(x_t|\mathrm{x}<t)} = \sum_{t=1}^{T}\log{\frac{\exp(h_{\theta}(\mathrm{x}_{1:t-1})^{\top} e(x_t))}{\sum_{x'}{\exp(h_{\theta}(\mathrm{x}_{1:t-1})^{\top} e(x'))}}} \qquad (1)
θmaxlogpθ(x)=t=1∑Tlogpθ(xt∣x<t)=t=1∑Tlog∑x′exp(hθ(x1:t−1)⊤e(x′))exp(hθ(x1:t−1)⊤e(xt))(1)
记号
x
<
t
\mathrm x < t
x<t 表示
t
t
t 时刻之前的所有
x
x
x,也就是
x
1
:
t
−
1
\mathrm x1:t-1
x1:t−1。
h
θ
(
x
1
:
t
−
1
)
h\theta(\mathrm x1:t-1)
hθ(x1:t−1)是 RNN 或者 Transformer(注:Transformer 也可以用于语言模型,比如在 OpenAI GPT)编码的
t
t
t 时刻之前的隐状态。
e
(
x
)
e(x)
e(x) 是词
x
x
x 的 embedding。
而 BERT 是去噪(denoising)自编码的方法。对于序列
x
\mathrm x
x,BERT 会随机挑选 15% 的 Token 变成 [MASK] 得到带噪声版本的
x
^
\hat \mathrm x
x^。假设被 Mask 的原始值为
x
ˉ
\bar \mathrm x
xˉ,那么 BERT 希望尽量根据上下文恢复(猜测)出原始值了,也就是:
max
θ
log
p
θ
(
x
ˉ
∣
x
^
)
≈
∑
t
=
1
T
m
t
log
p
θ
(
x
t
∣
x
^
)
=
∑
t
=
1
T
m
t
log
exp
(
H
θ
(
x
^
)
t
⊤
e
(
x
t
)
)
∑
x
′
exp
(
H
θ
(
x
^
)
t
⊤
e
(
x
′
)
)
(
2
)
\max_{\theta} \log{p_{\theta}(\bar{\mathrm{x}} | \hat{\mathrm{x}})} \approx \sum_{t=1}^{T}m_t\log{p_{\theta}(x_t|\hat{\mathrm{x}})} = \sum_{t=1}^{T}m_t\log{\frac{\exp(H_{\theta}(\hat{\mathrm{x}})_{t}^{\top} e(x_t))}{\sum_{x'}{\exp(H_{\theta}(\hat{\mathrm{x}})_{t}^{\top} e(x'))}}} \qquad (2)
θmaxlogpθ(xˉ∣x^)≈t=1∑Tmtlogpθ(xt∣x^)=t=1∑Tmtlog∑x′exp(Hθ(x^)t⊤e(x′))exp(Hθ(x^)t⊤e(xt))(2)
上式中
m
t
=
1
m_t=1
mt=1 表示
x
t
x_t
xt 时刻是一个 Mask,需要恢复。
H
θ
H_\theta
Hθ是一个 Transformer,它把长度为
T
T
T的序列
x
\mathrm x
x映射为隐状态的序列。
注意:前面的语言模型的 RNN 在 t t t 时刻只能看到之前的时刻,因此记号是 h θ ( x 1 : t − 1 ) h\theta(\mathrm x1:t-1) hθ(x1:t−1);而 BERT 的 Transformer(不同与用于语言模型的 Transformer)可以同时看到整个句子的所有 Token,因此记号是 H θ ( x ) H_\theta(\mathrm x) Hθ(x)。
这两个模型的优缺点分别为:
- 独立假设:注意等式 (2) 的约等号 ≈,它的意思是假设在给定 x ^ \hat \boldsymbol{\mathrm x} x^的条件下被 Mask 的词是独立的(没有关系的),这个显然并不成立,比如”New York is a city”,假设我们 Mask 住”New”和”York”两个词,那么给定”is a city”的条件下”New”和”York”并不独立,因为”New York”是一个实体,看到”New”则后面出现”York”的概率要比看到”Old”后面出现”York”概率要大得多。而公式 (1) 没有这样的独立性假设,它是严格的等号。
- 输入噪声:BERT 的在预训练时会出现特殊的 [MASK],但是它在下游的 fine-tuning 中不会出现,这就是出现了不匹配。而语言模型不会有这个问题。
- 双向上下文:语言模型只能参考一个方向的上下文,而 BERT 可以参考双向整个句子的上下文,因此这一点 BERT 更好一些。关于为什么 RNN 只能是单向的上下文而 BERT 可以参考整个句子的上线,读者可以参考 ELMo 和 OpenAI GPT 的问题 [2]。
排列(Permutation)语言模型
根据上面的讨论,语言模型和 BERT 各有优缺点,有什么办法能构建一个模型使得同时有它们的优点并且没有它们缺点呢?
借鉴 NADE(不了解的读者可以忽略,这是一种生成模型)的思路,XLNet 使用了排列语言模型,它同时有它们的优点。
给定长度为
T
T
T 的序列
x
\boldsymbol{\mathrm x}
x,总共有
T
!
T!
T!种排列方法,也就对应
T
!
T!
T!种链式分解方法。比如假设
x
=
x
1
x
2
x
3
\boldsymbol{\mathrm x}=x_1x_2x_3
x=x1x2x3,那么总共用
3
!
=
6
3!=6
3!=6 种分解方法:
p
(
x
)
=
p
(
x
1
)
p
(
x
2
∣
x
1
)
p
(
x
3
∣
x
1
x
2
)
⇒
1
→
2
→
3
p
(
x
)
=
p
(
x
1
)
p
(
x
2
∣
x
1
x
3
)
p
(
x
3
∣
x
1
)
⇒
1
→
3
→
2
p
(
x
)
=
p
(
x
1
∣
x
2
)
p
(
x
2
)
p
(
x
3
∣
x
1
x
2
)
⇒
2
→
1
→
3
p
(
x
)
=
p
(
x
1
∣
x
2
x
3
)
p
(
x
2
)
p
(
x
3
∣
x
2
)
⇒
2
→
3
→
1
p
(
x
)
=
p
(
x
1
∣
x
3
)
p
(
x
2
∣
x
1
x
3
)
p
(
x
3
)
⇒
3
→
1
→
2
p
(
x
)
=
p
(
x
1
∣
x
2
x
3
)
p
(
x
2
∣
x
3
)
p
(
x
3
)
⇒
3
→
2
→
1
\begin{array}{lcl} p(\boldsymbol{\mathrm x}) & = & p(x_1)p(x_2|x_1)p(x_3|x_1x_2) \Rightarrow 1\rightarrow 2\rightarrow 3 \\ p(\boldsymbol{\mathrm x}) & = & p(x_1)p(x_2|x_1x_3)p(x_3|x_1) \Rightarrow 1\rightarrow 3\rightarrow 2 \\ p(\boldsymbol{\mathrm x}) & = & p(x_1|x_2)p(x_2)p(x_3|x_1x_2) \Rightarrow 2\rightarrow 1\rightarrow 3 \\ p(\boldsymbol{\mathrm x}) & = & p(x_1|x_2x_3)p(x_2)p(x_3|x_2) \Rightarrow 2\rightarrow 3\rightarrow 1 \\ p(\boldsymbol{\mathrm x}) & = & p(x_1|x_3)p(x_2|x_1x_3)p(x_3) \Rightarrow 3\rightarrow 1\rightarrow 2 \\ p(\boldsymbol{\mathrm x}) & = & p(x_1|x_2x_3)p(x_2|x_3)p(x_3) \Rightarrow 3\rightarrow 2\rightarrow 1 \end{array}
p(x)p(x)p(x)p(x)p(x)p(x)======p(x1)p(x2∣x1)p(x3∣x1x2)⇒1→2→3p(x1)p(x2∣x1x3)p(x3∣x1)⇒1→3→2p(x1∣x2)p(x2)p(x3∣x1x2)⇒2→1→3p(x1∣x2x3)p(x2)p(x3∣x2)⇒2→3→1p(x1∣x3)p(x2∣x1x3)p(x3)⇒3→1→2p(x1∣x2x3)p(x2∣x3)p(x3)⇒3→2→1
注意
p
(
x
2
∣
x
1
x
3
)
p(x_2|x_1x_3)
p(x2∣x1x3)指的是第一个词是
x
1
x_1
x1并且第三个词是
x
3
x_3
x3的条件下第二个词是
x
2
x_2
x2的概率,也就是说原来词的顺序是保持的。如果理解为第一个词是
x
1
x_1
x1并且第二个词是
x
3
x_3
x3的条件下第三个词是
x
2
x_2
x2,那么就不对了。
如果我们的语言模型遍历 T ! T! T! 种分解方法,并且这个模型的参数是共享的,那么这个模型应该就能(必须)学习到各种上下文。普通的从左到右或者从右往左的语言模型只能学习一种方向的依赖关系,比如先”猜”一个词,然后根据第一个词”猜”第二个词,根据前两个词”猜”第三个词,……。
而排列语言模型会学习各种顺序的猜测方法,比如上面的最后一个式子对应的顺序 3 → 1 → 2 3\rightarrow 1\rightarrow 2 3→1→2,它是先”猜”第三个词,然后根据第三个词猜测第一个词,最后根据第一个和第三个词猜测第二个词。
因此我们可以遍历 T ! T! T!种路径,然后学习语言模型的参数,但是这个计算量非常大(10!=3628800,10 个词的句子就有这么多种组合)。因此实际我们只能随机的采样 T ! T! T!里的部分排列,为了用数学语言描述,我们引入几个记号。 Z T \mathcal{Z}_{T} ZT表示长度为 T T T的序列的所有排列组成的集合,则 z ∈ Z T z\in \mathcal{Z}_{T} z∈ZT是一种排列方法。我们用 z t z_t zt表示排列的第 t t t个元素,而 z < t z<t z<t 表示 z z z的第 1 到第 t − 1 t-1 t−1 个元素。
举个例子,假设 T=3,那么 Z T \mathcal{Z}_{T} ZT共有 6 个元素,我们假设其中之一 ?=[1,3,2],则 z 3 = 2 z_3=2 z3=2,而 z < 3 = [ 1 , 3 ] z<3=[1,3] z<3=[1,3]。
有了上面的记号,则排列语言模型的目标是调整模型参数使得下面的似然概率最大:
max
θ
E
z
∼
Z
t
[
∑
t
=
1
T
log
p
θ
(
x
z
t
∣
x
z
≤
t
)
]
\max_{\theta} \mathbb{E}_{z\sim\mathcal{Z}_t}[\sum_{t=1}^{T}\log p_{\theta}(\mathrm{x}_{\mathrm{z}_{t}}|\mathrm{x}_{\mathrm{z}\le t})]
θmaxEz∼Zt[t=1∑Tlogpθ(xzt∣xz≤t)]
上面的公式看起来有点复杂,细读起来其实很简单:从所有的排列中采样一种,然后根据这个排列来分解联合概率成条件概率的乘积,然后加起来。
注意:上面的模型只会遍历概率的分解顺序,并不会改变原始词的顺序。实现是通过 Attention 的 Mask 来对应不同的分解方法。比如
p
(
x
1
∣
x
3
)
p
(
x
2
∣
x
1
x
3
)
p
(
x
3
)
p(x_1|x_3)p(x_2|x_1x_3)p(x_3)
p(x1∣x3)p(x2∣x1x3)p(x3),我们可以在用 Transformer 编码
x
1
x_1
x1时候让它可以 Attend to
x
3
x_3
x3,而把
x
2
x_2
x2Mask 掉;编码
x
3
x_3
x3 的时候把
x
1
,
x
2
x_1,x_2
x1,x2 都 Mask 掉。
比如图的左上,对应的分解方式是 3 → 2 → 4 → 1 3\rightarrow2\rightarrow4\rightarrow1 3→2→4→1,因此预测 x 3 x_3 x3 是不能 attend to 任何其它词,只能根据之前的隐状态 ??? 来预测。而对于左下, x 3 x_3 x3 可以 attend to 其它 3 个词。
Two-Stream Self-Attention for Target-Aware Representations
没有目标(target)位置信息的问题
上面的思想很简单,但是如果我们使用标准的 Transformer 来实现时会有问题。我们来看一个例子。假设输入的句子是”I like New York”,并且一种排列为
z
=
[
1
,
3
,
4
,
2
]
z=[1, 3, 4, 2]
z=[1,3,4,2],假设我们需要预测
z
3
=
4
z_3=4
z3=4,那么根据公式:
p
θ
(
X
z
3
=
x
∣
x
z
1
z
2
)
=
p
θ
(
X
4
=
x
∣
x
1
x
3
)
=
exp
(
e
(
x
)
T
h
θ
(
x
1
x
3
)
)
∑
x
′
exp
(
e
(
x
′
)
T
h
θ
(
x
1
x
3
)
)
p_{\theta}(\boldsymbol{\mathrm{X}_{z_3}} = x|x_{z_1z_2}) = p_{\theta}(\boldsymbol{\mathrm{X}_{4}} = x|x_1x_3) = \frac{\exp(e(x)^{T}h_{\theta}(x_1x_3))}{\sum_{x'}\exp(e(x')^{T}h_{\theta}(x_1x_3))}
pθ(Xz3=x∣xz1z2)=pθ(X4=x∣x1x3)=∑x′exp(e(x′)Thθ(x1x3))exp(e(x)Thθ(x1x3))
注意,我们通常用大写
X
\boldsymbol{\mathrm{X}}
X的表示随机变量,比如
X
4
\boldsymbol{\mathrm{X}}4
X4,而小写的
x
x
x表示某一个具体取值,比如
x
x
x,我们假设
x
x
x 是”York”,则
p
θ
(
X
4
=
x
)
p_{\theta}(\boldsymbol{\mathrm{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,那么:
p
θ
(
X
z
3
=
x
∣
x
z
1
z
2
)
=
p
θ
(
X
2
=
x
∣
x
1
x
3
)
=
exp
(
e
(
x
)
T
h
θ
(
x
1
x
3
)
)
∑
x
′
exp
(
e
(
x
′
)
T
h
θ
(
x
1
x
3
)
)
p_{\theta}(\boldsymbol{\mathrm{X}_{z_3}} = x|x_{z_1z_2}) = p_{\theta}(\boldsymbol{\mathrm{X}_{2}} = x|x_1x_3) = \frac{\exp(e(x)^{T}h_{\theta}(x_1x_3))}{\sum_{x'}\exp(e(x')^{T}h_{\theta}(x_1x_3))}
pθ(Xz3=x∣xz1z2)=pθ(X2=x∣x1x3)=∑x′exp(e(x′)Thθ(x1x3))exp(e(x)Thθ(x1x3))
则上面是表示是第一个词是 I,第 3 个词是 New 的条件下第 2 个词是 York 的概率。我们仔细对比一下公式会发现这两个概率是相等的。但是根据经验,显然这两个概率是不同的,而且上面的那个概率大一些,因为 York 跟在 New 之后是一个城市,而”York New”是什么呢?
上面的问题的关键是模型并不知道要预测的那个词在原始序列中的位置。了解 Transformer 的读者可能会问:输入的位置编码在哪里呢?位置编码的信息不能起作用吗?
注意:位置编码是和输入的 Embedding 加到一起作为输入的,因此 p θ ( X 4 = x ∣ x 1 x 3 ) p_{\theta}(\boldsymbol{\mathrm{X}_{4}} = x|x_1x_3) pθ(X4=x∣x1x3)里的 x 1 x_1 x1和 x 3 x_3 x3是带了位置信息的,模型(可能)知道(根据输入的向量猜测)I 是第一个词,而 New 是第三个词,但是第四个词的向量显然这个是还不知道(知道了还要就不用预测了),因此就不可能知道它要预测的词到底是哪个位置的词,因此我们必须”显式”的告诉模型我要预测哪个位置的词。
为了后面的描述,我们再把上面的两个公式写出更加一般的形式。给定排列
z
z
z,我们需要计算
p
θ
(
X
z
t
∣
x
z
<
t
=
x
)
p_{\theta}(\boldsymbol{\mathrm{X}_{z_{t}}}|\boldsymbol{\mathrm{x}}_{z<t} = x)
pθ(Xzt∣xz<t=x),如果我们使用普通的 Transformer,那么计算公式为:
p
θ
(
X
z
t
=
x
∣
x
z
<
t
)
=
exp
(
e
(
x
)
T
h
θ
(
x
z
<
t
)
)
∑
x
′
exp
(
e
(
x
′
)
T
h
θ
(
x
z
<
t
)
)
p_{\theta}(\mathrm{X}_{z_{t}} = x | \mathrm{x}_{z<t}) = \frac{\exp(e(x)^{T}h_{\theta}(\mathrm{x}_{\mathrm{z}<t}))}{\sum_{x'}\exp(e(x')^{T}h_{\theta}(\mathrm{x}_{\mathrm{z}<t}))}
pθ(Xzt=x∣xz<t)=∑x′exp(e(x′)Thθ(xz<t))exp(e(x)Thθ(xz<t))
根据前面的讨论,我们知道问题的关键是模型并不知道要预测的到底是哪个位置的词,为了解决这个问题,我们把预测的位置
z
t
z_t
zt放到模型里:
p
θ
(
X
z
t
=
x
∣
x
z
<
t
)
=
exp
(
e
(
x
)
T
g
θ
(
x
z
<
t
,
z
t
)
)
∑
x
′
exp
(
e
(
x
′
)
T
g
θ
(
x
z
<
t
,
z
t
)
)
p_{\theta}(\mathrm{X}_{z_{t}} = x | \mathrm{x}_{z<t}) = \frac{\exp(e(x)^{T}\mathrm g_{\theta}(\mathrm{x}_{\mathrm{z}<t},z_{t}))}{\sum_{x'}\exp(e(x')^{T}\mathrm g_{\theta}(\mathrm{x}_{\mathrm{z}<t},z_{t}))}
pθ(Xzt=x∣xz<t)=∑x′exp(e(x′)Tgθ(xz<t,zt))exp(e(x)Tgθ(xz<t,zt))
上式中
g
θ
(
x
z
<
t
,
z
t
)
\mathrm g_{\theta}(\mathrm{x}_{\mathrm{z}<t},z_{t})
gθ(xz<t,zt)表示这是一个新的模型
g
g
g,并且它的参数除了之前的词
x
z
<
t
\mathrm{x}_{\mathrm{z}<t}
xz<t,还有要预测的词的位置
z
t
z_t
zt。
Two-Stream Self-Attention
接下来的问题是用什么模型来表示 g θ ( x z < t ) g_{\theta}(\boldsymbol{\mathrm{x}_{\mathrm z _{< t}}}) gθ(xz<t)。当然有很多种可选的函数(模型),我们这里通过位置 z t z_t zt 来从 c o n t e x t x z < t context \quad \boldsymbol{\mathrm{x}_{\mathrm z _{< t}}} contextxz<t 里通过 Attention 机制提取需要的信息来预测这个位置的词。那么它需要满足如下两点要求:
-
为了预测 x z < t \boldsymbol{\mathrm{x}_{\mathrm z _{< t}}} xz<t, g θ ( x z < t , z t ) g_{\theta}(\boldsymbol{\mathrm{x}_{\mathrm z _{< t}}},z_t) gθ(xz<t,zt)只能使用位置信息 z t z_t zt而不能使用 x z < t \boldsymbol{\mathrm{x}_{\mathrm z _{< t}}} xz<t。这是显然的:你预测一个词当然不能知道要预测的是什么词。
-
为了预测 z t z_t zt 之后的词, g θ ( x z < t , z t ) g_{\theta}(\boldsymbol{\mathrm{x}_{\mathrm z _{< t}}},z_t) gθ(xz<t,zt)必须编码了 x z < t \boldsymbol{\mathrm{x}_{\mathrm z _{< t}}} xz<t的信息(语义)。
但是上面两点要求对于普通的 Transformer 来说是矛盾的无法满足的。因为上面是理解为什么要搞出两个 Stream 的关键,所以我这里再啰嗦一点举一个例子。
假设输入的句子还是”I like New York”,并且一种排列为 z = [ 1 , 3 , 4 , 2 ] z=[1, 3, 4, 2] z=[1,3,4,2],假设 t = 2 t=2 t=2,我们现在是在计算 g θ ( x z < t , z t ) g_{\theta}(\boldsymbol{\mathrm{x}_{\mathrm z _{< t}}},z_t) gθ(xz<t,zt),也就是给定第一个位置的词为 I 预测第三个位置为 New 的概率,显然我们不能使用 New 本身的而只能根据第一个位置的 I 来预测。
假设我们非常幸运的找到了一很好的函数 g g g,它可以能够比较好的预测这个概率 g θ ( x 1 , z 2 ) \mathrm g\theta(x_1,z_2) gθ(x1,z2)。现在我们又需要计算 t = 3 t=3 t=3,也就是根据 g θ ( x 1 , z 2 ) \mathrm g\theta(x_1,z_2) gθ(x1,z2)和 z t z_t zt 来预测 York,显然知道第三个位置是 New 对于预测第四个位置是 York 会非常有帮助,但是 g θ ( x 1 , z 2 ) \mathrm g\theta(x_1,z_2) gθ(x1,z2)并没有 New 这个词的信息。
读者可能会问:你不是说 g g g 可以比较好的根据第一个词 I 预测第三个词 New 的概率吗?这里有两点:I 后面出现 New 的概率并不高;在预测 York 时我们是知道第三个位置是 New 的,只不过模型的限制我们没有重复利用这信息。
为了解决这个问题,论文引入了两个 Stream,也就是两个隐状态:
-
内容隐状态 h θ ( x z < t ) h_{\theta}(\mathrm{x}_{\mathrm{z}<t}) hθ(xz<t),简写为 h z t h_{z_{t}} hzt,它就会标准的 Transformer 一样,既编码上下文(context)也编码 x z t \mathrm x_{z_{t}} xzt的内容。
-
查询隐状态 g θ ( x z < t , z t ) \mathrm g_{\theta}(\mathrm{x}_{\mathrm{z}<t},z_{t}) gθ(xz<t,zt),简写为 g z t \mathrm g_{z_{t}} gzt,它只编码上下文和要预测的位置 z t z_t zt,但是不包含 x z t \mathrm x_{z_{t}} xzt。
下面我们介绍一下计算过程。我们首先把查询隐状态 g i ( 0 ) \mathrm g_{i}^{(0)} gi(0)初始化为一个变量 w w w,把内容隐状态 h i ( 0 ) h_{i}^{(0)} hi(0)初始化为词的 Embedding e ( x i ) e(x_i) e(xi)。这里的上标 0 表示第 0 层(不存在的层,用于计算第一层)。因为内容隐状态可以编码当前词,因此初始化为词的 Embedding 是比较合适的。
接着从
m
=
1
m=1
m=1 一直到第 M 层,我们逐层计算:
g
z
t
(
m
)
←
A
t
t
e
n
t
i
o
n
(
Q
=
g
z
t
(
m
−
1
)
,
K
V
=
h
z
<
t
(
m
−
1
)
;
θ
)
,
(
q
u
e
r
y
s
t
r
e
a
m
:
u
s
e
z
t
b
u
t
c
a
n
n
o
t
s
e
e
x
z
t
)
g_{z_{t}}^{(m)} \leftarrow Attention(\boldsymbol{Q} = g_{z_{t}}^{(m-1)},\boldsymbol{KV}=h_{z<t}^{(m-1)};\theta),(query\quad stream: use \quad z_t\quad but \quad cannot \quad see \quad x_{z_{t}})
gzt(m)←Attention(Q=gzt(m−1),KV=hz<t(m−1);θ),(querystream:useztbutcannotseexzt)
h
z
t
(
m
)
←
A
t
t
e
n
t
i
o
n
(
Q
=
h
z
t
(
m
−
1
)
,
K
V
=
h
z
≤
t
(
m
−
1
)
;
θ
)
,
(
c
o
n
t
e
n
t
s
t
r
e
a
m
:
u
s
e
b
o
t
h
z
t
a
n
d
x
z
t
)
h_{z_{t}}^{(m)} \leftarrow Attention(\boldsymbol{Q} = h_{z_{t}}^{(m-1)},\boldsymbol{KV}=h_{z\le t}^{(m-1)};\theta),(content\quad stream: use\quad both \quad z_t \quad and \quad x_{z_{t}})
hzt(m)←Attention(Q=hzt(m−1),KV=hz≤t(m−1);θ),(contentstream:usebothztandxzt)
上面两个流分别使用自己的 Query 向量
g
z
t
\mathrm g_{z_{t}}
gzt和
h
z
t
h_{z_{t}}
hzt;但是 Key 和 Value 向量都是用的
h
h
h,因为
h
h
h 是内容。但是注意 Query 流不能访问
z
t
z_t
zt 的内容,因此 KV 是
h
z
<
t
(
m
−
1
)
h_{z<t}^{(m-1)}
hz<t(m−1),这里用的是小于号 (
<
<
<) 表示不包括
t
t
t 时刻的 content。而 Content 流的 KV 是
h
z
≤
t
(
m
−
1
)
h_{z\le t}^{(m-1)}
hz≤t(m−1),它包含
x
z
t
\mathrm x_{z_{t}}
xzt。
上面的梯度更新和标准的 self-attention 是一样的。在 fine-tuning 的时候,我们可以丢弃掉 Query 流而只用 Content 流。最后在计算公式的时候我们可以用最上面一层的 Query 向量 g z t ( M ) g_{z_{t}}^{(M)} gzt(M)。
下面我们通过下图来直观的了解计算过程。
图的左上是 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 1 ( 0 ) , h 2 ( 0 ) , h 3 ( 0 ) ] KV=[h_{1}^{(0)},h_{2}^{(0)},h_{3}^{(0)}] KV=[h1(0),h2(0),h3(0)],而 Q = g 1 ( 0 ) Q=\mathrm 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 ) \mathrm g(1) g(1),然后计算第二层……。注意最右边的 Content Mask 和 Query Mask,我们先看 Content Mask。它的第一行全是红点,表示第一个词可以 attend to 所有的词(根据 3 → 2 → 4 → 1 3\rightarrow2\rightarrow4\rightarrow1 3→2→4→1),第二个词可以 attend to 它自己和第三个词,……。而 Query Mask 和 Content Mask 的区别就是不能 attend to 自己,因此对角线都是白点。
部分预测
虽然排列语言模型有很多有点,但是它的计算量很大(排列很多),很难优化。因此我们只预测一个句子后面的一些词,为什么不预测前面的词呢?因为前面的词的上下文比较少,上下文信息相对较少。比如句子”I like New York”。预测 I 的时候没有任何上下文,因此可能的选择很多。而到最后一个词 York 的时候,如果 New 已经知道了,那么 York 的概率就非常大了。
因此我们把一个排列
z
z
z 分成两个子序列
z
≤
c
z\le c
z≤c 和
z
>
c
z>c
z>c,分别叫做 non-target 序列和 target 序列,其中
c
c
c是切分点。我们会使用一个超参数
K
K
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 流,从而可以节省计算时间。
融入Transformer-XL的优点
到此为止,XLNet 的核心思想已经比较清楚了:还是使用语言模型,但是为了解决双向上下文的问题,引入了排列语言模型。排列语言模型在预测时需要 target 的位置信息,因此通过引入 Two-Stream,Content 流编码到当前时刻的所有内容,而 Query 流只能参考之前的历史以及当前要预测的位置。最后为了解决计算量过大的问题,对于一个句子,我们只预测后面的 1/K 的词。
接下来 XLNet 借鉴了 Transformer-XL 的优点,它对于很长的上下文的处理是要优于传统的 Transformer 的。我们这里只是简单的介绍 Transformer-XL,有兴趣的读者可以参考 Transformer-XL 论文 [3]。
Transformer-XL思想简介
首先 Transformer-XL 是一个语言模型,也就是改进 Transformer 来根据历史的词预测下一个词。它不同于 BERT 的 Mask 语言模型问题,也不同于 XLNet 使用的排列语言模型。我们知道 OpenAI GPT 就是使用 Transformer 来进行语言模型的建模。因为 Transformer 要求输入是定长的词序列(不像 RNN 可以处理变长的输入序列),太长的截断,不足的 padding,这样我们把一个语料库的字符串序列切分成固定长度的 segments。它有下面一些问题:
- 由于定长的要求,我们不可能让输入太长。因此虽然 Self-Attention 机制不太受长度的约束,但是 Transformer 的语言模型实际能够考虑的上下文就是输入的长度。
- 因为我们在序列语言模型的时候通常很难准确的分句(或者有时候一个句子比最大长度还长),所以一个 Segment 很可能不是一个完整的句子(甚至它是从某个句子的中间部分开始的),这样前面的几个词就很难预测(给人一个没头没脑的句子也很难预测),因为语言模型是自回归的,一步错步步错。这就是所谓的 context fragmentation 的问题。
- 预测的性能问题,假设我们要使用 Transformer 语言模型来计算一个句子的概率(而不是用于下游的任务),那么我们首先要计算 P ( x 1 ) P(x_1) P(x1),然后计算 P ( x 2 ∣ x 1 ) P(x_2|x_1) P(x2∣x1),……,一直计算到 P ( x T ∣ x 1 , x T − 1 ) P(x_T|x_1,x_{T-1}) P(xT∣x1,xT−1)。每个时刻都需要用 Transformer 计算一次,而不能像 RNN 那样之前的把历史都编码到一个 context 向量里。
上图做是普通的 Transformer 语言模型的训练过程。假设 Segment 的长度为 4,如图中我标示的:根据红色的路径,虽然 x 8 x_8 x8 的最上层是受 x 1 x_1 x1 影响的,但是由于固定的 segment, x 8 x_8 x8无法利用 x 1 x_1 x1 的信息。
而预测的时候的上下文也是固定的 4,比如预测 x 6 x_6 x6 时我们需要根据 [ x 2 , x 3 , x 4 , x 5 ] [x_2,x_3,x_4,x_5] [x2,x3,x4,x5] 来计算,接着把预测的结果作为下一个时刻的输入。接着预测 x 7 x_7 x7 的时候需要根据 [ x 3 , x 4 , x 5 , x 6 ] [x_3,x_4,x_5,x_6] [x3,x4,x5,x6] 完全进行重新的计算。之前的计算结果一点也用不上。
而 Transformer-XL 如下图所示:
我们会把之前一个固定长度的词序列每一层的输出都放到一个 cache 里,比如把 x 1 , … , x 4 x_1,\dots,x_4 x1,…,x4 的计算结果都存起来,那么在训练第二个 S e g m e n t [ x 5 , … , x 8 ] Segment[x_5,\dots,x_8] Segment[x5,…,x8] 的时候就可以让 Self-Attention 机制参考 [ x 1 , … , x 4 ] [x_1,\dots,x_4] [x1,…,x4] 的信息了。当然在反向计算梯度的时候,cache 里的内容是不会参与梯度的计算的。
而在预测的时候,比如右图我们在计算 x 12 x_{12} x12 作为输入的时候,之前那些 [ x 11 , x 10 , … ] [x_{11},x_{10},\dots] [x11,x10,…] 都不需要重新计算。而普通的 Transformer 是需要的,为什么呢?
我们仔细看一下上图,在 t = 12 t=12 t=12 的时候, x 11 x_{11} x11 可以 attend to [ x 11 , … , x 9 ] [x_{11},\dots,x_{9}] [x11,…,x9] (而 x 8 x_8 x8 被截掉了),而在 t = 11 t=11 t=11 的时候可以 attend to [ x 1 1 , … , x 8 ] [x_11,\dots,x_{8}] [x11,…,x8],因此这两个计算结果是不同的,需要重新计算。
Segment基本的状态重用
根据之前的思路,我们用 cache 缓存部分历史的状态。虽然计算梯度的时候只使用本 segment 的信息,但是在 forward 的时候其实是用到了之前的 segment(甚至很久以前的 segment,只要 cache 的空间足够大)的信息,因此它又有点类似于 RNN。
下面我们形式化的用数学语言来描述状态重用的过程。假设两个相邻的 segment 为
s
τ
=
[
x
τ
,
1
,
x
τ
,
2
,
…
,
x
τ
,
L
]
s_{\tau} = [x_{\tau, 1},x_{\tau, 2},\dots,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},\dots,x_{\tau+1, L}]
sτ+1=[xτ+1,1,xτ+1,2,…,xτ+1,L]。
假设 segment
s
τ
s_{\tau}
sτ 的第
n
n
n 层的隐状态序列为
h
τ
n
∈
R
L
×
d
h_{\tau}^{n}\in R^{L\times d}
hτn∈RL×d,那么计算 segment
s
τ
+
1
s_{\tau+1}
sτ+1的隐状态的过程如下:
h
~
τ
+
1
n
−
1
=
[
S
G
(
h
τ
n
−
1
)
∘
h
τ
+
1
n
−
1
]
q
τ
+
1
n
,
k
τ
+
1
n
,
v
τ
+
1
n
=
h
τ
+
1
n
−
1
W
q
T
,
h
~
τ
+
1
n
−
1
W
k
T
,
h
~
τ
+
1
n
−
1
W
v
T
h
τ
+
1
n
=
T
r
a
n
s
f
o
r
m
e
r
−
L
a
y
e
r
(
q
τ
+
1
n
,
k
τ
+
1
n
,
v
τ
+
1
n
)
\begin{array}{lcl} \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} & = & Transformer-Layer(q_{\tau + 1}^{n},k_{\tau + 1}^{n}, v_{\tau + 1}^{n}) \end{array}
h~τ+1n−1qτ+1n,kτ+1n,vτ+1nhτ+1n===[SG(hτn−1)∘hτ+1n−1]hτ+1n−1WqT,h~τ+1n−1WkT,h~τ+1n−1WvTTransformer−Layer(qτ+1n,kτ+1n,vτ+1n)
上式中
S
G
(
h
τ
n
−
1
)
SG(h_{\tau}^{n-1})
SG(hτn−1)函数代表
h
τ
n
−
1
h_{\tau}^{n-1}
hτn−1不参与梯度的计算。我们看到,计算 Query 的时候只是用本 segment 的信息
h
τ
+
1
n
−
1
h_{\tau + 1}^{n-1}
hτ+1n−1,而计算 Key 和 Value 的时候同时使用了
h
τ
+
1
n
−
1
h_{\tau + 1}^{n-1}
hτ+1n−1和
h
τ
n
−
1
h_{\tau}^{n-1}
hτn−1(实际用的是
h
~
τ
+
1
n
−
1
\tilde h_{\tau + 1}^{n-1}
h~τ+1n−1)。
Transformer-XL的相对位置编码
Transformer-XL 不能像 BERT 那样使用绝对位置编码,下面我们来分析一些为什么不行。
和前面一样,假设两个相邻的segment为 s τ = [ x τ , 1 , x τ , 2 , … , x τ , L ] s_{\tau} = [x_{\tau, 1},x_{\tau, 2},\dots,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},\dots,x_{\tau+1, L}] sτ+1=[xτ+1,1,xτ+1,2,…,xτ+1,L]。
假设 segment
s
τ
s_{\tau}
sτ 的第
n
n
n 层的隐状态序列为
h
τ
n
∈
R
L
×
d
h_{\tau}^{n}\in R^{L\times d}
hτn∈RL×d,那么计算公式如下:
h
τ
+
1
=
f
(
h
τ
,
E
s
τ
+
1
+
U
1
:
L
)
h
τ
=
f
(
h
τ
−
1
,
E
s
τ
+
U
1
:
L
)
\begin{array}{lcl} h_{\tau + 1} & = & f(h_{\tau}, E_{s_{\tau + 1}} + U_{1:L}) \\ h_{\tau} & = & f(h_{\tau - 1}, E_{s_{\tau}} + U_{1:L}) \end{array}
hτ+1hτ==f(hτ,Esτ+1+U1:L)f(hτ−1,Esτ+U1:L)
上式中
E
s
τ
E_{s_{\tau}}
Esτ是 segment 的每一个词的 Embedding 的序列。我们发现
E
s
τ
E_{s_{\tau}}
Esτ 和
E
s
τ
+
1
E_{s_{\tau+1}}
Esτ+1 都是加了
U
1
:
L
U_{1:L}
U1:L,因此模型无法通过向量判断它到底是当前 segment 的第
i
i
i 个位置还是前一个 Segment 的第
i
i
i 个位置。
注:不熟悉位置编码的读者需要参考 Transformer 图解 [4]。它的基本思想就是给每一个绝对位置一个Embedding,因此模型可以通过这个 Embedding 猜测它在编码哪个位置的信息,也可以学到某个位置用什么向量来表示更好。
因此 Transformer-XL 必须使用相对位置编码,它使用了和原始的 Transformer 使用正弦函数不同的方法。原始的 Transformer 是把位置信息 embedding 进去直接加到输入里,而 Transformer-XL 是在 Attention 计算的时候利用当前 Query 和 Key 的相对位置。因为 XLNet 使用的是正弦函数的位置编码,所以这里就不介绍 Transformer-XL 的位置编码方法了,感兴趣的读者可以参考 Transformer-XL 论文 [3]。
在XLNet里融入Transformer-XL的思想
首先 XLNet 借鉴了 Transoformer-XL 的相对位置编码的思想,这个和它基本一样,因此这里不再介绍。另外一点就是 cache 前一个 segment 的隐状态。
我们假设有两个从原始序列
s
\boldsymbol s
s里抽取的两个连续 Segment,
x
~
=
s
1
:
T
\tilde x = s_{1:T}
x~=s1:T和
x
=
s
T
+
1
:
2
T
x = s_{T+1:2T}
x=sT+1:2T。同时假设
z
~
\tilde z
z~和
z
z
z 分别是
[
1
,
…
,
T
]
[1,\dots, T]
[1,…,T] 和
[
T
+
1
,
…
,
2
T
]
[T+1,\dots,2T]
[T+1,…,2T] 的一个排列。然后根据排列
z
~
\tilde z
z~的概率分解我们首先计算第一个 segment,并且把 Content 流的隐状态
h
~
(
m
)
\tilde h ^{(m)}
h~(m) cache 下来,这里
h
~
(
m
)
\tilde h ^{(m)}
h~(m)是第
m
m
m 层的 Content 流的隐状态。那么计算第二个 Segment 的 Content 流的方法如下:
h
z
t
(
m
)
←
A
t
t
e
n
t
i
o
n
(
Q
=
h
z
t
(
m
−
1
)
,
K
V
=
[
h
~
m
−
1
,
h
z
≤
t
(
m
−
1
)
]
;
θ
)
h_{z_{t}}^{(m)} \leftarrow Attention(\boldsymbol{Q} = h_{z_{t}}^{(m-1)},\boldsymbol{KV}=[\tilde h^{m-1}, h_{z\le t}^{(m-1)}];\theta)
hzt(m)←Attention(Q=hzt(m−1),KV=[h~m−1,hz≤t(m−1)];θ)
上式用自然语言描述就是:为了计算
z
t
z_t
zt 第
m
m
m 层的隐状态,我们使用 Attention 机制,其中 Query 是上一次的隐状态
h
z
t
(
m
−
1
)
h_{z_{t}}^{(m-1)}
hzt(m−1),而 Key 和 Value 除了
z
1
,
…
,
z
t
z_1,\dots, z_t
z1,…,zt第
m
−
1
m-1
m−1 层的隐状态,也需要 attend to cached 上一个 segment 的所有第
m
−
1
m-1
m−1 层的隐状态。
在计算第二个 segment 时,我们只需要知道隐状态 h ~ ( m ) \tilde h^{(m)} h~(m)就可以了,而并不需要知道它是通过哪个排列 z ~ \tilde z z~计算出来的。这样我们在 cache 前一个 segment 时不用考虑它的排列。
建模多个segment
许多下游的任务会有多于一个输入序列,比如问答的输入是问题和包含答案的段落。下面我们讨论怎么在自回归框架下怎么预训练两个 segment。和 BERT 一样,我们选择两个句子,它们有 50% 的概率是连续的句子(前后语义相关),有 50% 的概率是不连续(无关)的句子。
我们把这两个句子拼接后当成一个句子来学习排列语言模型。输入和 BERT 是类似的:[A, SEP, B, SEP, CLS],这里 SEP 和 CLS 是特殊的两个 Token,而 A 和 B 代表两个 Segment。而 BERT 稍微不同,这里把 CLS 放到了最后。
原因是因为对于 BERT 来说,Self-Attention 唯一能够感知位置是因为我们把位置信息编码到输入向量了,Self-Attention 的计算本身不考虑位置信息。而前面我们讨论过,为了减少计算量,这里的排列语言模型通常只预测最后 1 / K 1/K 1/K 个 Token。我们希望 CLS 编码所有两个 Segment 的语义,因此希望它是被预测的对象,因此放到最后肯定是会被预测的。
但是和 BERT 不同,我们并没有增加一个预测下一个句子的 Task,原因是通过实验分析这个 Task 加进去后并不是总有帮助。
注:其实很多做法都是某些作者的经验,后面很多作者一看某个模型好,那么所有的 Follow,其实也不见得就一定好。有的时候可能只是对某个数据集有效果,或者效果好是其它因素带来的,一篇文章修改了 5 个因素,其实可能只是某一两个因素是真正带来提高的地方,其它 3 个因素可能并不有用甚至还是有少量副作用。
相对Segment编码
BERT 使用的是绝对的 Segment 编码,也就是第一个句子对应的 Segment id 是 0,而第二个句子是 1。这样如果把两个句子换一下顺序,那么输出是不一样的。
XLNet 使用的是相对的 Segment 编码,它是在计算 Attention 的时候判断两个词是否属于同一个 Segment,如果位置 i i i 和 j j j 的词属于同一个 segment,那么使用一个可以学习的 Embedding s i j = s + s_{ij}=s_{+} sij=s+,否则 s i j = s 1 s_{ij}=s_{1} sij=s1。也就是说,我们只关心它们是属于同一个 Segment 还是属于不同的 Segment 的。
当我们从位置 i i i attend to j j j 的时候,我们会这样计算一个新的 attention score: a i j = ( q i + b ) T s i j a_{ij} = (q_i+b)^{T}s_{ij} aij=(qi+b)Tsij。这里的 q i q_i qi是第 i i i 位置的 Query 向量, b b b 是一个可以学习的 bias。最后我们会把这个 attention score 加到原来计算的 Attention score 里,这样它就能学到当 i i i 和 j j j 都属于某个 segment 的特征和i和就属于不同 segment 的特征。
XLNet与BERT的对比
XLNet 和 BERT 都是预测一个句子的部分词,但是背后的原因是不同的。BERT 使用的是 Mask 语言模型,因此只能预测部分词(总不能把所有词都 Mask 了然后预测?)。而 XLNet 预测部分词是出于性能考虑,而 BERT 是随机的选择一些词来预测。
除此之外,它们最大的区别其实就是 BERT 是约等号,也就是条件独立的假设——那些被 MASK 的词在给定非 MASK 的词的条件下是独立的。但是我们前面分析过,这个假设并不(总是)成立。下面我们通过一个例子来说明(其实前面已经说过了,理解的读者跳过本节即可)。
假设输入是 [New, York, is, a, city],并且假设恰巧 XLNet 和 BERT 都选择使用 [is, a, city] 来预测 New 和 York。同时我们假设 XLNet 的排列顺序为 [is, a, city, New, York]。那么它们优化的目标函数分别为:
J
B
E
R
T
=
log
p
(
N
e
w
∣
i
s
a
c
i
t
y
)
+
log
p
(
Y
o
r
k
∣
i
s
a
c
i
t
y
)
J
X
L
N
e
t
=
log
p
(
N
e
w
∣
i
s
a
c
i
t
y
)
+
log
p
(
Y
o
r
k
∣
N
e
w
,
i
s
a
c
i
t
y
)
\begin{array}{lcl} J_{BERT} & = & \log p(New|is\quad a \quad city) + \log p(York|is\quad a \quad city) \\ J_{XLNet} & = & \log p(New|is\quad a \quad city) + \log p(York|New, is\quad a \quad city) \end{array}
JBERTJXLNet==logp(New∣isacity)+logp(York∣isacity)logp(New∣isacity)+logp(York∣New,isacity)
从上面可以发现,XLNet 可以在预测 York 的时候利用 New 的信息,因此它能学到”New York”经常出现在一起而且它们出现在一起的语义和单独出现是完全不同的。
XLNet与语言模型的对比
和语言模型相比,XLNet 最大的优势就是通过输入序列的各种排列,同时学习到上下文的信息。
实验
Pretraining和实现
和 BERT 一样,XLNet 使用了 BooksCorpus 和英文的维基百科作为训练数据,这两者总共 13GB 的文本。此外,XLNet 还增加了 Giga5(16GB)、ClueWeb 2012-B 和 Common Crawl 的数据来进行 Pretraining。
对于 ClueWeb 2012-B 和 Common Crawl 的内容使用了启发式的规则进行了预处理,最终各自保留了 19GB 和 78GB 的文本。使用 SentencePiece 工具 [5] 后分别得到 2.78B, 1.09B, 4.75B, 4.30B 和 19.97B Token (subword unit),总计 32.89B。
最大的模型 XLNet-Large 采样了和 BERT-large 一样的超参数,从而得到类似大小的模型。序列长度和 cache 分别设置为 512 和 384。训练 XLNet-Large 是在 512 核心(不是 512 个)的 TPU v3 芯片上进行,使用 Adam 优化器迭代了 500K 次。使用了线性的 Learning rate decay,batch 大小是 2048,最终训练了 2.5 天。
我们发现模型仍然是欠拟合(underfitting)的,如果继续训练的话在训练数据上的 loss 还能下降。但是对于下游的任务并没有太大帮助。因此我们判断是因为数据太大了,默认没有能力完全拟合数据。为了与 BERT 对比,我们也训练了 XLNet-Base 模型,它只使用了 BooksCorpus 和维基百科的数据。
因为引入了递归(recurrence)的机制,我们使用双向的输入 pipeline,也就是把一个 batch 的一半样本正常顺序输入而另一半反向输入。对于 XLNet-Large,我们设置 K 为 6,也就是预测 1/6 的 Token。Fine-tuning 过程基本是 follow BERT。
此外我们采样了 span-based 预测,也就是我们首先采样一个长度 L, L ∈ [ 1 , … , 5 ] L\in[1,\dots,5] L∈[1,…,5],也就是最少一个 Token (1-gram),最多连续 5 个 Token (5-gram)。然后使用长度为 K L KL KL 的上下文来预测这个 n-gram。后面的代码分析我们会看到这一点。
RACE数据集
RACE 数据集 [6],它包含大概 100K 个问题。它是中国 12 岁到 18 岁的学生在初中和高中的英语阅读理解问题。下图是其中一个样例,我们在中学经常做的阅读理解题。
下图是实验结果,我们可以看到 XLNet 比最好的 BERT 模型要提升很多。
SQuAD [7] 是一个大规模的阅读理解任务的数据集。和前面的选择题不同,SQuAD 可以看成问答题,也就是需要从阅读的文章找答案。如下图所示:
因为 v1.1 的问题是包含在 v2.0 里的,因此在打榜的时候我们直接使用 v2.0 的模型去做 v1.1 的题目,只是把判断有没有答案的部分去掉(因为 v1.1 肯定有答案)。另外因为很多参赛者会使用额外的数据,我们也增加了 NewsQA 的数据作为训练数据。如下表所示,我们的单一的模型(很多好成绩都是好的模型的 Ensembling)取得了 SOTA 的成绩。
我们在 IMDB,Yelp-2,Yelp-5,DBpedia,AG,Amazon-2 和 Amazon-5 等文本分类数据集上做了对比实验,结果如下:
GLUE 数据集上的实验如下:
ClueWeb09-B
ClueWeb09-B 是一个文档排序的数据集。它主要用于搜索引擎:给定一个 Query,然后给相关的网页排序。下面是实验结果:
因为 XLNet 引入了很多改进点,包括实验排列语言模型,使用 Transformer-XL 里的改进,而且还使用了不同的 Pretraining 数据,为了发现哪些改进是有效的,下面做了 Ablation 实验,对比的主要是 BERT-base 和 XLNet-base,因为它们的训练数据都是 BooksCorpus 和 Wiki。
从上面的对比实验发现:加上预测下一个句子这个 Multi-Task 任务在 XLNet 里并无作用。而去掉 memory、span-based 的预测和双向的数据时效果都是有所下降的,因此它们都是有用的。
相关链接
[1] https://fancyerii.github.io/2019/03/05/bert-prerequisites/
[2] https://fancyerii.github.io/2019/03/09/bert-theory/#elmo和openai-gpt的问题
[3] https://arxiv.org/abs/1901.02860
[4] https://fancyerii.github.io/2019/03/09/transformer-illustrated/#位置编码positional-encoding
[5] https://github.com/google/sentencepiece
[6] https://www.cs.cmu.edu/~glai1/data/race/
[7] https://rajpurkar.github.io/SQuAD-explorer/