主要参考:html
http://www.cnblogs.com/Determined22/p/5804455.html
算法
http://www.cnblogs.com/Determined22/p/5807362.html
网络
http://blog.csdn.net/u014595019/article/details/51943428
机器学习
http://www.open-open.com/lib/view/open1426578842601.html
分布式
仍是先给出总结:函数
神经几率语言模型NPLM,训练语言模型并同时获得词表示工具

word2vec:CBOW / Skip-gram,直接以获得词表示为目标的模型。学习
CBOW(Continuous Bag-of-Words),用周围词预测中心词:
优化
Skip-gram,用中心词预测周围词:
ui
![[干货]神经网络语言模型详解](http://static.javashuo.com/static/loading.gif)
原始的CBOW / Skip-gram模型虽然去掉了NPLM中的隐藏层从而减小了耗时,但因为输出层仍然是softmax(),因此实际上依然“impractical”。
两种加速策略,一种是Hierarchical Softmax,另外一种是Negative Sampling。
http://www.cnblogs.com/Determined22/p/5804455.html
神经几率语言模型NPLM
近年来,基于神经网络来获得词表示的模型备受青睐。这类模型所获得的词的向量表示是分布式表示distributed representation,一般被称为word embedding(词嵌入;词向量)。这种基于预测(predict)的模型的超参数每每要多于基于计数(count)的模型,所以灵活性要强一些。下面就简要介绍利用神经网络来获得词表示的开山之做——神经几率语言模型(NPLM, Neural Probabilistic Language Model),经过训练语言模型,同时获得词表示。
语言模型是指一个词串
{wt}Tt=1=wT1=w1,w2,...,wT
{wt}t=1T=w1T=w1,w2,...,wT 是天然语言的几率
P(wT1)
P(w1T)。 词
wt
wt的下标
t
t 表示其是词串中的第
t
t 个词。根据乘法公式,有
P(w1,w2,...,wT)=P(w1)P(w2|w1)P(w3|w1,w2)...P(wT|w1,w2,...,wT−1)
P(w1,w2,...,wT)=P(w1)P(w2|w1)P(w3|w1,w2)...P(wT|w1,w2,...,wT−1)
所以要想计算出这个几率,那就要计算出
P(wt|w1,w2,...,wt−1),t∈{1,2,...,T}
P(wt|w1,w2,...,wt−1),t∈{1,2,...,T} :
P(wt|w1,w2,...,wt−1)=count(w1,w2,...,wt−1,wt)count(w1,w2,...,wt−1)
P(wt|w1,w2,...,wt−1)=count(w1,w2,...,wt−1,wt)count(w1,w2,...,wt−1)
count()是指词串在语料中出现的次数。暂且抛开数据稀疏(若是分子为零那么几率为零,这个零合理吗?若是分母为零,又怎么办?)不谈,若是词串的长度很长的话,这个计算会很是费时。n-gram模型是一种近似策略,做了一个马尔可夫假设:认为目标词
wt
wt 的条件几率只与其以前的
n−1
n−1 个词有关:
P(wt|w1,w2,...,wt−1)≈P(wt|wt−(n−1),wt−(n−2),...,wt−1)=count(wt−(n−1),wt−(n−2),...,wt−1,wt)count(wt−(n−1),wt−(n−2),...,wt−1)
P(wt|w1,w2,...,wt−1)≈P(wt|wt−(n−1),wt−(n−2),...,wt−1)=count(wt−(n−1),wt−(n−2),...,wt−1,wt)count(wt−(n−1),wt−(n−2),...,wt−1)
神经几率语言模型NPLM延续了n-gram的假设:认为目标词
wt
wt 的条件几率与其以前的
n−1
n−1 个词有关。但其在计算
P(wt|w1,w2,...,wt−1)
P(wt|w1,w2,...,wt−1) 时,则使用的是机器学习的套路,而不使用上面count()的方式。那么它是如何在训练语言模型的同时又获得了词表示的呢?

图片来源:参考资料[1],加了几个符号
设训练语料为
D
D ,提取出的词表为
V={w1−,w2−,...,w|V|−−−}
V={w1_,w2_,...,w|V|_} 。词
wi−
wi_ 的下标
i−
i_ 表示其是词表中的第
i
i 个词,区别于不带下划线的下标。大体说来,NPLM将语料中的一个词串
wtt−(n−1)
wt−(n−1)t 的目标词
wt
wt 以前的
n−1
n−1 个词的词向量(即word embedding,设维度为
m
m )按顺序首尾拼接获得一个“长”的列向量
x
x ,做为输入层(也就是说共
(n−1)m
(n−1)m 个神经元)。而后通过权重矩阵
Hh×(n−1)m
Hh×(n−1)m 来到隐层(神经元数为
h
h ),并用tanh函数激活。以后再通过权重矩阵
U|V|×h
U|V|×h 来到输出层(神经元数固然为
|V|
|V| ),并使用softmax()将其归一化为几率。另外存在一个从输入层直连输出层的权重矩阵
W|V|×(n−1)m
W|V|×(n−1)m 。因此网络的输出以下(隐层和输出层加了偏置):
z=Utanh(Hx+d)+b+Wx
z=Utanh(Hx+d)+b+Wx
y^i−=P(wi−|wt−(n−1),wt−(n−2),...,wt−1)=softmax(zi−)=expzi−∑k=1|V|expzk−,wi−∈V
y^i_=P(wi_|wt−(n−1),wt−(n−2),...,wt−1)=softmax(zi_)=expzi_∑k=1|V|expzk_,wi_∈V
y^i−
y^i_ 表示目标词是词表中第
i
i 个词
wi−
wi_ 的几率。
expzi−
expzi_ 表示前
n−1
n−1 个词对词表中第
i
i 个词
wi−
wi_ 的能量汇集。
词表中的每一个词的词向量都存在一个矩阵
C
C 中,look-up操做就是从矩阵中取出须要的词向量。由此能够看出,NPLM模型和传统神经网络的区别在于,传统神经网络须要学习的参数是权重和偏置;而NPLM模型除了须要学习权重和偏置外,还须要对输入(也就是词向量)进行学习。
那么,模型的参数就有:
C,U,H,W,b,d
C,U,H,W,b,d 。
对于目标词
wt
wt ,模型对它的损失为(使用对数损失函数时,经验风险最小化等价于极大似然估计;在本处,对数损失是交叉熵损失的一种特殊状况)
J=−logy^t=−logP(wt|wt−(n−1),wt−(n−2),...,wt−1)=−logsoftmax(zt)
J=−logy^t=−logP(wt|wt−(n−1),wt−(n−2),...,wt−1)=−logsoftmax(zt)
那么模型的经验风险为(省略了常系数)
J=−∑wtt−(n−1)∈Dlogy^t=−∑wtt−(n−1)∈DlogP(wt|wt−(n−1),wt−(n−2),...,wt−1)=−∑wtt−(n−1)∈Dlogsoftmax(zt)
J=−∑wt−(n−1)t∈Dlogy^t=−∑wt−(n−1)t∈DlogP(wt|wt−(n−1),wt−(n−2),...,wt−1)=−∑wt−(n−1)t∈Dlogsoftmax(zt)
因此接下来就可使用梯度降低等方法来迭代求取参数了。这样便同时训练了语言模型和词向量。
word2vec:CBOW / Skip-gram
上面介绍的NPLM以训练语言模型为目标,同时获得了词表示。2013年的开源工具包word2vec则包含了CBOW和Skip-gram这两个直接以获得词向量为目标的模型。
这里先介绍两种模型的没有加速策略的原始形式(也就是输出层是softmax的那种。对于Skip-gram模型,做者在paper中称之为“impractical”),两种加速策略将在下篇文中介绍。
与NPLM不一样,在CBOW / Skip-gram模型中,目标词
wt
wt 是一个词串中间的词而不是最后一个词,其拥有的上下文(context)为先后各
m
m 个词:
wt−m,...,wt−1,wt+1,...,wt+m
wt−m,...,wt−1,wt+1,...,wt+m 。NPLM基于n-gram,至关于目标词只有上文。后文中,“目标词”和“中心词”是同一律念,“周围词”和“上下文”是同一律念。
在CBOW / Skip-gram模型中,任一个词
wi−
wi_ 将获得两个word embedding(设维度为
n
n):做为中心词时的词向量,也称为输出词向量
vi−∈Rn×1
vi_∈Rn×1 ;以及做为周围词时的词向量,也称为输入词向量
ui−∈Rn×1
ui_∈Rn×1 。词向量的下标和词的下标相对应,好比说目标词
wt
wt 的词向量就对应为
vt
vt 和
ut
ut 。
与NPLM相似,词表中每一个词的词向量都存在一个矩阵中。因为存在两套词向量,所以就有两个矩阵:输入词矩阵
Vn×|V|=[v1−,...,v|V|−−−]
Vn×|V|=[v1_,...,v|V|_] ,其每一列都是一个词做为周围词时的词向量;输出词矩阵
U|V|×n=[u⊤1−;...;u⊤|V|−−−]
U|V|×n=[u1_⊤;...;u|V|_⊤] ,其每一行都是一个词做为中心词时的词向量。好比说若想取出词做为周围词时的词向量,只要知道词在词表中的编号便可,取出的操做至关于用输入词矩阵乘以词的one-hot representation。
(一)CBOW(Continuous Bag-of-Words)
不带加速的CBOW模型是一个两层结构,经过上下文来预测中心词——
输入层:
n
n 个节点,上下文共
2m
2m 个词的词向量的平均值;
输入层到输出层的链接边:输出词矩阵
U|V|×n
U|V|×n ;
输出层:
|V|
|V| 个节点。第
i
i 个节点表明中心词是词
wi−
wi_ 的几率。
若是要“看作”三层结构的话,能够认为——
输入层:
2m×|V|
2m×|V|个节点,上下文共
2m
2m 个词的one-hot representation
输入层到投影层到链接边:输入词矩阵
Vn×|V|
Vn×|V| ;
投影层::
n
n 个节点,上下文共
2m
2m 个词的词向量的平均值;
投影层到输出层的链接边:输出词矩阵
U|V|×n
U|V|×n ;
输出层:
|V|
|V| 个节点。第
i
i 个节点表明中心词是词
wi−
wi_ 的几率。
这样表述相对清楚,将one-hot到word embedding那一步描述了出来。但我的认为投影层不宜称为“隐层”,由于我以为隐层可能每每有加非线性的意思,而这里没有。

图片来源:参考资料[5],把记号都改为和本文一致
首先,将中心词
wt
wt 的上下文
ct
ct :
wt−m,...,wt−1,wt+1,...,wt+m
wt−m,...,wt−1,wt+1,...,wt+m 由one-hot representation(
xt+j
xt+j )转为输入词向量(
vt+j
vt+j ):
vt+j=Vxt+j,j∈{−m,...,m}∖{0}
vt+j=Vxt+j,j∈{−m,...,m}∖{0}
进而将上下文的输入词向量
vt−m,...,vt−1,vt+1,...,vt+m
vt−m,...,vt−1,vt+1,...,vt+m 求平均值,做为模型输入:
v^t=12m∑jvt+j,j∈{−m,...,m}∖{0}
v^t=12m∑jvt+j,j∈{−m,...,m}∖{0}
这一步叫投影(projection)。能够看出,CBOW像词袋模型(BOW)同样抛弃了词序信息。丢掉词序看起来不太好,不过开个玩笑的话:“研表究明,汉字的序顺并不定一能影阅响读,事证明明了当你看这完句话以后才发字现都乱是的”。
与NPLM不一样,CBOW模型没有隐藏层,投影以后就用softmax()输出目标词是某个词的几率,进而减小了计算时间:
z=Uv^t
z=Uv^t
y^i−=P(wi−|wt−m,...,wt−1,wt+1,...,wt+m)=softmax(zi−)=softmax(u⊤i−v^t),wi−∈V
y^i_=P(wi_|wt−m,...,wt−1,wt+1,...,wt+m)=softmax(zi_)=softmax(ui_⊤v^t),wi_∈V
那么模型的参数就是两个词向量矩阵:
U,V
U,V 。
对于中心词
wt
wt ,模型对它的损失为
J=−logy^t=−logP(wt|wt−m,...,wt−1,wt+1,...,wt+m)=−logsoftmax(zt)=−logexp(u⊤tv^t)∑|V|k=1exp(u⊤k−v^t)=−u⊤tv^t+log∑k=1|V|exp(u⊤k−v^t)=−zt+log∑k=1|V|expzk−
J=−logy^t=−logP(wt|wt−m,...,wt−1,wt+1,...,wt+m)=−logsoftmax(zt)=−logexp(ut⊤v^t)∑k=1|V|exp(uk_⊤v^t)=−ut⊤v^t+log∑k=1|V|exp(uk_⊤v^t)=−zt+log∑k=1|V|expzk_
因此模型的经验风险为
J=−∑wt+mt−m∈Dlogyt^=−∑wt+mt−m∈DlogP(wt|wt−m,...,wt−1,wt+1,...,wt+m)=−∑wt+mt−m∈Dlogsoftmax(zt)
J=−∑wt−mt+m∈Dlogyt^=−∑wt−mt+m∈DlogP(wt|wt−m,...,wt−1,wt+1,...,wt+m)=−∑wt−mt+m∈Dlogsoftmax(zt)
下面开始是很是无聊的求导练习。。。
若是用SGD来更新参数的话,只需求出模型对一个样本的损失的梯度。也就是说上式的求和号能够没有,直接对
J
J 求梯度,来更新参数。
I. 首先是对输出词矩阵
U⊤=[u1−,...,u|V|−−−]
U⊤=[u1_,...,u|V|_] :
由于
zi−=u⊤i−v^t
zi_=ui_⊤v^t ,因此
∂J∂ui−=∂zi−∂ui−∂J∂zi−=v^t∂J∂zi−
∂J∂ui_=∂zi_∂ui_∂J∂zi_=v^t∂J∂zi_ (这里的
∂J∂zi−
∂J∂zi_ 有点像BP算法中的
δ
δ ),那么先求
∂J∂zi−
∂J∂zi_ :
(1) 对
∀wi−∈V∖{wt}
∀wi_∈V∖{wt} ,有
yi−=0
yi_=0,那么
∂J∂zi−=∂(−zt+log∑|V|k=1expzk−)∂zi=0+∂∑|V|k=1expzk−∂zi−∑k=1|V|expzk−=expzi−∑k=1|V|expzk−=y^i−=y^i−−yi−
∂J∂zi_=∂(−zt+log∑k=1|V|expzk_)∂zi=0+∂∑k=1|V|expzk_∂zi_∑k=1|V|expzk_=expzi_∑k=1|V|expzk_=y^i_=y^i_−yi_
(2) 对
wi−=wt
wi_=wt ,有
yi−=1
yi_=1,那么
∂J∂zi−=∂J∂zt=−1+y^t=y^i−−yi−
∂J∂zi_=∂J∂zt=−1+y^t=y^i_−yi_
可见两种情形的结果是统一的。
所以有
∂J∂ui−=(y^i