Word2vec

  word2vec是google在2013年推出的一个NLP工具,它的特点是将所有的词向量化,这样词与词之间就可以定量的去度量他们之间的关系,挖掘词之间的联系。虽然源码是开源的,但是谷歌的代码库国内无法访问,因此本文的讲解word2vec原理以Github上的word2vec代码为准。本文关注于word2vec的基础知识。

1. 词向量基础

  用词向量来表示词并不是word2vec的首创,在很久之前就出现了。最早的词向量是很冗长的,它使用是词向量维度大小为整个词汇表的大小,对于每个具体的词汇表中的词,将对应的位置置为1。比如我们有下面的5个词组成的词汇表,词"Queen"的序号为2, 那么它的词向量就是(0,1,0,0,0)(0,1,0,0,0)。同样的道理,词"Woman"的词向量就是(0,0,0,1,0)(0,0,0,1,0)。这种词向量的编码方式我们一般叫做1-of-N representation或者one hot representation.

   One hot representation用来表示词向量非常简单,但是却有很多问题。最大的问题是我们的词汇表一般都非常大,比如达到百万级别,这样每个词都用百万维的向量来表示简直是内存的灾难。这样的向量其实除了一个位置是1,其余的位置全部都是0,表达的效率不高,能不能把词向量的维度变小呢?

  Dristributed representation可以解决One hot representation的问题,它的思路是通过训练,将每个词都映射到一个较短的词向量上来。所有的这些词向量就构成了向量空间,进而可以用普通的统计学的方法来研究词与词之间的关系。这个较短的词向量维度是多大呢?这个一般需要我们在训练时自己来指定。

  比如下图我们将词汇表里的词用"Royalty","Masculinity", "Femininity"和"Age"4个维度来表示,King这个词对应的词向量可能是(0.99,0.99,0.05,0.7)(0.99,0.99,0.05,0.7)。当然在实际情况中,我们并不能对词向量的每个维度做一个很好的解释。

 

  有了用Dristributed representation表示的较短的词向量,我们就可以较容易的分析词之间的关系了,比如我们将词的维度降维到2维,有一个有趣的研究表明,用下图的词向量表示我们的词时,我们可以发现:

 

 

   可见我们只要得到了词汇表里所有词对应的词向量,那么我们就可以做很多有趣的事情了。不过,怎么训练得到合适的词向量呢?一个很常见的方法是使用神经网络语言模型。

2、计算机中词的离散表示 one-hot和 BOW

  每个语料库中的词都对应一个词典中的映射,词典中对每个词都有编号,在语料库中词在计算机中都用one-hot的表示模型以对应词典中的编号。

  但 无法衡量词与词之间的关系。

  文档的向量表示可以直接将各词的词向量表示加和。

      也可以采用 Bi-gram和N-gram 模型对词进行表示,相对于BOW 考虑了词的顺序。但由于组合使得词表膨胀。

3、NNLP模型

   算法流程

   1、(N-1)个前向词:one-hot表示
   2、采用线性映射将one-hot表示投影到稠密D维表示
   3、输出层:Softmax
   4、各层权重最优化:BP+SGD词典

这个模型先是词向量拼接 然后经过了一个隐层和一个全连接层的输出  导致的计算量过大。故对此进行了优化产生了CBOW和Skip-Gram模型。

4. CBOW与Skip-Gram用于神经网络语言模型

  在word2vec出现之前,已经有用神经网络DNN来用训练词向量进而处理词与词之间的关系了。采用的方法一般是一个三层的神经网络结构(当然也可以多层),分为输入层,隐藏层和输出层(softmax层)。

  这个模型是如何定义数据的输入和输出呢?一般分为CBOW(Continuous Bag-of-Words 与Skip-Gram两种模型。

  CBOW模型的训练输入是某一个特征词的上下文相关的词对应的词向量,而输出就是这特定的一个词的词向量。比如下面这段话,我们的上下文大小取值为4,特定的这个词是"Learning",也就是我们需要的输出词向量,上下文对应的词有8个,前后各4个,这8个词是我们模型的输入。由于CBOW使用的是词袋模型,因此这8个词都是平等的,也就是不考虑他们和我们关注的词之间的距离大小,只要在我们上下文之内即可。

  这样我们这个CBOW的例子里,我们的输入是8个词向量,输出是所有词的softmax概率(训练的目标是期望训练样本特定词对应的softmax概率最大),对应的CBOW神经网络模型输入层有8个神经元,输出层有词汇表大小个神经元。隐藏层的神经元个数我们可以自己指定。通过DNN的反向传播算法,我们可以求出DNN模型的参数,同时得到所有的词对应的词向量。这样当我们有新的需求,要求出某8个词对应的最可能的输出中心词时,我们可以通过一次DNN前向传播算法并通过softmax激活函数找到概率最大的词对应的神经元即可。

    
   Skip-Gram模型和CBOW的思路是反着来的,即输入是特定的一个词的词向量,而输出是特定词对应的上下文词向量。还是上面的例子,我们的上下文大小取值为4, 特定的这个词"Learning"是我们的输入,而这8个上下文词是我们的输出。

  这样我们这个Skip-Gram的例子里,我们的输入是特定词, 输出是softmax概率排前8的8个词,对应的Skip-Gram神经网络模型输入层有1个神经元,输出层有词汇表大小个神经元。隐藏层的神经元个数我们可以自己指定。通过DNN的反向传播算法,我们可以求出DNN模型的参数,同时得到所有的词对应的词向量。这样当我们有新的需求,要求出某1个词对应的最可能的8个上下文词时,我们可以通过一次DNN前向传播算法得到概率大小排前8的softmax概率对应的神经元所对应的词即可。

  以上就是神经网络语言模型中如何用CBOW与Skip-Gram来训练模型与得到词向量的大概过程。但是这和word2vec中用CBOW与Skip-Gram来训练模型与得到词向量的过程有很多的不同。

  word2vec为什么 不用现成的DNN模型,要继续优化出新方法呢?最主要的问题是DNN模型的这个处理过程非常耗时。我们的词汇表一般在百万级别以上,这意味着我们DNN的输出层需要进行softmax计算各个词的输出概率的的计算量很大。有没有简化一点点的方法呢?

5. word2vec基础之霍夫曼树

   word2vec也使用了CBOW与Skip-Gram来训练模型与得到词向量,但是并没有使用传统的DNN模型。最先优化使用的数据结构是用霍夫曼树来代替隐藏层和输出层的神经元,霍夫曼树的叶子节点起到输出层神经元的作用,叶子节点的个数即为词汇表的小大。 而内部节点则起到隐藏层神经元的作用。

   具体如何用霍夫曼树来进行CBOW和Skip-Gram的训练我们在下一节讲,这里我们先复习下霍夫曼树。

   霍夫曼树的建立其实并不难,过程如下:

   输入:权值为(w1,w2,...wn)(w1,w2,...wn)的nn个节点

   输出:对应的霍夫曼树

   1)将(w1,w2,...wn)(w1,w2,...wn)看做是有nn棵树的森林,每个树仅有一个节点。

   2)在森林中选择根节点权值最小的两棵树进行合并,得到一个新的树,这两颗树分布作为新树的左右子树。新树的根节点权重为左右子树的根节点权重之和。

   3) 将之前的根节点权值最小的两棵树从森林删除,并把新树加入森林。

   4)重复步骤2)和3)直到森林里只有一棵树为止。

  下面我们用一个具体的例子来说明霍夫曼树建立的过程,我们有(a,b,c,d,e,f)共6个节点,节点的权值分布是(16,4,8,6,20,3)。

  首先是最小的b和f合并,得到的新树根节点权重是7.此时森林里5棵树,根节点权重分别是16,8,6,20,7。此时根节点权重最小的6,7合并,得到新子树,依次类推,最终得到下面的霍夫曼树。

   那么霍夫曼树有什么好处呢?一般得到霍夫曼树后我们会对叶子节点进行霍夫曼编码,由于权重高的叶子节点越靠近根节点,而权重低的叶子节点会远离根节点,这样我们的高权重节点编码值较短,而低权重值编码值较长。这保证的树的带权路径最短,也符合我们的信息论,即我们希望越常用的词拥有更短的编码。如何编码呢?一般对于一个霍夫曼树的节点(根节点除外),可以约定左子树编码为0,右子树编码为1.如上图,则可以得到c的编码是00。

  在word2vec中,约定编码方式和上面的例子相反,即约定左子树编码为1,右子树编码为0,同时约定左子树的权重不小于右子树的权重。

  word2vec有两种改进方法,一种是基于Hierarchical Softmax的,另一种是基于Negative Sampling的。本文关注于基于Hierarchical Softmax的改进方法。

6. 基于Hierarchical Softmax的模型概述

  我们先回顾下传统的神经网络词向量语言模型,里面一般有三层,输入层(词向量),隐藏层和输出层(softmax层)。里面最大的问题在于从隐藏层到输出的softmax层的计算量很大,因为要计算所有词的softmax概率,再去找概率最大的值。这个模型如下图所示。其中VV是词汇表的大小,

 

  word2vec对这个模型做了改进,首先,对于从输入层到隐藏层的映射,没有采取神经网络的线性变换加激活函数的方法,而是采用简单的对所有输入词向量求和并取平均的方法。比如输入的是三个4维词向量:(1,2,3,4),(9,6,11,8),(5,10,7,12)(1,2,3,4),(9,6,11,8),(5,10,7,12),那么我们word2vec映射后的词向量就是(5,6,7,8)(5,6,7,8)。由于这里是从多个词向量变成了一个词向量。

  第二个改进就是从隐藏层到输出的softmax层这里的计算量个改进。为了避免要计算所有词的softmax概率,word2vec采样了霍夫曼树来代替从隐藏层到输出softmax层的映射。我们在上一节已经介绍了霍夫曼树的原理。如何映射呢?这里就是理解word2vec的关键所在了。

  由于我们把之前所有都要计算的从输出softmax层的概率计算变成了一颗二叉霍夫曼树,那么我们的softmax概率计算只需要沿着树形结构进行就可以了。如下图所示,我们可以沿着霍夫曼树从根节点一直走到我们的叶子节点的词w2w2。

 

  和之前的神经网络语言模型相比,我们的霍夫曼树的所有内部节点就类似之前神经网络隐藏层的神经元,其中,根节点的词向量对应我们的投影后的词向量,而所有叶子节点就类似于之前神经网络softmax输出层的神经元,叶子节点的个数就是词汇表的大小。在霍夫曼树中,隐藏层到输出层的softmax映射不是一下子完成的,而是沿着霍夫曼树一步步完成的,因此这种softmax取名为"Hierarchical Softmax"。

  如何“沿着霍夫曼树一步步完成”呢?在word2vec中,我们采用了二元逻辑回归的方法,即规定沿着左子树走,那么就是负类(霍夫曼树编码1),沿着右子树走,那么就是正类(霍夫曼树编码0)。判别正类和负类的方法是使用sigmoid函数。

  其中xwxw是当前内部节点的词向量,而θθ则是我们需要从训练样本求出的逻辑回归的模型参数。

  使用霍夫曼树有什么好处呢?首先,由于是二叉树,之前计算量为VV,现在变成了log2Vlog2V。第二,由于使用霍夫曼树是高频的词靠近树根,这样高频词需要更少的时间会被找到,这符合我们的贪心优化思想。

  容易理解,被划分为左子树而成为负类的概率为P()=1P(+)P(−)=1−P(+)。在某一个内部节点,要判断是沿左子树还是右子树走的标准就是看P(),P(+)P(−),P(+)谁的概率值大。而控制P(),P(+)P(−),P(+)谁的概率值大的因素一个是当前节点的词向量,另一个是当前节点的模型参数θθ。

  对于上图中的w2w2,如果它是一个训练样本的输出,那么我们期望对于里面的隐藏节点n(w2,1)n(w2,1)的P()P(−)概率大,n(w2,2)n(w2,2)的P()P(−)概率大,n(w2,3)n(w2,3)的P(+)P(+)概率大。

  回到基于Hierarchical Softmax的word2vec本身,我们的目标就是找到合适的所有节点的词向量和所有内部节点θθ, 使训练样本达到最大似然。那么如何达到最大似然呢?

7. 基于Hierarchical Softmax的CBOW模型

  由于word2vec有两种模型:CBOW和Skip-Gram,我们先看看基于CBOW模型时, Hierarchical Softmax如何使用。

  首先我们要定义词向量的维度大小M,以及CBOW的上下文大小2c2c,这样我们对于训练样本中的每一个词,其前面的cc个词和后面的cc个词作为了CBOW模型的输入,该词本身作为样本的输出,期望softmax概率最大。

    在做CBOW模型前,我们需要先将词汇表建立成一颗霍夫曼树。

    对于从输入层到隐藏层(投影层),这一步比较简单,就是对ww周围的2c2c个词向量求和取平均即可。 

    第二步,通过梯度上升法来更新我们的θj−1w和xw,注意这里的xwxw是由2c2c个词向量相加而成,我们做梯度更新完毕后会用梯度项直接更新原始的各个xi(i=1,2,,,,2c)xi(i=1,2,,,,2c),即:

 θwj1=θwj1+η(1dwjσ(xTwθwj1))xwθj−1w=θj−1w+η(1−djw−σ(xwTθj−1w))xw

xw=xw+η(1dwjσ(xTwθwj1))θwj1(i=1,2..,2c)xw=xw+η(1−djw−σ(xwTθj−1w))θj−1w(i=1,2..,2c)

    其中η为梯度上升法的步长。

   这里总结下基于Hierarchical Softmax的CBOW模型算法流程,梯度迭代使用了随机梯度上升法:

   输入:基于CBOW的语料训练样本,词向量的维度大小M,CBOW的上下文大小2c2c,步长η

   输出:霍夫曼树的内部节点模型参数θ,所有的词向量w

    1. 基于语料训练样本建立霍夫曼树。

    2. 随机初始化所有的模型参数θθ,所有的词向量ww

    3. 进行梯度上升迭代过程

8. 基于Hierarchical Softmax的Skip-Gram模型

   现在我们先看看基于Skip-Gram模型时, Hierarchical Softmax如何使用。此时输入的只有一个词ww,输出的为2c2c个词向量context(w)context(w)。

   我们对于训练样本中的每一个词,该词本身作为样本的输入, 其前面的c个词和后面的c个词作为了Skip-Gram模型的输出,,期望这些词的softmax概率比其他的词大。

   Skip-Gram模型和CBOW模型其实是反过来的。

   在做CBOW模型前,我们需要先将词汇表建立成一颗霍夫曼树。

   对于从输入层到隐藏层(投影层),这一步比CBOW简单,由于只有一个词,所以,即xwxw就是词ww对应的词向量。

   第二步,通过梯度上升法来更新我们的θwj1θj−1w和xwxw,注意这里的xwxw周围有2c2c个词向量,此时如果我们期望P(xi|xw),i=1,2...2cP(xi|xw),i=1,2...2c最大。此时我们注意到由于上下文是相互的,在期望P(xi|xw),i=1,2...2cP(xi|xw),i=1,2...2c最大化的同时,反过来我们也期望P(xw|xi),i=1,2...2cP(xw|xi),i=1,2...2c最大。那么是使用P(xi|xw)P(xi|xw)好还是P(xw|xi)P(xw|xi)好呢,word2vec使用了后者,这样做的好处就是在一次迭代时,我们不是更新xwxw一个词,而是xi,i=1,2...2cxi,i=1,2...2c共2c2c个词。这样整体的迭代会更加的均衡。因为这个原因,Skip-Gram模型并没有和CBOW模型一样对输入进行迭代更新,而是对2c2c个输出进行迭代更新。

   这里总结下基于Hierarchical Softmax的Skip-Gram模型算法流程,梯度迭代使用了随机梯度上升法:

   输入:基于Skip-Gram的语料训练样本,词向量的维度大小M,Skip-Gram的上下文大小2c2c,步长ηη

   输出:霍夫曼树的内部节点模型参数θθ,所有的词向量w

    1. 基于语料训练样本建立霍夫曼树。

    2. 随机初始化所有的模型参数θ,所有的词向量w,

    3. 进行梯度上升迭代过程。 

9. Hierarchical Softmax的缺点与改进

  在讲基于Negative Sampling的word2vec模型前,我们先看看Hierarchical Softmax的的缺点。的确,使用霍夫曼树来代替传统的神经网络,可以提高模型训练的效率。但是如果我们的训练样本里的中心词ww是一个很生僻的词,那么就得在霍夫曼树中辛苦的向下走很久了。能不能不用搞这么复杂的一颗霍夫曼树,将模型变的更加简单呢?

  Negative Sampling就是这么一种求解word2vec模型的方法,它摒弃了霍夫曼树,采用了Negative Sampling(负采样)的方法来求解,下面我们就来看看Negative Sampling的求解思路。

10. 基于Negative Sampling的模型概述

  既然名字叫Negative Sampling(负采样),那么肯定使用了采样的方法。采样的方法有很多种,比如之前讲到的大名鼎鼎的MCMC。我们这里的Negative Sampling采样方法并没有MCMC那么复杂。

  比如我们有一个训练样本,中心词是ww,它周围上下文共有2c2c个词,记为context(w)context(w)。由于这个中心词ww,的确和context(w)context(w)相关存在,因此它是一个真实的正例。通过Negative Sampling采样,我们得到neg个和ww不同的中心词wi,i=1,2,..negwi,i=1,2,..neg,这样context(w)context(w)和$$w_i$就组成了neg个并不真实存在的负例。利用这一个正例和neg个负例,我们进行二元逻辑回归,得到负采样对应每个词$w_i$对应的模型参数$ heta_{i}$,和每个词的词向量。

  从上面的描述可以看出,Negative Sampling由于没有采用霍夫曼树,每次只是通过采样neg个不同的中心词做负例,就可以训练模型,因此整个过程要比Hierarchical Softmax简单。

11. Negative Sampling负采样方法

  现在我们来看看如何进行负采样,得到neg个负例。word2vec采样的方法并不复杂,如果词汇表的大小为VV,那么我们就将一段长度为1的线段分成VV份,每份对应词汇表中的一个词。当然每个词对应的线段长度是不一样的,高频词对应的线段长,低频词对应的线段短。 

  在采样前,我们将这段长度为1的线段划分成M等份,这里M>>VM>>V,根据词出现的频数占据不同的段。这样可以保证每个词对应的线段都会划分成对应的小块。而M份中的每一份都会落在某一个词对应的线段上。在采样的时候,我们只需要从M个位置中采样出negneg个位置就行,此时采样到的每一个位置对应到的线段所属的词就是我们的负例词。

   在word2vec中,M取值默认为108

12.  基于Negative Sampling的CBOW模型

   有了上面Negative Sampling负采样的方法和逻辑回归求解模型参数的方法,我们就可以总结出基于Negative Sampling的CBOW模型算法流程了。梯度迭代过程使用了随机梯度上升法:

   输入:基于CBOW的语料训练样本,词向量的维度大小M,CBOW的上下文大小2c2c,步长ηη, 负采样的个数neg。

   输出:词汇表每个词对应的模型参数θθ,所有的词向量xw

    1. 随机初始化所有的模型参数θ,所有的词向量w。

    2. 对于每个训练样本(context(w0),w0)(context(w0),w0),负采样出neg个负例中心词wi,i=1,2,...negwi,i=1,2,...neg

    3. 进行梯度上升迭代过程。

13.  基于Negative Sampling的Skip-Gram模型

    有了上一节CBOW的基础和上一篇基于Hierarchical Softmax的Skip-Gram模型基础,我们也可以总结出基于Negative Sampling的Skip-Gram模型算法流程了。梯度迭代过程使用了随机梯度上升法:

    输入:基于Skip-Gram的语料训练样本,词向量的维度大小M,Skip-Gram的上下文大小2c2c,步长η, , 负采样的个数neg。

    输出:词汇表每个词对应的模型参数θ,所有的词向量xw。

    1. 随机初始化所有的模型参数θ,所有的词向量w。

    2. 对于每个训练样本(context(w0),w0)(context(w0),w0),负采样出neg个负例中心词wi,i=1,2,...negwi,i=1,2,...neg

    3. 进行梯度上升迭代过程。

 

    参考:http://www.cnblogs.com/pinard/p/7249903.html

原文地址:https://www.cnblogs.com/kang06/p/9470137.html