词表示(Word Representation)的发展历程
众所周知,NLP任务中如何把文本转换成计算机可识别信息输入是一件非常重要的事情,本文就自己现阶段的了解,就词表示相关(Word-Representation)相关的知识自己做了一个总结,文中结论基于笔者现阶段认知,如有不妥之处尽请留言指正!
文章目录
一、One-hot编码和词袋模型
1.什么是One-Hot编码?
One-Hot编码是分类变量作为二进制向量的表示。这首先要求将分类值映射到整数值。然后,每个整数值被表示为二进制向量,除了整数的索引之外,它都是零值,它被标记为1。
2.举例
为了更直观的表述One-hot编码在NLP中的应用,我们拿英文字典做例子(牛津英文词典号称301000个主词汇),对应的单词和序号只是举例子,并非真实的序号:
单词 | 序号 | one-hot表示 |
---|---|---|
A | 1 | [1,0,0…,0,0] |
About | 2 | [0,1,0…,0,0] |
An | 3 | [0,0,1…,0,0] |
… | … | … |
Zero | 300999 | [0,0,0…,1,0] |
Zoo | 301000 | [0,0,0…,0,1] |
如果用one-hot编码作为此表示的话,整个需要占用的空间为301000*301000维的矩阵,但是这个矩阵很稀疏,只有主对角元素有值,其他位置都是0。
3、词袋模型
词袋模型的表示方法其实和上面的one-hot表示方法很像,唯一不同的就是在主对角元素位置上,词袋模型不一定是1,而是该单词在文档中出现的频数(TF)。
二、Tf-idf
上面one-hot的表示方法有明显的缺陷,因为它只表明词的位置,其他信息一概不管。词袋模型只考虑了每个单词在文档中出现的频数,而tf-idf引入了一些统计信息,在一定程度上丰富了词袋模型的表意。
在一份给定的文件里,词频 (term frequency, TF) 指的是某一个给定的词语在该文件中出现的次数。这个数字通常会被归一化(分子一般小于分母 区别于IDF),以防止它偏向长的文件。
对于在某一特定文件里的词语 来说,它的重要性可表示为:
T
F
i
j
=
n
i
j
∑
k
n
k
,
j
TF_{ij}=\frac{n_{ij}}{\sum_{k}n_{k,j}}
TFij=∑knk,jnij
以上式子中
n
i
j
n_{ij}
nij 是该词在文件
d
j
d_j
dj中的出现次数,而分母则是在文件
d
j
d_j
dj中所有字词的出现次数之和。
逆向文件频率 (inverse document frequency, IDF) 是一个词语普遍重要性的度量。某一特定词语的IDF,可以由总文件数目除以包含该词语之文件的数目,再将得到的商取对数得到。
I
D
F
i
=
l
o
g
∣
D
∣
{
j
:
t
i
∈
d
j
}
IDF_i=log\frac{|D|}{\{j:t_i\in d_j\}}
IDFi=log{j:ti∈dj}∣D∣
其中
|D|:语料库中的文件总数
{
j
:
t
i
∈
d
j
}
\{j:t_i\in d_j\}
{j:ti∈dj}:包含词语
t
i
t_i
ti的文件数目(即
n
i
j
≠
0
n_{ij }\not=0
nij=0的文件数目)。
如果该词语不在语料库中,就会导致被除数为零,因此一般情况下使用
1
+
{
j
:
t
i
∈
d
j
}
1+\{j:t_i\in d_j\}
1+{j:ti∈dj}
T
F
−
I
D
F
i
=
T
F
i
∗
I
D
F
i
TF-IDF_i=TF_i*IDF_i
TF−IDFi=TFi∗IDFi
因此,某一特定文件内的高词语频率,以及该词语在整个文件集合中的低文件频率,可以产生出高权重的TF-IDF。TF-IDF倾向于过滤掉常见的词语,保留重要的词语。
三、Word2vec
One hot representation用来表示词向量非常简单,但是却有很多问题。最大的问题是我们的词汇表一般都非常大,比如达到百万级别,这样每个词都用百万维的向量来表示简直是内存的灾难。这样的向量其实除了一个位置是1,其余的位置全部都是0,表达的效率不高,能不能把词向量的维度变小呢?
Distributed representation可以解决One hot representation的问题,它的思路是通过训练,将每个词都映射到一个较短的词向量上来。所有的这些词向量就构成了向量空间,进而可以用普通的统计学的方法来研究词与词之间的关系。这个较短的词向量维度是多大呢?这个一般需要我们在训练时自己来指定。
我们用一个例子简单介绍一下,one-hot和word2vec的到的词向量表示:
比如下图我们将词汇表里的词用"Royalty",“Masculinity”, “Femininity"和"Age"4个维度来表示,King这个词对应的词向量可能是(0.99,0.99,0.05,0.7)。当然在实际情况中,我们并不能对词向量的每个维度做一个很好的解释。
有了用Distributed Representation表示的较短的词向量,我们就可以较容易的分析词之间的关系了,比如我们将词的维度降维到2维,有一个有趣的研究表明,用下图的词向量表示我们的词时,我们可以发现:
K
i
n
g
→
−
M
a
n
→
+
W
o
m
a
n
→
=
Q
u
e
n
e
→
\overset {\rightarrow}{King} −\overset {\rightarrow}{Man} +\overset {\rightarrow}{Woman}=\overset {\rightarrow}{Quene}
King→−Man→+Woman→=Quene→
可见我们只要得到了词汇表里所有词对应的词向量,那么我们就可以做很多有趣的事情了。不过,怎么训练得到合适的词向量呢?一个很常见的方法是使用神经网络语言模型。
CBOW与Skip-Gram用于神经网络语言模型
在word2vec出现之前,已经有用神经网络DNN来用训练词向量进而处理词与词之间的关系了。采用的方法一般是一个三层的神经网络结构(当然也可以多层),分为输入层,隐藏层和输出层(softmax层)。
这个模型是如何定义数据的输入和输出呢?一般分为CBOW(Continuous Bag-of-Words 与Skip-Gram两种模型。
CBOW和skip-gram的模型结构如下图所示:
CBOW模型的训练输入是某一个特征词的上下文相关的词对应的词向量,而输出就是这特定的一个词的词向量。比如下面这段话,我们的上下文大小取值为4,特定的这个词是"Learning”,也就是我们需要的输出词向量,上下文对应的词有8个,前后各4个,这8个词是我们模型的输入。由于CBOW使用的是词袋模型,因此这8个词都是平等的,也就是不考虑他们和我们关注的词之间的距离大小,只要在我们上下文之内即可。
这样我们这个CBOW的例子里,我们的输入是8个词向量,输出是所有词的softmax概率(训练的目标是期望训练样本特定词对应的softmax概率最大),对应的CBOW神经网络模型输入层有8个神经元,输出层有词汇表大小个神经元。隐藏层的神经元个数我们可以自己指定。通过DNN的反向传播算法,我们可以求出DNN模型的参数,同时得到所有的词对应的词向量。这样当我们有新的需求,要求出某8个词对应的最可能的输出中心词时,我们可以通过一次DNN前向传播算法并通过softmax激活函数找到概率最大的词对应的神经元即可。
Skip-Gram模型和CBOW的思路是反着来的,即输入是特定的一个词的词向量,而输出是特定词对应的上下文词向量。还是上面的例子,我们的上下文大小取值为4, 特定的这个词"Learning"是我们的输入,而这8个上下文词是我们的输出。
这样我们这个Skip-Gram的例子里,我们的输入是特定词, 输出是softmax概率排前8的8个词,对应的Skip-Gram神经网络模型输入层有1个神经元,输出层有词汇表大小个神经元。隐藏层的神经元个数我们可以自己指定。通过DNN的反向传播算法,我们可以求出DNN模型的参数,同时得到所有的词对应的词向量。这样当我们有新的需求,要求出某1个词对应的最可能的8个上下文词时,我们可以通过一次DNN前向传播算法得到概率大小排前8的softmax概率对应的神经元所对应的词即可。
以上就是神经网络语言模型中如何用CBOW与Skip-Gram来训练模型与得到词向量的大概过程(理论)。但是这和word2vec中用CBOW与Skip-Gram来训练模型与得到词向量的过程有很多的不同,如过有兴趣可以去找一下官方的代码实现研究一下。
四、GLOVE
接下来要说的Glove又做了怎样的改进呢?
我们还看上面的例子:
不管是CBOW还是skip-gram,他们在训练词向量的过程中都受到了窗口大小的限制,只能看到以中心词为中心、上下文为C的若干词,也可以看做只用到了局部的上下文信息。
Glove全称Global Vectors for Word Representation,引入了全局的词共现矩阵Co-occurrence Probabilities Matrix,来作为训练词向量的依据。
首先,需要以滑动窗口的形式,得到全局的词共现矩阵。过程如下表:
中心词 | 上下文词(窗口为2) |
---|---|
an | efficient,method |
efficient | an|method,for |
method | an,efficient|for learningg |
for | efficient,method|learning,high |
learning | method, for|high,quality |
high | for,learning|quality,distributed |
quality | learning, high|distributed,vector |
distributed | high,quality|vector |
vector | quality,distributed |
共现矩阵:列表示中心词,行表示上下文词。
an | efficient | method | for | learning | high | quality | distributed | vector | |
---|---|---|---|---|---|---|---|---|---|
an | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
efficient | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 |
method | 1 | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 |
for | 0 | 1 | 1 | 0 | 1 | 1 | 0 | 0 | 0 |
learning | 0 | 0 | 1 | 1 | 0 | 1 | 1 | 0 | 0 |
high | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 1 | 0 |
quality | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 1 |
distributed | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 1 |
vector | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 |
定义几个符号:
X
i
=
∑
j
=
1
N
X
i
,
j
X_i=∑_{j=1}^{N}X_{i,j}
Xi=j=1∑NXi,j
其实就是矩阵单词i那一行的和;
P
i
,
k
=
X
i
,
k
X
i
P_{i,k}=\frac {X_{i,k}}{X_i}
Pi,k=XiXi,k
条件概率,表示单词k出现在单词i上下文语境中的概率;
r
a
t
i
o
i
,
j
,
k
=
P
i
,
k
P
j
,
k
ratio_{i,j,k}=\frac {P_{i,k}}{P_{j,k}}
ratioi,j,k=Pj,kPi,k
两个条件概率的比率。
作者发现,
r
a
t
i
o
i
,
j
,
k
ratio_{i,j,k}
ratioi,j,k这个指标是有规律的,规律统计在下表:
ratio(i,j,k)的值 | 单词j,k相关 | 单词j,k不相关 |
---|---|---|
单词i,k相关 | 趋近1 | 很大 |
单词i,k不相关 | 很小 | 趋近1 |
很简单的规律,也很好理解。
假设我们已经得到了词向量,如果我们用词向量
v
i
、
v
j
、
v
k
v_i、v_j、v_k
vi、vj、vk通过某种函数计算
r
a
t
i
o
i
,
j
,
k
ratio_{i,j,k}
ratioi,j,k,能够同样得到这样的规律的话,就意味着我们词向量与共现矩阵具有很好的一致性,也就说明我们的词向量中蕴含了共现矩阵中所蕴含的信息。
设用词向量
v
i
、
v
j
、
v
k
v_i、v_j、v_k
vi、vj、vk计算
r
a
t
i
o
i
,
j
,
k
ratio_{i,j,k}
ratioi,j,k的函数为
g
(
v
i
,
v
j
,
v
k
)
g(v_i,v_j,v_k)
g(vi,vj,vk),那么应该有:
P
i
,
k
P
j
,
k
=
g
(
v
i
,
v
j
,
v
k
)
=
r
a
t
i
o
i
,
j
,
k
\frac {P_{i,k}}{P_{j,k}}=g(v_i,v_j,v_k)=ratio_{i,j,k}
Pj,kPi,k=g(vi,vj,vk)=ratioi,j,k
即二者应该尽可能地接近;
很容易想到用二者的差方来作为代价函数:
J
=
∑
i
,
j
,
k
(
P
i
,
k
P
j
,
k
−
g
(
v
i
,
v
j
,
v
k
)
)
2
J=\underset {i,j,k}{\sum}(\frac {P_{i,k}}{P_{j,k}}-g(v_i,v_j,v_k))^2
J=i,j,k∑(Pj,kPi,k−g(vi,vj,vk))2
现在我们来仔细思考
g
(
v
i
,
v
j
,
v
k
)
g(v_i,v_j,v_ k)
g(vi,vj,vk),或许它能帮上忙;
作者的脑洞是这样的:
1、要考虑单词i和单词j之间的关系,那
g
(
v
i
,
v
j
,
v
k
)
g(v_i,v_j, v_k)
g(vi,vj,vk)中大概要有这么一项距离项吧:
(
v
i
−
v
j
)
(v_i−v_j)
(vi−vj),在线性空间中考察两个向量的相似性,不失线性地考察,那么
(
v
i
−
v
j
)
(v_i−v_j)
(vi−vj)大概是个合理的选择;
2、
r
a
t
i
o
i
,
j
,
k
ratio_{i,j,k}
ratioi,j,k是个标量,那么
g
(
v
i
,
v
j
,
v
k
)
g(v_i,v_j, v_k)
g(vi,vj,vk)最后应该是个标量啊,虽然其输入都是向量,那內积应该是合理的选择,于是应该有这么一项吧:
(
v
i
−
v
j
)
T
⋅
v
k
(v_i−v_j)^T·v_k
(vi−vj)T⋅vk。
3、然后作者又往
(
v
i
−
v
j
)
T
⋅
v
k
(v_i−v_j)^T·v_k
(vi−vj)T⋅vk的外面套了一层指数运算exp(),得到最终的
g
(
v
i
,
v
j
,
v
k
)
=
e
x
p
(
(
v
i
−
v
j
)
T
⋅
v
k
)
g(v_i,v_j,v_k)=exp((v_i−v_j)^T·v_k)
g(vi,vj,vk)=exp((vi−vj)T⋅vk)
那么现在的
r
a
t
i
o
i
,
j
,
k
ratio_{i,j,k}
ratioi,j,k就可以写成:
P
i
,
k
P
j
,
k
=
r
a
t
i
o
i
,
j
,
k
=
e
x
p
(
(
v
i
−
v
j
)
T
⋅
v
k
)
=
e
x
p
(
v
i
T
⋅
v
k
)
e
x
p
(
v
j
T
⋅
v
k
)
\frac {P_{i,k}}{P_{j,k}}=ratio_{i,j,k}=exp((v_i−v_j)^T·v_k)\\=\frac {exp(v_i^T·v_k)}{exp(v_j^T·v_k)}
Pj,kPi,k=ratioi,j,k=exp((vi−vj)T⋅vk)=exp(vjT⋅vk)exp(viT⋅vk)
容易的到此时:
g
(
v
i
,
v
j
)
=
e
x
p
(
v
i
T
⋅
v
j
)
g(v_i,v_j)=exp(v_i^T·v_j)
g(vi,vj)=exp(viT⋅vj)
那么我们前面想要优化的J:
J
=
∑
i
,
j
,
k
(
P
i
,
k
P
j
,
k
−
g
(
v
i
,
v
j
,
v
k
)
)
2
可
以
写
成
:
∑
i
,
j
(
P
i
,
j
−
g
(
v
i
,
v
j
)
)
2
=
∑
i
,
j
(
P
i
,
j
−
e
x
p
(
v
i
T
⋅
v
j
)
)
2
J=\underset {i,j,k}{\sum}(\frac {P_{i,k}}{P_{j,k}}-g(v_i,v_j,v_k))^2可以写成:\\ \underset {i,j}{\sum}(P_{i,j}-g(v_i,v_j))^2=\underset {i,j}{\sum}(P_{i,j}-exp(v_i^T·v_j))^2
J=i,j,k∑(Pj,kPi,k−g(vi,vj,vk))2可以写成:i,j∑(Pi,j−g(vi,vj))2=i,j∑(Pi,j−exp(viT⋅vj))2
相当于优化:
J
=
∑
i
,
j
(
l
o
g
P
i
,
j
−
v
i
T
⋅
v
j
)
2
J=\underset {i,j}{\sum}(log P_{i,j}-v_i^T·v_j)^2
J=i,j∑(logPi,j−viT⋅vj)2
根据
P
i
,
j
P_{i,j}
Pi,j定义:
l
o
g
P
i
,
j
=
l
o
g
X
i
,
j
−
l
o
g
X
i
logP_{i,j} =log X_{i,j}-logX_i\\
logPi,j=logXi,j−logXi
其中偏置项
b
i
,
b
j
b_i,b_j
bi,bj 将会替换
l
o
g
X
i
logX_i
logXi
而且考虑到每一个词对并不都是平权的,需要考虑词频来设定每一个词对的权重:
f
(
X
i
,
j
)
=
{
X
i
,
j
/
C
0.75
;
X
>
C
1
;
X
<
C
f(X_{i,j})=\left\{\begin{matrix} X_{i,j}/C^{0.75};X>C \\ 1;X<C \end{matrix}\right.
f(Xi,j)={Xi,j/C0.75;X>C1;X<C
最后我们希望最小化:
J
=
∑
i
,
j
f
(
X
i
,
j
)
(
l
o
g
X
i
,
j
−
v
i
T
⋅
v
j
−
b
i
−
b
j
)
2
J=\underset {i,j}{\sum}f(X_{i,j})(log X_{i,j}-v_i^T·v_j-b_i-b_j)^2
J=i,j∑f(Xi,j)(logXi,j−viT⋅vj−bi−bj)2
于是我们的词向量就
v
i
,
v
j
v_i,v_j
vi,vj就可以通过词共现矩阵的方式求解得到了。由于使用了基于全局的词共现信息,Glove在一定程度上集合了局部窗口和全局信息来训练词向量,并且训练效率远比word2vec要好,同word2vec一样,Glove词向量也是一种很常见的distributed respresentation。
五、Elmo
ELMO(Embeddings from Language Models) 的本质思想是:我事先用语言模型学好一个单词的 Word Embedding,此时多义词无法区分,不过这没关系。在我实际使用 Word Embedding 的时候,单词已经具备了特定的上下文了,这个时候我可以根据上下文单词的语义去调整单词的 Word Embedding 表示,这样经过调整后的 Word Embedding 更能表达在这个上下文中的具体含义,自然也就解决了多义词的问题了。所以 ELMO 本身是个根据当前上下文对 Word Embedding 动态调整的思路。
既然提到了上下文,自然我们会想到用LSTM来表示一句话以便获得长程依赖,实际上ELMo也正是基于深度Bi-LSTM模型来实现对原始的word embedding进行微调的。
下面我们看一张图:
这张动图解释的很形象了,我们输入原始的词向量,经过两层Bi-lstm层分别得到微调的词向量,最后综合三个词向量,生成符合当前语义的word embedding。
双向语言模型( bi-LM )是ELMO的基础。模型的输入是由n个token构成的序列,$ x_1,…,x_n
,
语
言
模
型
根
据
历
史
的
序
列
预
测
,语言模型根据历史的序列预测
,语言模型根据历史的序列预测x_1…x_{i-1}$下一个token
x
i
x_i
xi的概率。
在正向计算(forward pass)过程中,根据历史之前的序列
x
1
.
.
.
.
.
x
i
−
1
x_1.....x_{i-1}
x1.....xi−1预测第i个token
x
i
x_ i
xi的概率,
P
(
x
1
,
x
2
.
.
.
x
n
)
=
∏
i
=
1
n
p
(
x
i
∣
x
1
,
x
2
.
.
.
x
i
−
1
)
P(x_1,x_2...x_n)=\prod_{i=1}^{n}p(x_i|x_1,x_2...x_{i-1})
P(x1,x2...xn)=i=1∏np(xi∣x1,x2...xi−1)
在反向计算(backward pass)过程中,根据历史之后的序列
x
i
+
1
.
.
.
.
.
.
x
n
x_{i+1}......x_n
xi+1......xn预测第i个token
x
i
x_i
xi的概率,
P
(
x
1
,
x
2
.
.
.
x
n
)
=
∏
i
=
1
n
p
(
x
i
∣
x
i
+
1
,
x
i
+
2
.
.
.
x
n
)
P(x_1,x_2...x_n)=\prod_{i=1}^{n}p(x_i|x_{i+1},x_{i+2}...x_{n})
P(x1,x2...xn)=i=1∏np(xi∣xi+1,xi+2...xn)
两个方向上的预测过程都是由多层LSTM建模的,给定输入token
x
i
x_i
xi得到隐状态为
h
→
i
,
ℓ
\overset →h_{i,ℓ}
h→i,ℓ和
h
←
i
,
ℓ
\overset ← h_{i,ℓ}
h←i,ℓ。最后一层的输出的隐状态为
h
i
,
L
=
[
h
→
i
,
L
;
h
←
i
,
L
]
h_{i,L}=[\overset→ h_{i,L};\overset←h_{i,L}]
hi,L=[h→i,L;h←i,L]作为输出层的输入,用于得到最终的输出
y
i
y_i
yi。模型共享embedding层和softmax层,分别由
θ
e
θ_e
θe和
θ
s
θ_s
θs参数化表示。
该模型的目标函数(loss function)是最小化两个方向的负对数似然( =最大化真实单词的对数似然) :
L
o
s
s
=
−
∑
i
=
1
n
(
l
o
g
p
(
x
i
∣
x
1
,
.
.
.
,
x
i
−
1
;
Θ
e
,
Θ
→
L
S
T
M
,
Θ
s
)
+
(
l
o
g
p
(
x
i
∣
x
i
+
1
,
.
.
.
,
x
n
;
Θ
e
,
Θ
←
L
S
T
M
,
Θ
s
)
Loss = - \sum_{i=1}^{n}(logp(x_i|x_1,...,x_{i-1};\Theta_e,\overset \rightarrow \Theta_{LSTM}, \Theta_s)+(logp(x_i|x_{i+1},...,x_{n};\Theta_e,\overset \leftarrow \Theta_{LSTM}, \Theta_s)
Loss=−i=1∑n(logp(xi∣x1,...,xi−1;Θe,Θ→LSTM,Θs)+(logp(xi∣xi+1,...,xn;Θe,Θ←LSTM,Θs)
ELMO表示
在L层bi-LSTM之上,ELMO通过学习task-specific线性组合,将所有隐状态stack在一起。token
x
i
x_i
xi的隐状态表示包含2L+1向量:
R
i
=
h
i
,
ℓ
∣
ℓ
=
0
,
…
,
l
R_i={h_{i,ℓ}∣ℓ=0,…,l}
Ri=hi,ℓ∣ℓ=0,…,l,其中,
h
0
,
ℓ
h_{0,ℓ}
h0,ℓ代表embedding层的输出,且
h
i
,
ℓ
=
[
h
→
i
,
ℓ
;
h
←
i
,
ℓ
]
h_{i,ℓ}=[\overset →h_{i,ℓ};\overset ←h_{i,ℓ}]
hi,ℓ=[h→i,ℓ;h←i,ℓ]。
S(task)表示加权求和中每个任务的权重(通过softmax进行归一化)。缩放因子γ(task)用于校正bi-LSTM隐状态分布和task-specific表示分布之间的不一致性。
v
i
=
f
(
R
i
;
Θ
t
a
s
k
)
=
γ
t
a
s
k
∑
ℓ
=
0
L
s
i
t
a
s
k
h
i
,
ℓ
v_i=f(R_i;\Theta^{task})=\gamma^{task}\sum_{ℓ=0}^{L}s_i^{task}h_{i,ℓ}
vi=f(Ri;Θtask)=γtaskℓ=0∑Lsitaskhi,ℓ
为了评估不同层的隐状态具体捕获了什么样的信息,ELMO分别被应用于语义密集型(semantic-intensive)和语法密集型(syntax-intensive)任务,使用bi-LSTM的不同层中的表示(Representation)处理:
语义(Semantic)任务:词义消歧( word sense disambiguation,WSD )任务强调给定语境下单词的含义。biLM最底层的输出的表示比第一层的输出的表示获得更好的效果。
句法(Syntax)任务:词性标注任务(POS)旨在推断一个单词在一个句子中的语法角色。实验证明使用biLM第一层的输出的表示可以比最底层的输出表示获得更高的精度。
对比研究表明,ELMO底层表示学习到了很多句法特征,而在高层表示学习到更多语义特征。
ELMO仍然存在两个比较明显的不足:
1、使用Bi-LSTM模型构建语言模型,捕捉语义、长距离依赖和task-specific特征抽取、并行计算的能力弱于基于Self-Attention的Transformer模型。
2、通过拼接(word embedding,Forward hidden state,backward hidden state)方式融合特征的方式,削弱了语言模型特征抽取的能力。
六、Transformer
Transformer的Decoder(任何Encoder-Decoder结构的模型)阶段都会涉及到的一个算法:Beam-Search.
七、Bert
Bert是基于transformer编码层结构结合两个独立的任务NSP,MLM训练得到的基于语境的动态分布式词表示。
transformer这里不再多说了,原理基于self-attention,效果上面也提到了捕捉语义、长距离依赖和task-specific特征抽取、并行计算的能力都很好。
BERT的模型结构:
其实从结构上看,bert和elmo区别仅仅在于bert在抽取文本特征上采用了Bi-transformer模块,而Elmo采用的则是Bi-LSTM模块。
再来看一下bert的输入:
其中,position embeddings是为了给内部封装 的transformer提供位置信息,segment embeddings是提供了NSP任务训练的正样本数据。
在每句的最开头添加【CLS】的token,通过深度transformer编码之后作为句子级别的向量可以直接用,同时之隔向量也作为NSP分类任务的输入。
Masked Language Model
MLM和核心思想取自Wilson Taylor在1953年发表的一篇论文cloze procedure: A new tool for measuring readability。所谓MLM是指在训练的时候随即从输入预料上mask掉一些单词,然后通过的上下文预测该单词,该任务非常像我们在中学时期经常做的完形填空。正如传统的语言模型算法和RNN匹配那样,MLM的这个性质和Transformer的结构是非常匹配的。
Next Sentence Prediction
Next Sentence Prediction(NSP)的任务是判断句子B是否是句子A的下文。如果是的话输出’IsNext‘,否则输出’NotNext‘。训练数据的生成方式是从平行语料中随机抽取的连续两句话,其中50%保留抽取的两句话,它们符合IsNext关系,另外50%的第二句话
BERT的损失函数:
L
o
s
s
=
L
o
s
s
N
S
P
+
L
o
s
s
M
L
M
Loss = Loss_{NSP} + Loss_{MLM}
Loss=LossNSP+LossMLM
最终模型通过庞大语料规模下的预训练模型。
八、XLnet
现阶段对XLnet的理解并不是很深刻这里就只就自己的理解谈一下XLnet和bert明显的不同。
1、XLnet是基于Transformer的Decoder阶段,是自回归模型;而BERT用的是Transformer的Encoder阶段,是自编码模型。
自回归模型:常说的自左向右的语言模型任务,或者反过来也行,就是根据下文预测前面的单词,这种类型的LM被称为自回归语言模型。
自编码模型:相比而言,Bert通过在输入X中随机Mask掉一部分单词,然后预训练过程的主要任务之一是根据上下文单词来预测这些被Mask掉的单词,如果你对Denoising_Autoencoder比较熟悉的话,会看出,这确实是典型的DAE的思路。 通俗来讲就是Encoder+Decoder。
2、XLnet作者认为BERT训练过程中,MASK机制在后续Fine-tuning过程中不会涉及,因此会造成训练和测试不一致的情况可能会影响效果,因此提出了PLM(Permutation Language Model),Permutation是变化排序的意思,去掉了MASK机制,仍然用传统的语言模型思路解决问题。
XLNet做了些什么?
上文说过,Bert这种自编码语言模型的好处是:能够同时利用上文和下文,所以信息利用充分。对于很多NLP任务而言,典型的比如阅读理解,在解决问题的时候,是能够同时看到上文和下文的,所以当然应该把下文利用起来。在Bert原始论文中,与GPT1.0的实验对比分析也可以看出来,BERT相对GPT 1.0的性能提升,主要来自于双向语言模型与单向语言模型的差异。这是Bert的好处,很明显,Bert之后的改进模型,如果不能把双向语言模型用起来,那明显是很吃亏的。当然,GPT 2.0的作者不信这个邪,坚持沿用GPT 1.0 单向语言模型的旧瓶,装进去了更高质量更大规模预训练数据的新酒,而它的实验结果也说明了,如果想改善预训练语言模型,走这条扩充预序列模型训练数据的路子,是个多快好但是不省钱的方向。
但是Bert的自编码语言模型也有对应的缺点,就是XLNet在文中指出的,第一个预训练阶段因为采取引入[Mask]标记来Mask掉部分单词的训练模式,而Fine-tuning阶段是看不到这种被强行加入的Mask标记的,所以两个阶段存在使用模式不一致的情形,这可能会带来一定的性能损失;另外一个是,Bert在第一个预训练阶段,假设句子中多个单词被Mask掉,这些被Mask掉的单词之间没有任何关系,是条件独立的,而有时候这些单词之间是有关系的,XLNet则考虑了这种关系(关于这点原因是否可靠,后面会专门分析)。
上面两点是XLNet在第一个预训练阶段,相对Bert来说要解决的两个问题。
其实从另外一个角度更好理解XLNet的初衷和做法,我觉得这个估计是XLNet作者真正的思考出发点,是啥呢?就是说自回归语言模型有个缺点,要么从左到右,要么从右到左,尽管可以类似ELMO两个都做,然后再拼接的方式。但是跟Bert比,效果明显不足够好(这里面有RNN弱于Transformer的因素,也有双向语言模型怎么做的因素)。那么,能不能类似Bert那样,比较充分地在自回归语言模型中,引入双向语言模型呢?因为Bert已经证明了这是非常关键的一点。这一点,想法简单,但是看上去貌似不太好做,因为从左向右的语言模型,如果我们当前根据上文,要预测某个单词Ti,那么看上去它没法看到下文的内容。具体怎么做才能让这个模型:看上去仍然是从左向右的输入和预测模式,但是其实内部已经引入了当前单词的下文信息呢?XLNet在模型方面的主要贡献其实是在这里。
那么XLNet是怎么做到这一点的呢?其实思路也比较简洁,不像Bert那种带Mask符号的Denoising-autoencoder的模式,而是采用自回归LM的模式。就是说,看上去输入句子X仍然是自左向右的输入,看到
T
i
T_i
Ti单词的上文
C
o
n
t
e
x
t
b
e
f
o
r
e
Context_{before}
Contextbefore,来预测Ti这个单词。但是又希望在
C
o
n
t
e
x
t
b
e
f
o
r
e
Context_{before}
Contextbefore里,不仅仅看到上文单词,也能看到Ti单词后面的下文
C
o
n
t
e
x
t
a
f
t
e
r
Context_{after}
Contextafter里的下文单词,这样的话,Bert里面预训练阶段引入的Mask符号就不需要了,于是在预训练阶段,看上去是个标准的从左向右过程,Fine-tuning当然也是这个过程,于是两个环节就统一起来。当然,这是目标。剩下是怎么做到这一点的问题。
假设我们现在有一句话: I have a Dream.
正常语序token编号为:1–>2–>3–>4,自回归模型的特点是,只能依靠上文的信息预测下一个词。
现在我们的目的是预测原始语句中,token=3词(注意是Token=3),正常语序是,自然我们需要利用1,2的信息,下面我们看图:
那么,怎么能够在单词Ti的上文中
C
o
n
t
e
x
t
b
e
f
o
r
e
Context_{before}
Contextbefore中揉入下文
C
o
n
t
e
x
t
a
f
t
e
r
Context_{after}
Contextafter的内容呢?你可以想想。XLNet是这么做的,在预训练阶段,引入Permutation Language Model的训练目标。什么意思呢?就是说,比如包含单词
T
i
T_ i
Ti的当前输入的句子X,由顺序的几个单词构成,比如
x
1
,
x
2
,
x
3
,
x
4
x_1,x_2,x_3,x_4
x1,x2,x3,x4四个单词顺序构成。我们假设,其中,要预测的单词
T
i
T_i
Ti是
x
3
x_3
x3,位置在Position3,要想让它能够在上文
C
o
n
t
e
x
t
b
e
f
o
r
e
Context_{before}
Contextbefore中,也就是Position1或者Position2的位置看到Position 4的单词
x
4
x_4
x4。可以这么做:假设我们固定住x3所在位置,就是它仍然在Position 3,之后随机排列组合句子中的4个单词,在随机排列组合后的各种可能里,再选择一部分作为模型预训练的输入X。比如随机排列组合后,抽取出
x
4
,
x
2
,
x
3
,
x
1
x_4,x_2,x_3,x_1
x4,x2,x3,x1这一个排列组合作为模型的输入X。于是,
x
3
x_3
x3就能同时看到上文
x
2
x_2
x2,以及下文
x
4
x_4
x4的内容了。这就是XLNet的基本思想,所以说,看了这个就可以理解上面讲的它的初衷了吧:看上去仍然是个自回归的从左到右的语言模型,但是其实通过对句子中单词排列组合,把一部分
T
i
T_i
Ti下文的单词排到Ti的上文位置中,于是,就看到了上文和下文,但是形式上看上去仍然是从左到右在预测后一个单词。
当然,上面讲的仍然是基本思想。难点其实在于具体怎么做才能实现上述思想。首先,需要强调一点,尽管上面讲的是把句子X的单词排列组合后,再随机抽取例子作为输入,但是,实际上你是不能这么做的,因为Fine-tuning阶段你不可能也去排列组合原始输入。所以,就必须让预训练阶段的输入部分,看上去仍然是
x
1
,
x
2
,
x
3
,
x
4
x_1,x_2,x_3,x_4
x1,x2,x3,x4这个输入顺序,但是可以在Transformer部分做些工作,来达成我们希望的目标。具体而言,XLNet采取了Attention掩码的机制,你可以理解为,当前的输入句子是X,要预测的单词Ti是第i个单词,前面1到i-1个单词,在输入部分观察,并没发生变化,该是谁还是谁。但是在Transformer内部,通过Attention掩码,从X的输入单词里面,也就是Ti的上文和下文单词中,随机选择i-1个,放到Ti的上文位置中,把其它单词的输入通过Attention掩码隐藏掉,于是就能够达成我们期望的目标(当然这个所谓放到Ti的上文位置,只是一种形象的说法,其实在内部,就是通过Attention Mask,把其它没有被选到的单词Mask掉,不让它们在预测单词Ti的时候发生作用,如此而已。看着就类似于把这些被选中的单词放到了上文
C
o
n
t
e
x
t
b
e
f
o
r
e
Context_{before}
Contextbefore的位置了)。具体实现的时候,XLNet是用“双流自注意力模型”实现的,细节可以参考论文,但是基本思想就如上所述,双流自注意力机制只是实现这个思想的具体方式。