word2vec原理剖析

本文根据word2vec 中的数学原理详解整理而成。
根据word2vec算法的原理,大概总结如下;
1) 由统计语言模型发展到n-gram模型,再由n-gram模型发展到NNLM模型,最后到word2vec模型;
2) word2vec模型包括CBOW模型和Skip-gram模型;
3) 对于CBOW模型和Skip-gram模型,又分别有基于Hierarchical Softmax的模型和基于Negative Sampling的模型
一 模型发展历程
1. 统计语言模型
统计语言模型是用来计算一个句子出现的概率的模型,它通常基于一个语料库来构建。假设W=w1T:=(w1,w2,...,wT)表示由T个词w1,w2,...,wT按顺序构成的一个句子,则w1,w2,...,wT的联合概率

p(W)=p(w1T)=p(w1,w2,...,wT)

即为句子W的出现的概率。利用Bayes公式(P(A|B)=P(AB)P(B)),上式可以被链式分解为:
p(w1T)=p(w1)p(w2|w1)p(w3|w12)p(wT|w1T1)             (1)

其中(条件)概率p(w1),p(w2|w1),p(w3|w12),,p(wT|w1T1)就是语言模型的参数,计算这些参数,就可以得到一个句子出现的概率了。
原理很简单,但计算很复杂。假设语料库对应词典D的大小(即词汇量)为N,那么,如果考虑长度为T的任意句子,理论上就有NT种可能,而每种可能都要计算T个参数,总共就需要计算TNT个参数(不考虑重复参数的情况下),这个计算量是非常大的。
那么,这些参数如何计算呢?常见的方法有n-gram模型,神经网络等方法。首先来看n-gram模型。

2. n-gram模型
考虑p(wk|w1k1)(k>1)的近似计算。利用Bayes公式,有

p(wk|w1k1)=p(w1k)p(w1k1),

根据大数定理,当语料库足够大时,p(wk|w1k1)可近似表示为
p(wk|w1k1)count(w1k)count(C)count(w1k1)count(C)count(w1k)count(w1k1),             (2)

其中count(w1k)count(w1k1)分别表示词串w1kw1k1在语料中出现的次数,count(C)表示语料库中的单词数量。当k很大时,count(w1k)count(w1k1)的统计将会非常耗时。
在统计语言模型中,我们认为一个词出现的概率和它前面的所有词都有关(如公式(1))。n-gram模型的基本思想是:假定一个词出现的概率只与它前面固定数目的词相关。它做了一个n-1阶的Markov假设,认为一个词出现的概率就只与它前面的n-1个词相关,即
p(wk|w1k1)=p(wk|wkn+1k1)

因此公式(2)就变成了
p(wk|w1k1)count(wkn+1k)count(wkn+1k1)

以n=2时为例,可以得到:
p(wk|w1k1)count(wk1,wk)count(wk1).

通过这样简化,可以大大减少要统计的参数数量(统计时需要匹配的词串更短)。
至于n-gram中的参数n的取值,请看下图(图片来源:word2vec 中的数学原理详解
word2vec原理剖析
另外,n-gram模型中还需要做平滑化处理。
训练语料库中有些n元组没有出现过,其对应的条件概率就是0,导致一整句话的概率为0。平滑化处理最简单的方法Laplace平滑:把每个n元组的出现次数加1,那么原来出现k次的某个n元组就会被记为k+1次,原来出现0次的n元组就会被记为1次。
总结起来,n-gram模型的主要工作就是在语料中统计各种词串出现的次数以及平滑化处理。概率值计算好之后就存储起来,下次需要计算一个句子的概率时,只需要找到相关的概率参数,将它们连乘起来就好了。

3. 神经网络语言模型(Neural Network Language Model)
神经网络语言模型涉及到一个词向量的概念:对词典D中的任意词w,指定一个固定长度的向量v(w)Rm,v(w)就称为w的词向量,m为词向量的长度。
神经网络语言模型的框架如下图所示(图片来源:《A Neural Probabilistic Language Model》):
word2vec原理剖析
对于语料C中的任意一个词w,将Context(w)取为其前面的n-1个词(类似于n-gram),这样二元对(Context(w),w)就是一个训练样本了。一旦语料C和词向量长度m给定后,输入层和输出层的规模就确定了,前者为(n1)m,后者为N=|D|,即语料C的词汇量大小,而隐藏层的大小nh是可调参数,由用户指定。
输入层的向量xw是这样构造的:将输入层的n-1个词向量按顺序首尾相接地拼起来形成一个长向量,因此其长度就是(n1)m。因此,NNLM模型的框架实现过程如下:

{zw=tanh(Wxw+p),yw=Uzw+q,p(w|Context(w))=eyw,iwi=1Neyw,i

NNLM模型相对n-gram模型的优势是:
(1)词语之间的相似性可以由词向量来体现;
(2)不需要再做平滑化处理(softmax函数保证p(w|Context(w))不会为0).
那么,NNLM模型待确定的参数有哪些呢?
(1)词向量:v(w)Rm,wD以及填充向量;
(2)神经网络参数:WRnh×(n1)m,pRnh;URN×nh,qRN
这些参数都可以通过训练得到。要特别注意的是,通常的机器学习算法中,输入都是已知的,而在NNLM中,输入v(w)也需要通过训练才能得到。
接下来我们分析一下模型的运算量。输入层,隐藏层,输出层的规模分别是(n1)m,nh,N,其中:
(1)n是一个词的上下文中包含的词数,通常不超过5;
(2)m是词向量长度,通常是10-100的量级;
(3)nh由用户指定,通常不需要取得太大,如100量级;
(4)N是语料库词汇量的大小,与语料库有关,通常是104105量级。
通过分析,我们可以发现,整个模型的大部分计算集中在隐藏层和输出层之间的矩阵向量运算,以及输出层上的softmax归一化处理。word2vec就是针对这部分进行优化。

4. word2vec模型
word2vec包括两个重要的模型:CBOW模型(Continuous Bag-of-Words Model)和Skip-gram模型(Continuous Skip-gram Model),如下图所示(图片来源:word2vec 中的数学原理详解):
word2vec原理剖析
两个模型都包含三层:输入层,投影层和输出层。CBOW模型是在已知当前词wt的上下文wt2,wt1,wt+1,wt+2的前提下预测当前词wt;而Skip-gram模型与其相反,是在已知当前词wt的前提下,预测其上下文wt2,wt1,wt+1,wt+2.

二 基于Hierarchical Softmax的模型
基于Hierarchical Softmax的模型跟NNLM模型的一个比较大的区别是在输出层采用了基于Huffman树的树形结构,因此,在讲基于Hierarchical Softmax的模型之前,先简单介绍一下Huffman树。
结点的权和带权路径长度:若为树中结点赋予一个具有某种含义的非负数值,则这个数值称为该结点的权。结点的带权路径长度是指,从根结点到该结点之间的路径长度与该结点的权的乘积。
具体Huffman树的构造请看下图(图片来源:word2vec 中的数学原理详解):
word2vec原理剖析
2.1 CBOW模型
CBOW模型的网络结构如下图所示(图片来源:word2vec 中的数学原理详解),它包含三层:输入层,投影层和输出层。下面以样本(Context(w),w)为例(这里假设Context(w)由w前后各c个词构成),对这三个层做简单说明:
(1)输入层:包含Context(w)中2c个词的词向量v(Context(w)1),v(Context(w)2),...,v(Context(w)2c)Rm.m表示词向量的长度;
(2)投影层:将输入层的2c个向量做求和累加,即xw=i=12cv(Context(w)iRm.
(3)输出层:输出层对应一棵Huffman树,它以语料库中出现过的词当叶子结点,以各词再语料库中出现的次数当权值构造出来的Huffman树。在这棵Huffman树中,叶子结点共N(=|D|)个,分别对应词典D中的词,非叶子结点N-1个(图中标成黄色的那些结点)。
word2vec原理剖析

CBOW与NNLM的不同之处:
(1)(输入层):NNLM通过前后拼接,CBOW通过累加求和;
(2)(隐藏层):NNLM有,CBOW没有;
(3)(输出层):NNLM线性结构,CBOW树形结构。
因此,CBOW模型通过去除隐藏层以及把输出层改用Huffman树,极大地降低了计算复杂度。
接下来就是梯度计算了。以下几张图是在计算梯度之前,对整个CBOW框架的分析,主要是如何使用Hierarchical Softmax技术进行概率计算,原作者已经描述得非常好了,我们直接看图:
word2vec原理剖析
word2vec原理剖析
word2vec原理剖析
word2vec原理剖析
逻辑理完,接下来开始码公式了。哈哈,肯定有人会问,贴了这么多图,为什么不继续贴呢?(我承认前面偷懒了。。)其实这里就是想自己写写公式,这样理解得深刻一些~
对于CBOW模型,我们优化的目标函数为如下的对数似然函数:

L=wClog p(w|Context(w))

条件概率p(w|Context(w))的一般公式可写为:
p(w|Context(w))=j=2lwp(djw|xw,θj1w),

其中
p(djw|xw,θj1w)={σ(xwTθj1w),djw=0;(即被分为正类的概率)1σ(xwTθj1w),djw=1;(即被分为负类的概率)

写成整体表达式:
p(djw|xw,θj1w)=[σ(xwTθj1w)]1djw[1σ(xwTθj1w)]djw

将上式代入对数似然函数中,可得:
(57)L=wClog j=2lw{[σ(xwTθj1w)]1djw[1σ(xwTθj1w)]djw}(58)=wCj=2lw{(1djw)log[σ(xwTθj1w)]+djwlog[1σ(xwTθj1w)]}

为方便求导,将花括号里的内容简记为L(w,j),即
L(w,j)=(1djw)log[σ(xwTθj1w)]+djwlog[1σ(xwTθj1w)].

以上的L便是CBOW模型的目标函数,优化过程就是最大化这个目标函数,word2vec中使用随机梯度上升法:每取一个样本(Context(w),w),就对目标函数中的所有参数做一次更新。目标函数L中的参数包括向量xw,θj1w,wC,j=2,...,lw.首先考虑L(w,j)关于θj1w的梯度计算:
(59)L(w,j)θj1w=θj1w{(1djw)log[σ(xwTθj1w)]+djwlog[1σ(xwTθj1w)]}(60)=(1djw)[1σ(xwTθj1w)]xwdjwσ(xwTθj1w)xw(61)=[1djwσ(xwTθj1w)]xw.

因此,θj1w的更新公式为:
θj1w:=θj1w+α[1djwσ(xwTθj1w)]xw,

其中α表示学习率。
观察L(w,j)可知变量xwθj1w是对称的(即两者可交换位置),因此,我们可以得到L(w,j)对于xw的梯度:
L(w,j)xw=[1djwσ(xwTθj1w)]θj1w.

那么问题来了,我们的最终目的是要求词典D中每个词的词向量,而xw表示的是Context(w)中各词词向量的累加。那么,如何利用L(w,j)xw来对v(w^),w^Context(w)进行更新呢?word2vec中的做法很简单,如下:
v(w^):=v(w^)+αj=2lwL(w,j)xw,w^Context(w)

即把j=2lwL(w,j)xw贡献到Context(w)中每个词的词向量上。这个很好理解,既然xw本身就是Context(w)中各词词向量的累加,求完梯度后当然也应该将其贡献到每个分量上去。(当然,个人认为使用平均贡献会更合理,即v(w^):=v(w^)+α|Context(w)|j=2lwL(w,j)xw,w^Context(w),其中|Context(w)|表示 Context(w)中词的个数)。
下面贴出原作者提供的伪代码:
word2vec原理剖析

2.2 Skip-gram模型
由上文我们知道,Skip-gram模型是由当前词预测其上下文词的模型,因此,Skip-gram模型的输入层只由当前样本的中心词w的词向量v(w)Rm.输出层和CBOW模型一样,也是一棵Huffman树。
梯度计算:
对于Skip-gram模型,已知的是当前词w,需要对其上下文Context(w)中的词进行预测,因此目标函数如下:

L=wClog p(Context(w)|w)

其中,条件概率
p(Context(w)|w)=uContext(w)p(u|w),

其中,p(u|w)可按照上文介绍的Hierarchical Softmax思想,即
p(u|w)=j=2lwp(dju|v(w),θj1u),

其中
p(dju|v(w),θj1u)=[σ(v(w)Tθj1u)]1dju[1σ(v(w)Tθj1u)]dju.

依次代回,可得到对数似然函数L的具体表达式为:
(62)L=wClog uContext(w)j=2lw{[σ(v(w)Tθj1u)]1dju[1σ(v(w)Tθj1u)]dju}(63)=wClog uContext(w)j=2lw{(1dju)log[σ(v(w)Tθj1u)]+dju[1σ(v(w)Tθj1u)]}

同样,为了方便推导,将三重求和符号下花括号里的内容简记为L(w,u,j),即
L(w,u,j)=(1dju)log[σ(v(w)Tθj1u)]+dju[1σ(v(w)Tθj1u)].

至于梯度计算部分,与CBOW模型对应部分的推导完全类似,请大家自行推导,这里直接给出推导结果如下:
L(w,u,j)θj1u=[1djuσ(v(w)Tθj1u)]v(w).

于是,θj1u的更新公式可写为:
θj1u:=θj1u+α[1djuσ(v(w)Tθj1u)]v(w).

同样,利用L(w,u,j)v(w)θj1u的对称行,可以得到L(w,u,j)关于v(w)的梯度:
L(w,u,j)v(w)=[1djuσ(v(w)Tθj1u)]θj1u.

于是,v(w)的更新公式可写为:
v(w):=v(w)+αuContext(w)j=2lwL(w,u,j)v(w)

同样,给出原作者提供的伪代码:
word2vec原理剖析
三 基于Negative Sampling的模型
这部分将介绍基于Negative Sampling(简称NEG)的CBOW和Skip-gram模型。与Hierarchical Softmax 相比,NEG不再使用复杂的Huffman树,而是利用相对简单的随机负采样,能大幅度提升性能,因而可作为Hierarchical Softmax 的一种代替。

3.1 CBOW模型
在CBOW模型中,已知词w的上下文Context(w),需要预测w,因此,对于给定的Context(w),词w就是一个正样本,语料库中的其它词就是负样本了。首先必须明确,一个词对应一个负样本子集,也就是说,每训练一个正样本,都会生成一个负样本子集。至于负样本那么多,该如何选取的问题,后面会讲到。
假设现在已经选好了一个关于w的负样本子集NEG(w),且对w^D,定义

Lw(w^)={1,w^=w;0,w^w;

表示词w^的标签,即正样本的标签为1,负样本的标签为0.
对于一个给定的正样本(Context(w),w),我们希望最大化
g(w)=u{w}NEG(w)p(u|Context(w)),

其中
p(u|Context(w))={σ(xwTθu),Lw(u)=1;1σ(xwTθu),Lw(u)=0;

写成整体表达式如下:
p(u|Context(w))=[σ(xwTθu)]Lw(u)[1σ(xwTθu)]1Lw(u)

这里xw仍表示Context(w)中各词的词向量之和,而θuRm表示词u对应的一个辅助向量,为待训练的参数。必须明确的是,每个词训练一个θ,也就是说,相同正样本迭代的是同个θ,但不同正样本迭代的是不同的这里的θ,所以最终训练完的theta是个二维矩阵,大小为:词向量的大小*词个数。这是与基于Hierarchical Softmax的模型不同的地方,Huffman树的每个非叶子结点只有一个theta。
对于一个给定的语料库C,函数
G=wCg(w)

就可以作为整体优化的目标。取对数似然,可得最终的目标函数为:
L=logG=log wCg(w)=log wCg(w)

把上文的g(w)代入即可。得到目标函数后,优化方法还是梯度上升法,跟上一节的方法是类似的,这里就不赘述了。

3.2 Skip-gram模型
接下来介绍基于Negative Sampling 的Skip-gram模型。首先,我们将优化函数由原来的

G=wCg(w)

改写为:
G=wCuContext(w)g(u),

这里,uContext(w)g(u)表示对于一个给定的样本(w,Context(w)),我们希望最大化的量,g(u)类似于上一节的g(w),定义为:
g(u)=z{u}NEG(u)p(z|w),

其中,NEG(u)表示处理词u时生成的负样本子集,条件概率
p(z|w)={σ(v(w)Tθz),Lu(z)=1 (z=u);1σ(v(w)Tθz),Lu(z)=0 (zu);

写成整体表达式:
p(z|w)=[σ(v(w)Tθz)]Lu(z)[1σ(v(w)Tθz)]1Lu(z).

对G取对数,即可得到最终的目标函数为:
L=logG=wCuContext(w)z{u}NEG(u)log p(z|w),

p(z|w)的表达式代入即可。
接下来的梯度计算和参数优化跟前面的都一样,同样不再赘述。

3.3 负采样算法
在基于Negative Sampling 的CBOW和Skip-gram的模型中,负采样是一个很重要的环节,对于一个给定的词w,如何生成负样本子集NEG(W)呢?其实,这本质上是一个带权采样的问题,高频词被选为负样本的概率就比较高,低频词被选中的概率就比较小。原作者提供了word2vec的具体做法,直接贴图:
word2vec原理剖析