第2章 自然语言和单词的分布式表示
第3章 word2vec
第4章 word2vec的高速化
第5章 RNN
第6章 Gated RNN
第7章 基于RNN生成文本
第8章 Attention
《深度学习进阶:自然语言处理》第3章 word2vec
第3章 word2vec
本章描述的是上一章中提到的单词的分布式表示的新的方法。上一章中介绍了基于计数的分布式表示,本章将介绍基于推理的分布式表示
。
3.1 基于推理的方法和神经网络
3.1.1 基于计数的方法的问题
基于计数的分布式表示,先生成单词的共现矩阵,再对矩阵进行奇异值分解(SDV)得到密集向量。从这个过程中就能容易发现改方法的弊端:现实中语料库单词数量非常庞大,就需要形成非常大的矩阵,对这样庞大的矩阵进行SDV是不现实的。对于一个 n × n 的矩阵,SVD 的复杂度是 O(n3)。
而基于推理的方法使用神经网络,通常在 mini-batch 数据上进行学习。这意味着神经网络一次只
需要看一部分学习数据(mini-batch),并反复更新权重。并且,神经网络的学习可以使用多台机器、多个 GPU 并行执行,从而加速整个学习过程。
3.1.2 基于推理的方法的概要
给出周围的单词(上下文)时,预测当前位置会出现什么单词。下面是窗口大小为1的一个例子。
如果把推理过程封装成一个模型,这个模型的输入就是“you”、“goodbey”,期望的输出是语料库中出现的每个单词的概率。模型在学习的过程中会产生单词的分布式表示。
3.1.3 神经网络中单词的处理方法
我们使用神经网络实现上面说的模型。
神经网络只能接收数值,需要将单词转化为编号。常用的一种方法是独热编码(one-hot)。
“You say goodbye and I say hello.”这一句话语料库中的单词可以编码如下:
单词 | 单词ID | one-hot表示 |
---|---|---|
you | 0 | (1,0,0,0,0,0,0) |
say | 1 | (0,1,0,0,0,0,0) |
goodbey | 2 | (0,0,1,0,0,0,0) |
and | 3 | (0,0,0,1,0,0,0) |
i | 4 | (0,0,0,0,1,0,0) |
hello | 5 | (0,0,0,0,0,1,0) |
. | 6 | (0,0,0,0,0,0,1) |
这样就给每个单词创建了一个初始的7维向量,与神经网络的输入层神经元个数一致。
下面将使用一个7*3的单层网络,将单词you的向量(1,0,0,0,0,0,0)由7维转化为3维。
输入层接收单词的向量,输入层和中间层之间7*3=21条箭头是参数,表示神经元之间传递的权重,中间层就是转化后的单词向量。
下面我们将参数矩阵
W
W
W中的元素都初始化为1,方便演示。
[
1
0
0
0
0
0
0
]
⋅
[
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
]
=
[
1
1
1
]
\left[\begin{array}{l} 1 & 0 & 0 & 0 & 0 & 0 & 0 \end{array}\right] · \left[\begin{array}{l} 1 & 1 & 1 \\ 1 & 1 & 1 \\ 1 & 1 & 1 \\ 1 & 1 & 1 \\ 1 & 1 & 1 \\ 1 & 1 & 1 \\ 1 & 1 & 1 \\ \end{array}\right] = \left[\begin{array}{l} 1 & 1 & 1 \end{array}\right]
[1000000]⋅
111111111111111111111
=[111]
能看出来,参数矩
W
W
W每个列向量就是与中间层对应元素连接的箭头的权重,每个行向量就是与输入层对应元素连接的箭头的权重。也就是说,
W
i
j
W_{ij}
Wij表示输入层第
i
i
i个元素与中间层第
j
j
j个元素之间的权重。
权重矩阵 W W W有各种不同的初始化方式,在训练过程中会不断迭代优化。
3.2 简单的word2vec
word2vec有两个常用的神经网络:CBOW和skip-gram。先讲CBOW,3.5.2中将介绍skip-gram。
3.2.1 CBOW模型的推理
CBOW,continuous bag-of-words,根据上下文预测目标词的神经网络。还是以“You say goodbey and i say hello.”为例,对于目标单词第一个say,窗口大小为1时,上下文为[‘you’, ‘goodbye’],构建如下的CBOW神经网络。
两个输入网络共用一个全连接层,权重为
W
i
n
W_{in}
Win,一个输出网络,权重为
W
o
u
t
W_{out}
Wout。如果窗口大小为2,则会有4个输入网络。
第一个输入层的结果
h
1
h_1
h1,第二个输出入层的结果
h
2
h_2
h2,将两个输入网络的结果的平均
1
2
(
h
1
+
h
2
)
\frac{1}{2}(h_1+h_2)
21(h1+h2)作为中间层的值。
输出层的神经元个数需要和单词的个数一致,每个神经元的值对应每个单词的得分,经过softmax函数后就可以得到每个单词作为目标词的概率。
两个输入层和图3.7的结构一致,权重矩阵
W
i
n
W_{in}
Win每个行向量就是对应单词的分布式表示。
中间层的神经元数量比输入层少这一点很重要。中间层需要将预测单词所需的信息压缩保存,从而产生密集的向量表示。这时,中间层被写入了我们人类无法解读的代码,这相当于“
编码
”工作。而从中间层的信息获得期望结果的过程则称为“解码
”。这一过程将被编码的信息复原为我们可以理解的形式。
从层视角图示CBOW模型:
3.2.2 CBOW模型的学习
3.2.3 word2vec的权重和分布式表示
输入侧的权重
W
i
n
W_{in}
Win的每一行对应于各个单词的分布式表示,输出侧的权重
W
o
u
t
W_{out}
Wout在列方向上保存了各个单词的分布式表示。
word2vec(特别是 skip-gram 模型)使用输入侧的权重,与word2vec相似的GloVe则将两个权重相加。
3.3 学习数据的准备
- 使用指定大小的窗口在语料库中滑动,形成上下文输入单词列表和对应的目标单词
- 生成单词ID
- 每个单词生成one-hot编码
3.4 CBOW模型的实现
本书中使用自己编写的代码实现。后面补充直接使用pytorch实现。
3.5 word2vec的补充说明
3.5.1 CBOW模型和概率
很显然,CBOW模型的训练过程就是给出每个目标单词的上下文,预测目标单词,输出每个待选单词的概率。
当窗口大小为1时,使用概率公式表示如下:
P
(
w
t
∣
w
t
−
1
,
w
t
+
1
)
(3.1)
P(w_t|w_{t-1}, w_{t+1}) \tag {3.1}
P(wt∣wt−1,wt+1)(3.1)
结合交叉熵损失函数
L
=
−
∑
k
t
k
l
o
g
y
k
L=-\sum \limits _k t_klog y_k
L=−k∑tklogyk,其中
t
k
t_k
tk是监督标签,
y
k
y_k
yk表示第
k
k
k个事件的概率。
由于此处
t
k
t_k
tk是one-hot向量,只有正确解
w
t
w_t
wt处的元素是1,所以对于一个目标单词,损失函数如下:
L
=
−
l
o
g
P
(
w
t
∣
w
t
−
1
,
w
t
+
1
)
(3.2)
L=-logP(w_t|w_{t-1}, w_{t+1}) \tag {3.2}
L=−logP(wt∣wt−1,wt+1)(3.2)
上面的定义又称为负对数似然(negative log likelihood)
。
扩展到整个预料库(或者在训练时的一个batch),损失函数为:
L
=
−
1
T
∑
t
=
1
T
l
o
g
P
(
w
t
∣
w
t
−
1
,
w
t
+
1
)
(3.3)
L=-\frac {1}{T} \sum \limits _{t=1} ^T log P(w_t|w_{t-1}, w_{t+1}) \tag {3.3}
L=−T1t=1∑TlogP(wt∣wt−1,wt+1)(3.3)
3.5.2 skip-gram模型
word2vec的另外一个模型,skip-gram。
CBOW模型是根据上下文推测当前词,skip-gram是根据当前词推测上下文。
skip-gram模型的网络如下:
skip-gram模型的损失是多个输出层损失的和。
套用上一节的概率公式,skip-gram模型可以建模为:
P
(
w
t
−
1
,
w
t
+
1
∣
w
t
)
(3.4)
P(w_{t-1}, w_{t+1}|w_t) \tag {3.4}
P(wt−1,wt+1∣wt)(3.4)
假设上下文单词互相独立,上式可以写成:
P
(
w
t
−
1
,
w
t
+
1
∣
w
t
)
=
P
(
w
t
−
1
∣
w
t
)
P
(
w
t
+
1
∣
w
t
)
(3.5)
P(w_{t-1}, w_{t+1}|w_t) = P(w_{t-1}|w_t) P(w_{t+1}|w_t) \tag {3.5}
P(wt−1,wt+1∣wt)=P(wt−1∣wt)P(wt+1∣wt)(3.5)
则skip-gram模型的损失函数为:
L
=
−
l
o
g
P
(
w
t
−
1
,
w
t
+
1
∣
w
t
)
=
−
l
o
g
P
(
w
t
−
1
∣
w
t
)
P
(
w
t
+
1
∣
w
t
)
=
−
(
l
o
g
P
(
w
t
−
1
∣
w
t
)
+
l
o
g
P
(
w
t
+
1
∣
w
t
)
)
(3.6)
\begin{aligned} L&=-log P(w_{t-1}, w_{t+1}|w_t) \\ &=-log P(w_{t-1}|w_t) P(w_{t+1}|w_t) \\ &=-(log P(w_{t-1}|w_t) + log P(w_{t+1}|w_t)) \tag {3.6} \end{aligned}
L=−logP(wt−1,wt+1∣wt)=−logP(wt−1∣wt)P(wt+1∣wt)=−(logP(wt−1∣wt)+logP(wt+1∣wt))(3.6)
扩展到整个预料库(或者在训练时的一个batch),损失函数为:
L
=
−
1
T
∑
t
=
1
T
(
l
o
g
P
(
w
t
−
1
∣
w
t
)
+
l
o
g
P
(
w
t
+
1
∣
w
t
)
)
(3.7)
L=-\frac {1}{T} \sum \limits _{t=1} ^T (log P(w_{t-1}|w_t) + log P(w_{t+1}|w_t)) \tag {3.7}
L=−T1t=1∑T(logP(wt−1∣wt)+logP(wt+1∣wt))(3.7)
所以skip-gram模型的损失函数需要求所有上下文的单词对于的损失的总和。
从单词的分布式表示的准确度来看,相比CBOW模型,在大多数情况下,skip-grm模型的结果更好。特别是随着语料库规模的增大,在低频词和类推问题的性能方面,skip-gram模型往往会有更好的表现。当然因为要计算的损失更多,skip-gram模型的计算速度较慢。
3.5.3 基于计数与基于推理
基于计数的方法通过对整个语料库的统计数据进行一次学习来获得单词的分布式表示,而基于推理的方法则反复观察语料库的一部分数据进行学习(mini-batch 学习)。
如果向词汇表添加新词,基于计数的方法需要从头开始计算。即需要重新完成生成共现矩阵、进行 SVD 等一系列操作。
基于推理的方法(word2vec)允许参数的增量学习,可以将之前学习到的权重作为下一次学习的初始值,在更新的语料库上继续学习。
基于计数的方法和word2vec都能编码单词的相似性,并且在效果上难分上下;但word2vec(特别是 skip-gram 模型)还能理解更复杂的单词之间的模式,word2vec因能解开“king − man + woman = queen”这样的类推问题而知名。
基于推理的方法和基于计数的方法存在关联性。GloVe方法融合了基于推理的方法和基于计数的方法。