第2章 自然语言和单词的分布式表示
第3章 word2vec
第4章 word2vec的高速化
第5章 RNN
第6章 Gated RNN
第7章 基于RNN生成文本
第8章 Attention
《深度学习进阶:自然语言处理》第4章 word2vec的高速化
第4章 word2vec的高速化
本章是对第3章的补充,在第3章中介绍的基本原理的基础上,在一些实现的细节上实现计算量的缩减。对于只想了解word2vec原理的同学,可以跳过原书中的本章内容,或者只看本篇总结即可。
上一章中提到的CBOW模型的机构如下:
可以看出,输入层和输入权重矩阵有两次矩阵相乘计算,中间层和输出层权重矩阵有一次矩阵相乘计算,后面还有Softmax计算。如果语料库中包含100万个单词,中间层降维到100维,则每个单词的one-hot编码
c
c
c将是100万维,输入权重矩阵
W
i
n
W_{in}
Win尺寸为1000000*100,输出权重矩阵
W
o
u
t
W_{out}
Wout尺寸为100*1000000。这是非常大的计算量。
针对中间层之前和之后,下面分别介绍两个减小计算量的方式:
- 引入Embedding层
- 负采样(negative sampling)
4.1 word2vec的改进①
引入Embedding层简化输入侧的计算。
对于正向传播,每个输入单词的one-hot编码向量
c
c
c 与输入权重矩阵
W
i
n
W_{in}
Win相乘时,都是相当于从权重矩阵
W
i
n
W_{in}
Win中直接取出与输入单词编码向量
c
c
c 中唯一为1的元素的索引对应的行向量,其他行向量都在与单词编码向量
c
c
c 中的0元素相乘中被剔除了。过程如下:
所以输入向量与输入权重矩阵
W
i
n
W_{in}
Win不需要经过矩阵相乘进行计算,而是直接从中抽取对应行向量就可以了。
对于反向传播,由于正向传播中没有计算,直接将后端的梯度传递到输入梯度矩阵
d
W
i
n
dW_{in}
dWin对应行即可。
有一种情况需要注意,某一次输入可能会包含相同的单词,这样就会从输入权重矩阵 W i n W_{in} Win中多次取相同的行向量。所以这种情况下反向传播时,就会有多个梯度需要传递到输入梯度 d W i n dW_{in} dWin的同一行中。我们采用想加的方式处理这些传递来的多个梯度。
4.2 word2vec的改进②
图4-2中,中间层向量
h
h
h(1*100)需要和输出权重矩阵
W
o
u
t
W_{out}
Wout(100*1000000)相乘,输出向量
s
s
s(1*1000000),每个元素表示对应单词的可能性。这是典型的多分类问题,解答了“当前单词是什么”的疑问,返回所有单词作为结果的可能性,并且横向对比所有可能性,得出最佳单词。虽然多分类问题得出的结果信息量较大,但最佳单词之外的其他单词的概率作用不大。
将多分类问题拆解成二分类,使用二分类来拟合多分类问题,能极大降低计算量。将上面的多分类问题转化为“当前单词是否是xxxx”,只需要模型给出一个单词的概率。
这样我们只需要计算中间层向量
h
h
h(1*100)和输出权重矩阵
W
o
u
t
W_{out}
Wout中与指定单词对应的列向量(100*1)相乘的结果,并通过Sigmoid函数激活。计算量大大降低。
问题来了,选择哪些单词作为关注的对象呢?每次窗口滑动时的当前单词肯定是不能缺少的,作为二分类问题中的正例;负例是不可或缺的,不然模型只知道正例,没有学到负例。获取负例的过程就是负采样
。
显然,我们可以直接在单词库中去除正例单词,在剩下的单词中随机抽样获取若干单词作为负例。本书是在语料库中统计单词出现的频次,按照频次权重进行抽样。
对于正例,我们希望最终的结果经过激活函数后趋近于1,负例则趋近于0。Sigmoid损失函数:
L
=
−
(
t
log
y
+
(
1
−
t
)
log
(
1
−
y
)
)
(4.3)
L=-(t\log y + (1-t)\log(1-y)) \tag {4.3}
L=−(tlogy+(1−t)log(1−y))(4.3)
再回头看看,在多分类的情况下的交叉熵损失函数式(3.2),如果输出层只有两个神经元,则和二分类的式 (4.3) 是完全一致的。所以说Sigmoid损失函数是交叉熵损失函数的特例。
对于一组正例与负例的组合,我们将该组内的所有损失相加作为最后的损失。