词向量表示word2vec
- tensorflow代码
https://github.com/NELSONZHAO/zhihu/tree/master/skip_gram - pytorch代码
https://github.com/bamtercelboo/pytorch_word2vec - 参考
https://www.jianshu.com/p/ecd137e75454
1、Word Meanning
1.1我们怎样表述一个单词的意思
对于英语中的meaning这个单词,韦伯词典中的解释如下:
- the idea that is represented by a word, phrase, etc.
- the idea that a person wants to express by using words, signs, etc.
- the idea that is expressed in a work of writing, art, etc
这也就是上一节中讲的NLP难的问题之一,歧义。一个单词往往有很多个意思,在合适的语境下有合适的意思。
1.2 在计算机中我们如何处理意思?
使用分类词典(WordNet)来表示计算机中的词语。
from nltk.corpus import wordnet as wd
panda = wd.synset('panda.n.01')
hyper = lambda s : s.hypernyms()
list(panda.closure(hyper)
结果是:
[Synset('procyonid.n.01'),
Synset('carnivore.n.01'),
Synset('placental.n.01'),
Synset('mammal.n.01'),
Synset('vertebrate.n.01'),
Synset('chordate.n.01'),
Synset('animal.n.01'),
Synset('organism.n.01'),
Synset('living_thing.n.01'),
Synset('whole.n.02'),
Synset('object.n.01'),
Synset('physical_entity.n.01'),
Synset('entity.n.01')]
可以看到我们是采用一种离散的形式来进行表示的。那么这种离散表示存在什么问题呢?
-
使用语言学的资源虽然很好,但是还是不够细致。比如一些同义词:
adept, expert, good, practiced, proficient, skillful -
缺少新词
-
太过主观
-
需要人自己去创建和更新
-
很难去计算单词之间的相似性
无论是在任何学科,研究语言处理都是将词语作为最小的单位,那么在这其中有一种最简单的给词语编码的方式就是one-hot编码。比如现在单词表有5个单词,分别是【我,喜欢,跑步,运动,喝水】,那么我们想表示运动这个词语就是:[0, 0, 0, 1, 0],同样的表示喝水就是[0, 0, 0, 0, 1]。可见实际上表示的就是单词的位置。但是这样表示有一些问题:
维度爆炸问题,课程中提出了谷歌发布的1TB语料词汇量是1300万,这样完全维度爆炸了。
相关性问题,比如上例中运动的向量是[0, 0, 0, 1, 0],而跑步的向量是[0, 0, 1, 0, 0]两向量的内积为0,也就是完全不相关。但实际中我们知道,运动和跑步并不是毫无关系的,所以这种表示方式表达不出来这种关系。
ps:为什么向量内积可以得到相关性?
请参考余弦定理。
1.3 从符号表示到分布式表示
正因为 ont-hot 编码无法表示相关性,所以新提出了一种分布式表示,也就是分布式表示。语言学家J. R. Firth提出,通过一个单词的上下文可以得到它的意思,这是现代基于统计的自然语言处理成功的主要原因。比如下面这个例子,你只有把banking放到句子中,你才能真正确定它的意思。
通过向量去表示单词的意思
将为每个单词类型构建一个密集向量,以便它能够很好地预测上下文中出现的其他单词,而这个密集向量一般在300-500维左右,有的甚至超过1000维,但是和ont-hot相比,已经是降维了。它用一些上下文中出现的单词来表示一个中心词,比如下面这个例子,当然维度要更高。
横坐标表示我想表示出来的中心词,纵坐标表示用一些上下文中出现的词来表示他们。我们可以很明确的看到MAN的性别是-1,Woman的性别是1,其他的都差不多,而Apple和Orange都是食物,所以基本Food的占比很高。
可以看的出来,这种表示的方法贼好,但是有个问题就是,我们怎么能成功的用计算机实现这种表示方法,其中的一种方法就是后面要提到的word2vec模型。
1.4 学习神经网络词嵌入的基本思想
我们首先定义一个预测某个单词的上下文的模型,
p
(
c
o
n
t
e
x
t
∣
w
t
)
=
.
.
.
p(context|w_t)=...
p(context∣wt)=...
其中
c
o
n
t
e
x
t
context
context为需要预测的上下文,
w
t
w_t
wt为给定的词
再定义一个损失函数:
J
=
1
−
p
(
w
−
t
∣
w
t
)
J = 1-p(w_{-t}|w_t)
J=1−p(w−t∣wt)
这里的
w
−
t
w_{-t}
w−t表示除了的上下文
w
t
w_t
wt(负号通常表示除了某某之外,比如下面的例子中表示除了苹果以外的其他任何单词),如果完美预测,损失函数为零。然后在一个大型语料库中的不同位置得到训练实例,调整词向量,最小化损失函数。
比如下面这句话:我想吃苹果,也想吃梨子。
根据上面的例子,当苹果出现了,预测下面梨子出现的可能性。所以概率模型也就是:
p
(
梨
子
∣
苹
果
)
=
.
.
.
p(梨子|苹果)=...
p(梨子∣苹果)=...
那么损失函数也就是:
J
=
1
−
p
(
w
∣
苹
果
)
J = 1-p(w|苹果)
J=1−p(w∣苹果)
如果w = 梨子,那么概率为1,损失函数为0。
1.5 直接学习低维词向量
- Learning representations by back-propagating errors (Rumelhart et al., 1986)
- A neural probabilistic language model (Bengio et al., 2003)
- NLP (almost) from Scratch (Collobert & Weston, 2008)
- A recent, even simpler and faster model: word2vec (Mikolov et al. 2013)
2. Word2vec
首先word2vec有两种算法:
- skip-gram:通过中心词去预测周边的词
- Continuous Bag of Words(CBOW):通过上下文的单词,去预测中心词。
两种有效的训练方法:
- Hierarchical softmax
- Negative sampling
在本课中只会讲Naïve softmax。
按步骤,我们首先来讲Skip-gram
2.1 Skip-gram
我们Skip-gram模型的任务就是要通过into去预测周围的一些词。先定义一个半径m,我们需要预测的就是以into为原点,m为半径范围内的词注意这里虽然有四条线,但模型中只有一个条件分布。学习就是要最大化这些词在into的假设下的条件概率。
2.1.1 损失函数
对上面的模型,我们的损失函数定义为所有位置的预测结果的乘积:
J
′
(
θ
)
=
∏
t
=
1
T
∏
−
−
m
≤
j
≤
m
,
j
≠
0
p
(
w
t
+
j
∣
w
t
;
θ
)
J'(\theta) = \prod_{t=1}^T \prod_{--m\leq j \leq m,j\neq 0}p(w_{t+j}|w_t;\theta)
J′(θ)=t=1∏T−−m≤j≤m,j̸=0∏p(wt+j∣wt;θ)
其中T为文本的总长度,m为上下文的长度,然后取对数似然的相反数方便计算,得到:
J
(
θ
)
=
−
1
T
∑
t
=
1
T
∑
−
−
m
≤
j
≤
m
,
j
≠
0
l
o
g
p
(
w
t
+
j
∣
w
t
)
J(\theta) = -\frac{1}{T}\sum_{t=1}^T \sum_{--m\leq j \leq m,j\neq 0}log p(w_{t+j}|w_t)
J(θ)=−T1t=1∑T−−m≤j≤m,j̸=0∑logp(wt+j∣wt)
其中的
p
(
w
t
+
j
∣
w
t
)
p(w_{t+j}|w_t)
p(wt+j∣wt)可以使用softmax函数进行计算,softmax函数主要是将数值转化成概率。
p
(
o
∣
c
)
=
e
x
p
(
u
o
T
v
c
)
∑
w
=
1
v
e
x
p
(
u
w
T
v
c
)
p(o|c) = \frac{exp(u_o^T v_c)}{\sum_{w=1}^v exp(u_w^T v_c)}
p(o∣c)=∑w=1vexp(uwTvc)exp(uoTvc)
其中o是输出的中间词上下文中的确切某一个,c是中间词。u是对应的上下文词向量,v是词向量。
这里举一个栗子:
我想吃苹果,也想吃__.
现在我有这样一个需求在这,需要根据苹果取预测后面那句话空白处的结果,此时
o
o
o表示空白处的需要预测的词,比如梨子;
c
c
c表示中间词苹果,
u
u
u和
v
v
v表示的都是词向量,
u
o
u_o
uo表示的是梨子(假设空白处的正确答案是梨子)的向量,
v
c
v_c
vc表示的是中间词苹果的词向量,
u
w
u_w
uw表示所有上下文的词的词向量。
上述式子中 u o T v c = u ⋅ v = ∑ i u i v i u_o^T v_c = u\cdot v = \sum_{i} u_i v_i uoTvc=u⋅v=∑iuivi 为向量 u u u与 v v v的内积,其可以刻画两个向量的相关性,两个向量的相似度越大,其内积也越大。Softmax即从实数空间到概率分布的标准映射方法。所以 p ( o ∣ c ) p(o|c) p(o∣c)其实衡量了预测的答案梨子与中间词苹果的相关性,并且将这个相关性的大小转化为概率。
2.1.2 skip-gram的详细过程
首先左边输入的是一个某个单词one-hot变量,我们知道skip-gram是通过一个中心词去预测周围的词的,然后
V
×
1
V\times 1
V×1维的输入向量和一个
d
×
V
d\times V
d×V维的嵌入矩阵
W
W
W相乘,得到一个压缩后
d
×
1
d\times 1
d×1维词向量,再和一个
V
×
d
V\times d
V×d 维嵌入矩阵
W
′
W'
W′相乘得到输出向量,然后输出向量经过softmax函数,变成一个概率,然后我们可以找到对应位置概率最大的判断为预测单词,根据位置去写出对应的ont-hot变量,进而确定是哪个单词。三个
W
′
W'
W′矩阵具有相同的参数,因此每次训练的时候分别用中心词上下文所有的目标单词去计算loss,目的是最小化所有条件分布的乘积。其中矩阵
W
W
W和
W
′
W'
W′都可以作为词嵌入的矩阵。
2.1.3 训练模型:计算向量的的梯度
我们把所有的需要参数放进一个向量当作,向量如下:
其实这就是两个矩阵
W
W
W和
W
′
W'
W′的所有权重。然后求这个向量的梯度,再使用梯度下降法进行训练即可。
2.2 梯度下降
对于损失函数
J
(
θ
)
=
−
1
T
∑
t
=
1
T
∑
−
−
m
≤
j
≤
m
,
j
≠
0
l
o
g
p
(
w
t
+
j
∣
w
t
)
J(\theta) = -\frac{1}{T}\sum_{t=1}^T \sum_{--m\leq j \leq m,j\neq 0}log p(w_{t+j}|w_t)
J(θ)=−T1t=1∑T−−m≤j≤m,j̸=0∑logp(wt+j∣wt)
其中
p
(
o
∣
c
)
=
e
x
p
(
u
o
T
v
c
)
∑
w
=
1
v
e
x
p
(
u
w
T
v
c
)
p(o|c) = \frac{exp(u_o^T v_c)}{\sum_{w=1}^v exp(u_w^T v_c)}
p(o∣c)=∑w=1vexp(uwTvc)exp(uoTvc)o和c为context和中心词。训练中每次都已
(
c
e
n
t
e
r
,
c
o
n
t
e
x
t
)
(center, context)
(center,context)的格式送入数据
∂
∂
V
c
l
o
g
e
x
p
(
u
o
T
v
c
)
∑
w
=
1
v
e
x
p
(
u
w
T
v
c
)
=
∂
∂
V
c
l
o
g
e
x
p
(
u
o
T
v
c
)
−
∂
∂
V
c
l
o
g
∑
w
=
1
v
e
x
p
(
u
w
T
v
c
)
\frac{\partial}{\partial V_c}log \frac{exp(u_o^T v_c)}{\sum_{w=1}^v exp(u_w^T v_c)} = \frac{\partial}{\partial V_c}log exp(u_o^T v_c)- \frac{\partial}{\partial V_c}log \sum_{w=1}^v exp(u_w^T v_c)
∂Vc∂log∑w=1vexp(uwTvc)exp(uoTvc)=∂Vc∂logexp(uoTvc)−∂Vc∂logw=1∑vexp(uwTvc)
由上式可以看出,损失函数可以分为两部分
∂
∂
V
c
l
o
g
e
x
p
(
u
o
T
v
c
)
\frac{\partial}{\partial V_c}log exp(u_o^T v_c)
∂Vc∂logexp(uoTvc)
∂
∂
V
c
l
o
g
∑
w
=
1
v
e
x
p
(
u
w
T
v
c
)
\frac{\partial}{\partial V_c}log \sum_{w=1}^v exp(u_w^T v_c)
∂Vc∂logw=1∑vexp(uwTvc)
左边部分由于
l
o
g
log
log和
e
x
p
exp
exp可以抵消,故
∂
∂
V
c
l
o
g
e
x
p
(
u
o
T
v
c
)
=
∂
∂
V
c
u
o
T
v
c
=
u
o
\frac{\partial}{\partial V_c}log exp(u_o^T v_c) = \frac{\partial}{\partial V_c}u_o^T v_c= u_o
∂Vc∂logexp(uoTvc)=∂Vc∂uoTvc=uo
右边部分则利用链式法则进行求解
∂
∂
V
c
l
o
g
∑
w
=
1
v
e
x
p
(
u
w
T
v
c
)
=
1
∑
w
=
1
v
e
x
p
(
u
w
T
v
c
)
∑
x
=
1
v
e
x
p
(
u
x
T
v
c
)
u
x
\frac{\partial}{\partial V_c}log \sum_{w=1}^v exp(u_w^T v_c) = \frac{1}{\sum_{w=1}^v exp(u_w^T v_c)} \sum_{x=1}^v exp(u_x^T v_c)u_x
∂Vc∂logw=1∑vexp(uwTvc)=∑w=1vexp(uwTvc)1x=1∑vexp(uxTvc)ux
注意在链式法则中求和随着链式延展要更换索引(w到x)。
所以最后损失函数的梯度为
∂
∂
V
c
l
o
g
(
p
(
o
∣
c
)
)
=
u
0
−
∑
x
=
1
v
e
x
p
(
u
x
T
v
c
)
u
x
∑
w
=
1
v
e
x
p
(
u
w
T
v
c
)
=
u
0
−
∑
x
=
1
v
p
(
x
∣
c
)
u
x
\frac{\partial}{\partial V_c}log (p(o|c) ) = u_0 - \sum_{x=1}^v\frac{ exp(u_x^T v_c)u_x}{\sum_{w=1}^v exp(u_w^T v_c)} = u_0-\sum_{x=1}^v p(x|c) u_x
∂Vc∂log(p(o∣c))=u0−x=1∑v∑w=1vexp(uwTvc)exp(uxTvc)ux=u0−x=1∑vp(x∣c)ux
再用刚刚的例子解释一遍这个结果:
我想吃苹果,也想吃__.
假设这句话的中心词是苹果抽到的周边词答案是梨子,
u
0
u_0
u0为梨子的向量表述,
p
(
x
∣
c
)
p(x|c)
p(x∣c)为
p
(
输
出
的
词
∣
苹
果
)
p(输出的词|苹果)
p(输出的词∣苹果)的概率,
v
v
v即所有可能的结果,
u
x
u_x
ux代表输出的词的向量表述,
∑
x
=
1
v
p
(
x
∣
c
)
u
x
\sum_{x=1}^v p(x|c) u_x
∑x=1vp(x∣c)ux即代表对所有可能的输出的结果的加权求和,即期望。损失函数可以解释为正确答案与输出答案的期望的差距。
最后使用随机梯度下降SGD进行更新,每次梯度下降只更新
W
W
W矩阵中的周边词对应的列向量(例如梨子、想、吃等),
W
′
W'
W′矩阵只更新中心词的列向量(苹果)。
END