word2vec基本原理和实现

Word2Vec

 

目录

一.概述... 1

CBow模型 (Continuous Bag-of-Words Model) 2

Skip-gram模型... 2

Hierarchical Softmax. 3

Negative Sampling. 3

二.详析... 3

基于Hierarchical Softmax的模型... 4

基于Hierarchical Softmax的模型梯度计算... 5

具体使用:... 6

对比... 8

代码... 9

基于Negative Sampling的word2vec模型... 9

基于Negative Sampling的模型概述... 9

基于Negative Sampling的模型梯度计算... 9

具体应用:... 11

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

代码... 12

 

 

 

 

一.概述

https://www.cnblogs.com/chason95/articles/10746960.html

 

  1. word2vec算法的背后是一个浅层神经网络,很多人误以为word2vec是一种深度学习算法。
  2. word2vec是一个计算word vector的开源工具。当我们在说word2vec算法或模型的时候,其实指的是其背后用于计算word vector的CBoW模型和Skip-gram模型。很多人以为word2vec指的是一个算法或模型,这也是一种谬误。

所以word2vec是一个工具,这个工具包含了CBow和Skip-gram两种模型来计算word vector。CBow和Skip-gram也可以用于NNLM,但是word2vec并不是这么做的,它针对NNLM的缺点提出了新的训练技巧Hierarchical Softmax和Negative Sampling

 

CBow模型 (Continuous Bag-of-Words Model)

 

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

https://img2018.cnblogs.com/blog/1372735/201904/1372735-20190422201934867-74946992.png

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

这里介绍的是CBow的思想,也可以理解为在NNLM中使用CBoW。基于CBoW的word2vec工具在这个思想基础上还做了很多训练技巧的改进。
所以如果用基于CBowNNLM,目标函数可以选交叉熵

 

Skip-gram模型

 

Skip-Gram模型和CBOW的思路是反着来的,即输入是特定的一个词的词向量,而输出是特定词对应的上下文词向量。还是上面的例子,我们的上下文大小取值为4, 特定的这个词"Learning"是我们的输入,而这8个上下文词是我们的输出。
这样我们这个Skip-Gram的例子里,我们的输入是特定词, 输出是softmax概率排前8的8个词,对应的Skip-Gram神经网络模型输入层有1个神经元,输出层有词汇表大小个神经元。隐藏层的神经元个数我们可以自己指定。通过DNN的反向传播算法,我们可以求出DNN模型的参数,同时得到所有的词对应的词向量。这样当我们有新的需求,要求出某1个词对应的最可能的8个上下文词时,我们可以通过一次DNN前向传播算法得到概率大小排前8的softmax概率对应的神经元所对应的词即可。
和CBoW一样,这里介绍的是Skip-Gram的思想,也可以理解为在NNLM中使用Skip-Gram。基于Skip-Gram的word2vec工具在这个思想基础上也做了很多训练技巧的改进。
这里目标函数同样可以选取交叉熵

 

Hierarchical Softmax

 

NNLM中,hidden layer是由input经过神经网络的线性变换加激活函数得到的,基于CBow的word2vec中,将这步简化为了直接对input中的词向量们做average pooling,即输入(1,2,3,4),(9,6,11,8),(5,10,7,12),得到(5,6,7,8)。(基于Skip-gram的word2vec的hidden layer就直接等于input了,其实就是没有hidden layer)
另外第二个改进是针对隐藏层到softmax层的。我们知道,哈夫曼树是一种让权重高的节点(使用频率高的)编码短(更快取到)的数据结构。word2vec就使用哈夫曼树来对单词们编码,从根节点开始,每个节点之间都对应一个权重,通过节点权重和隐层中间语义的二分类逻辑回归来判断对应的下一个单词应该出现在那个叶子节点上。可以看出,哈夫曼树的叶子节点就对应了NNLM中输出softmax层中的一个个神经元。
这样,我们在训练阶段,如果使用NNLM,我们需要对所有V个节点都进行前向传播,而使用了Hierarchical Softmax之后,前向传播最多只用计算logV次即可(哈夫曼树树深)。大大降低了前向传播的计算量。
不过反向传播更新参数的计算量可能并没有减少。我们依然可以把哈夫曼树的叶子节点们看作NNLM中最后一层输出层,所以目标函数还是可以用同样的softmax。

 

Negative Sampling

 

Hierarchical Softmax有效降低了前向传播的计算量,但是其实它是增加了网络的参数量的。Negative Sampling则是从另一个方向来对NNLM做改进。
其实思路比Hierarchical Softmax简单,就是我们要根据输入的wi两边的2c个词,然wi在输出中概率最高,那么我们再采几个负样本的词,然后他们在输出中概率最低,这样可以避免网络的参数倾向于让大家的得分都越来越高(冷落了那些出现较少的但是其实也很重要的词)。这点在推荐系统中比较常见。

 

 

 

二.详析

现在我们开始关注word2vec的语言模型如何改进传统的神经网络的方法。由于word2vec有两种改进方法,一种是基于Hierarchical Softmax的,另一种是基于Negative Sampling的。

 

基于Hierarchical Softmax的模型

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

 

https://images2017.cnblogs.com/blog/1042406/201707/1042406-20170727105326843-18935623.png

 

 word2vec对这个模型做了改进,

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

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

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

https://images2017.cnblogs.com/blog/1042406/201707/1042406-20170727105752968-819608237.png

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

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

其中xw是当前内部节点的词向量,而θ则是我们需要从训练样本求出的逻辑回归的模型参数。使用霍夫曼树有什么好处呢?首先,由于是二叉树,之前计算量为V,现在变成了log2V。第二,由于使用霍夫曼树是高频的词靠近树根,这样高频词需要更少的时间会被找到,这符合我们的贪心优化思想。

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

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

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

 

基于Hierarchical Softmax的模型梯度计算

      

我们使用最大似然法来寻找所有节点的词向量和所有内部节点θ。先拿上面的w2例子来看,我们期望最大化下面的似然函数:

  对于所有的训练样本,我们期望最大化所有样本的似然函数乘积。

  为了便于我们后面一般化的描述,我们定义输入的词为w,其从输入层词向量求和平均后的霍夫曼树根节点词向量为xw, 从根节点到w所在的叶子节点,包含的节点总数为lw, w在霍夫曼树中从根节点开始,经过的第i个节点表示为pwi,对应的霍夫曼编码为dwi∈{0,1},其中i=2,3,...lw。而该节点对应的模型参数表示为θwi, 其中i=1,2,...lw−1,没有i=lw

是因为模型参数仅仅针对于霍夫曼树的内部节点。

定义w经过的霍夫曼树某一个节点j的逻辑回归概率为,其表达式为:

  那么对于某一个目标输出词w,其最大似然为:

  在word2vec中,由于使用的是随机梯度上升法,所以并没有把所有样本的似然乘起来得到真正的训练集最大似然,仅仅每次只用一个样本更新梯度,这样做的目的是减少梯度计算量。这样我们可以得到w的对数似然函数L如下:

要得到模型中w词向量和内部节点的模型参数θ, 我们使用梯度上升法即可。首先我们求模型参数θwj−1的梯度:

如果大家看过之前写的逻辑回归原理小结,会发现这里的梯度推导过程基本类似。

同样的方法,可以求出xw的梯度表达式如下:

    有了梯度表达式,我们就可以用梯度上升法进行迭代来一步步的求解我们需要的所有的θwj−1和xw。

具体使用:

基于Hierarchical SoftmaxCBOW模型

 

基于Hierarchical SoftmaxSkip-Gram模型

 

对比

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

       不是只更新xw一个词,而是xi,i=1,2...2c共2c个词。这样整体的迭代会更加的均衡。因为这个原因,Skip-Gram模型并没有和CBOW模型一样对输入进行迭代更新,而是对2c个输出进行迭代更新。

 

代码

https://github.com/tmikolov/word2vec/blob/master/word2vec.c

在源代码中,基于Hierarchical Softmax的CBOW模型算法在435-463行,基于Hierarchical Softmax的Skip-Gram的模型算法在495-519行。大家可以对着源代码再深入研究下算法。

在源代码中,neule对应我们上面的e, syn0对应我们的xw, syn1对应我们的θij−1, layer1_size对应词向量的维度,window对应我们的c。另外,vocab[word].code[d]指的是,当前单词word的,第d个编码,编码不含Root结点。vocab[word].point[d]指的是,当前单词word,第d个编码下,前置的结点。

 

 

基于Negative Sampling的word2vec模型

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

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

 

基于Negative Sampling的模型概述

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

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

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

不过有两个问题还需要弄明白:1)如果通过一个正例和neg个负例进行二元逻辑回归呢? 2) 如何进行负采样呢?

基于Negative Sampling的模型梯度计算

 

Negative Sampling负采样方法

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

在word2vec中,分子和分母都取了3/4次幂如下:

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

https://images2017.cnblogs.com/blog/1042406/201707/1042406-20170728152731711-1136354166.png

  在word2vec中,M取值默认为108。

具体应用:

基于Negative SamplingCBOW模型

基于Negative Sampling的Skip-Gram模型

 

代码

https://github.com/tmikolov/word2vec/blob/master/word2vec.c

在源代码中,基于Negative Sampling的CBOW模型算法在464-494行,基于Negative Sampling的Skip-Gram的模型算法在520-542行。大家可以对着源代码再深入研究下算法。

在源代码中,neule对应我们上面的e, syn0对应我们的xw, syn1neg对应我们的θwi, layer1_size对应词向量的维度,window对应我们的c。negative对应我们的neg, table_size对应我们负采样中的划分数M。

 另外,vocab[word].code[d]指的是,当前单词word的,第d个编码,编码不含Root结点。vocab[word].point[d]指的是,当前单词word,第d个编码下,前置的结点。这些和基于Hierarchical Softmax的是一样的。

以上就是基于Negative Sampling的word2vec模型,希望可以帮到大家,后面会讲解用gensim的python版word2vec来使用word2vec解决实际问题。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章