谈谈谷歌word2vec的原理

word2vec

在NLP领域中,为了能表示人类的语言符号,一般会把这些符号转成一种数学向量形式以方便处理,我们把语言单词嵌入到向量空间中就叫词嵌入(word embedding)。谷歌开源的word2vec则是这么一种词嵌入工具,它能生成词向量,通过词向量可以很好地度量词与词之间的相似性。word2vec采用的模型包含了连续词袋模型(CBOW)和Skip-Gram模型。通过它可以在大数据量上进行高效训练从而得到词向量。

n-gram语言模型

在讨论词向量时先看NLP中很重要的统计语言模型,简单来说就是计算某个句子出现的概率,比如“我今天上班迟到了”这句话在整个语言下的概率,一般我们会通过一个大的语料库来进行统计。

用数学语言来描述,假设我们的句子为 s=w1,w2,...,wt ,则该句子的概率为

P(s)=P(w1,w2,...,wt)=P(w1)P(w2|w1)P(w3|w1,w2)...P(wt|w1,w2,...,wt1)

其中P(w1)表示第一个词w1出现的概率;P(w2|w1)是第一个词出现的前提下第二个词出现的概率;以此类推。比如s=“我今天上班迟到了”,那么P(s)=P(我)P(今天|我)P(上班|我,今天)P(迟到了|我,今天,上班)。

上面的语言模型的参数空间太大而且数据稀疏,导致在实际中基本无法使用。所以我们需要一个假设,某个词出现的概率只与它前面的一个或几个词有关,这个假设就是马尔科夫假设。有了它问题就变简单了,某个句子出现的概率就可以用下面表示(这里假设某词只与前面一个词相关)。

P(s)=P(w1,w2,...,wt)P(w1)P(w2|w1)P(w3|w2)...P(wn|wt1)

n-gram这种处理序列信息的方式依然存在局限性,比如当n大于3时基本就无法处理了,参数空间太大。另外它不能表示词与词之间的关联性。于是我们看下另外一种语言模型——神经网络语言模型,虽然它并不能完全解决n的问题,但它提供了另外一种语言模型思路。实际上,在深度学习中有另外的处理方式,比如RNN、LSTM等,可以克服n-gram中n无法取太大(即无法关联距离较远的词)的缺点。

one-hot形式的词向量

在继续了解神经网络语言模型之前我们先看怎么用向量来表示词,前面我们说过词向量就是用来表示人类语言的一种数学化的方式,其包含了很多种表示方式。其中最简单的向量方式即是one-hot形式。

它的处理方式简单粗暴,一般就是统计词库包含的所有V个词,然后将这V个词固定好顺序,然后每个词就可以用一个V维的稀疏向量来表示,向量中只有在该词出现的位置的元素才为1,其它元素全为0。比如下面这几个词,第一个元素为1的表示中国,第六个元素为1的表示美国,第五个元素为1的表示日本。

中国 [1,0,0,0,0,0,0,0,0,……,0,0,0,0,0,0,0]
美国 [0,0,0,0,0,1,0,0,0,……,0,0,0,0,0,0,0]
日本 [0,0,0,0,1,0,0,0,0,……,0,0,0,0,0,0,0]
   
   
   
   
  • 1
  • 2
  • 3

从中可以看到one-hot形式的维数通常会很大,因为词数量一般在10W级别,这会导致训练时难度大大增加,造成维数灾难。另外这么多维只以顺序信息并且只用1和0来表示单词,很浪费空间。再一个是这种方式的任意两个词都是孤立的,没法看出两个词之间的相似性。于是看看有没有改进的方法。

分布式词向量

鉴于one-hot形式词向量的缺点,出现了另外一种词向量表示方式——分布式词向量(distributed word representation)。 分布式词向量则干脆直接用普通的向量来表示词向量,而元素的值为任意实数,该向量的维数可以在事前确定,一般可以为50维或100维。这时的词向量类似如下(这里假设用5维来表示):

中国 [1.25, 0.2, 0.3, 0.5, 0.6]
美国 [0.1, 0.3, 0.5, 0.1, 1.5]
日本 [2.2, 0.2, 0.4, 0.6, 1.0]
   
   
   
   
  • 1
  • 2
  • 3

其中每个元素的具体数值则由训练来确定。这样一来就克服了在深度学习中可能的维度灾难,而且充分利用了空间,如果使用适当的训练方法训练出来的词向量可以直接根据两个词之间的距离来计算相似性。

神经网络语言模型

神经网络语言模型即用神经网络来训练语言模型,最经典的模型是Bengio等人提出的三层神经网络,它思路大概是对于语料C任意一个词w,取它的前n-1个词作为输入,这个跟n-gram的思路是同样的,而w则为它的输出,有了输入和输出就组成了训练样本了。

下面根据上图进行讲解,首先设每个词w用m维向量来表示,则每个词都有对应着自己的m维向量,另外设语料的总词数为v。接着分别看三层网络,

  • 输入层,输入层负责将前n-1个词向量输入,即将 C(wtn1),...,C(wt2),C(wt1) 输入,而他们的连接方式只是将他们首尾拼接起来即可,这样输入其实就是一个m(n-1)维向量,设为X。
  • 隐含层,该层节点数可自己设定,该层输出为 z=tanh(wx+p) ,其中w为输入层到隐含层的权重,p为偏置项。
  • 输出层,一共有v个节点,每个节点的值为对应词的概率,即下个词为该词的概率值,最后还会通过softmax激励函数对输出值归一化。y值的计算公式为 y=uz+q ,其中u是隐含层到输出层的权重,q为偏置项。

现在样本集及神经网络结构都有了,接下去使用梯度下降法对其进行优化训练就能得到一个语言模型。

通过神经网络训练出来的语言模型自带平滑,这个归功于我们的激活函数,而传统的n-gram方式则需要我们额外做平滑化处理。而且词向量还能表示词语的相似性。

Skip-gram和CBOW模型

神经网络语言模型解决了概率平滑、数据稀疏及维度灾难等问题,但它仍然无法彻底解决n的问题,即通过前n个词来预测接着的单词,这里认为神经网络语言模型无法取n大于5。另外一个是训练需要花费很多时间。

为了克服这些缺点,出现了CBOW模型和Skip-gram模型。它们都是通过对原始的神经网络语言模型进行改良。

另外CBOW模型和Skip-gram模型的输入向量中不再使用one-hot形式,而是可以假设一个m维向量,初始值可以随机设置,然后通过训练不断优化最后得到具体的向量值。这也就是为什么说我们在训练CBOW模型和Skip-gram模型时会产生一个词向量的副产品,该词向量是一个分布式词向量。

以CBOW模型为例

它对原始的神经网络语言模型做了一些改造,比如把原来的隐含层去掉了,投影层直接与输出层相连,这是因为隐含层增加了计算量,而且去掉后基本不会影响效果。投影层做的操作就是累加输入层的所有向量,再回想一下传统神经网络语言模型是怎么处理输入层的?就是向量的连接操作,将输入层的所有向量首尾相连接。这种方式也就意味着丢弃词语的序列信息,这很好理解,原来方式是输入向量首尾先连,而现在是做累和操作。

  • 输入层,对于词w(t),取前后若干个词作为输入,比如前后2个词,则w(t-2)、w(t-1)、t(t+1)、t(t+2)作为输入。
  • 投影层,对输入层所有向量做求和累加操作。
  • 输出层,是一棵庞大的很深的二叉树,如下图,树的叶子结点表示语料库中出现的词,比如w1 w2 w3 w4 …wv,语料库总共有v个不同的词,它其实是一棵哈夫曼树,通过语料库中词频而构建的,把词频当做权值然后按照哈夫曼树构造的算法进行构造即可,这个比较简单。 

CBOW模型的训练

训练其实就是根据某个词前后若干词来预测该词,这其实可以看成是多分类。最朴素的想法就是直接使用softmax来分别计算每个词的对应的归一化的概率。但对于动辄十几万词汇量的场景中使用softmax计算量太大,于是需要用一种二分类组合形式的hierarchical softmax,即输出层为一棵二叉树。

现在看看怎么来训练这棵二叉树。如下图,每个二叉树叶子结点表示一个词,非叶子结点则代表词,但它有一个权重参数 θ ,每个非叶子结点的权重参数都不同,另外再约定每个节点的左边的子节点编码为1,右边的子节点编码为0。那么从根节点开始(根节点不作编码),词“每天”的编码为10,词“运动”的编码为00。

现在假设正确输出为“每天”,对应的编码为10,那么具体是怎么更新参数的?实际上除了叶子结点,其他节点都可以看成是一个神经元,可以看到它是一个二分类的结构,那么通过logistic回归就可以实现二分类。为了找到“每天”这个词,首先通过根节点,经过logistic分类后应该分到左边子节点,即编码为1,接着继续进行logistic分类到右边子节点,即编码为0,然后得到最后的结果。

设w为某个词,w前后若干个词用 wm 表示,二叉树总共有s层,设从根节点到w节点经过的节点的参数为 θ1,θ2,...θs1 ,比如“每天”经过的参数为 θ1,θ2 ,再设从第二层节点到w节点经过的节点的编码为 b2,b3,...,bs ,则w的条件概率为,

p(w|wm)=si=2p(bi|xw,θi1)

我们知道除了叶子结点外,其他每个节点都是做logistic回归,有

p(bi|xw,θi1)=[σ(xwθi1)]1bi[1σ(xwθi1)]bi

为方便计算取对数似然函数作为目标函数,得到

T=wllog(si=2[σ(xwθi1)]1bi[1σ(xwθi1)]bi)

其中l为语料库,接着对目标函数做最大化优化,可以看到目标函数中有两个参数需要学习,一个是 θ 一个是输入x,x学习就是得到我们的词向量副产品,而 θ 则是CBOW模型的参数。分别对两者求偏导,有

Tθi1=wllog(si=2[σ(xwθi1)]1bi[1σ(xwθi1)]bi)θi1=[1biσ(xwθi1)]xw

有了偏导就可以用梯度上升法了,继续求另外一个偏导,有

Txw=wllog(si=2[σ(xwθi1)]1bi[1σ(xwθi1)]bi)xw=[1biσ(xwθi1)]θi1

这样,两个向量参数都可以通过梯度上升法更新训练了。


原文地址: http://blog.csdn.net/wangyangzhizhou/article/details/77073023

你可能感兴趣的:(Deep,Learning,Natural,Language,Processing,(NLP))