(一)理解word2vec:原理篇

为什么想起来学习word2vec呢?其实之前自己根本没有接触过NLP的知识和任务,只是最近尝试使用了embedding的方法去处理类别特征和用embedding去做推荐,发现有不错的效果。同时,自己也感触到了所掌握知识的匮乏,因此,决定好好学习一下word2vec。

最近几天自己研读了网上一些介绍word2vec原理的博客,写得非常得清晰和易懂。因此,自己也想做一个总结,关于word2vec的原理和实践的一个总结。供自己和读者们随时可以参看。

好了,废话不多说了。本篇博客主要记录word2vec的原理。关于实践word2vec的博客请参看这篇

声明:本篇博客主要借鉴了刘建平老师在博客园的系列文章和皮国提在CSDN上的系列文章,在他们的基础上,加入了一些自己的理解和感触。非常感谢他们的文章带领我这种小白入门。

一、引言

1.1 什么是word2vec

word2vec是google在2013年推出的一个NLP工具,它的特点是将所有的词向量化,这样词与词之间就可以定量的去度量他们之间的关系,挖掘词之间的联系。原始论文《Exploiting Similarities among Languages for Machine Translation》。根据上面简单的介绍,我们一句话来说明word2vec是什么:word2vec就是把单词转换成向量。

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

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

有了用Dristributed representation表示的较短的词向量,我们就可以较容易的分析词之间的关系了,比如我们将词的维度降维到2维,有一个有趣的研究表明,用下图的词向量表示我们的词时,我们可以发现:
K i n g M a n + W o m a n = Q u e e n \vec {King} - \vec {Man} + \vec {Woman} = \vec {Queen}

CSDN图标

还比如,在一些介绍手机的语料库中,我们可以找相关词,注意是相关词而不是同义词。例如你输入”雷军”,计算出来的相关词就会有:手机,小米,乔布斯等等。

那么,word2vec是怎么样学会相关词了呢?我这里先给一个小小的例子,具体的细节后面会讲。

word2vec的原理就是 一个词预测 前后词 或者 前后词 预测 当前词,使得概率最大化。

这会导致2个结果:

  • 相似的句子,相同部位的词会相关。

    比如 句子1 w1 w2 w3 w4 X w5 w6 w7.

    句子2 w1 w2 w3 w5 Y w5 w6 w7.

    因为 X 的向量 受 w1 w2 w3 w4 w5 w6 w7 向量影响决定, Y也是受这几个词影响决定。

    所以 X Y 是相关的。

  • 挨着近的词,也是相关的。

    比如 句子 w1 w2 w3 w4 X Y w5 w6 w7. 这样 X Y 都是受到 来自 w1 w2 w3 w4 w5 w6 w7
    向量影响决定。

    所以X Y是相关的。

1.2 word2vec在工业界有什么应用

那么,word2vec在工业中有什么应用呢?我在这里先介绍一些具体的应用,让大家感受以下word2vec的强大作用。随后,我再介绍它的原理。

1.2.1 在社交网络中的推荐

通过类似nearest neighbor原理,找意思相关的词汇,段落或者文章匹配,推荐排序。比如有一个个性化推荐的场景,给当前用户推荐他可能关注的『大V』。对一个新用户,此题基本无解。但如果在已知用户关注了几个『大V』之后,相当于知道了当前用户的一些关注偏好,根据此偏好给他推荐和他关注过大V相似的大V,就是一个很不错的推荐策略。所以,如果可以求出来任何两个V用户的相似度,上面问题就可以基本得到解决。

我们知道word2vec中两个词的相似度可以直接通过余弦来衡量,接下来就是如何将每个V用户变为一个词向量的问题了。巧妙的地方就是如何定义doc和word,针对上面问题,可以将doc和word定义为:

1.word -> 每一个大V就是一个词

2.doc -> 根据每一个用户关注大V的顺序,生成一篇文章

由于用户量很大(大约4亿),可以将关注word个数少的doc删掉,因为本身大V的种类是十万级别(如果我没记错的话), 选择可以覆盖绝大多数大V的文章数量就足够了。

1.2.2 计算商品的相似度

在商品推荐的场景中,竞品推荐和搭配推荐的时候都有可能需要计算任何两个商品的相似度,根据浏览/收藏/下单/App下载等行为,可以将商品看做词,将每一个用户的一类行为序看做一个文档,通过word2vec将其训练为一个向量。

同样的,在计算广告中,根据用户的点击广告的点击序列,将每一个广告变为一个向量。变为向量后,用此向量可以生成特征融入到rank模型中。

1.2.3 作为另一个模型的输入

把word2vec生成的向量直接作为深度神经网络的输入。

二、词向量基础

那么我们应该怎么训练得到合适的词向量呢?一个很常见的方法是使用神经网络语言模型

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

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

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

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

CSDN图标

在我们这个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计算各个词的输出概率的的计算量很大。有没有简化一点点的方法呢?

2.2 Hierarchical Softmax模型和Negative Sampling模型

为了加速训练过程,Google论文里真实实现的word2vec对模型提出了两种改进思路,即Hierarchical Softmax模型和Negative Sampling模型。

Hierarchical Softmax是用输出值的霍夫曼编码代替原本的one-hot向量,用霍夫曼树替代Softmax的计算过程。

Negative Sampling(简称NEG)使用随机采用替代Softmax计算概率,它是另一种更严谨的抽样模型NCE的简化版本。

关于Hierarchical Softmax和Negative Sampling的原理细节我将在第三章和第四章进行详细地阐述。

将这两种算法与前面的两个模型组合,在Google的论文里一共包含了4种Word2Vec的实现。

  • Hierarchical Softmax CBOW 模型
  • Hierarchical Softmax Skip-Gram 模型
  • Negative Sampling CBOW 模型
  • Negative Sampling Skip-Gram 模型

2.3 word2vec基础之霍夫曼树

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

具体如何用霍夫曼树来进行CBOW和Skip-Gram的训练我们在第三章里讲,这里我们先复习下霍夫曼树。
    
霍夫曼树大家都很熟悉,建立其实并不难,过程如下:

  • 输入:权值为 ( w 1 , w 2 , . . . w n ) (w_1,w_2,...w_n) n n 个节点

  • 输出:对应的霍夫曼树

    1)将 ( w 1 , w 2 , . . . w n ) (w_1,w_2,...w_n) 看做是有 n n 棵树的森林,每个树仅有一个节点。

    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合并,得到新子树,依次类推,最终得到下面的霍夫曼树。

CSDN图标

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

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

三、 基于Hierarchical Softmax的模型

3.1 基于Hierarchical Softmax的模型概述

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

CSDN图标

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

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

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

CSDN图标

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

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

P ( + ) = σ ( x w T θ ) = 1 1 + e x w T θ P(+) = \sigma(x_w^T\theta) = \frac{1}{1+e^{-x_w^T\theta}}

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

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

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

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

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

3.2 基于Hierarchical Softmax的模型梯度计算

我们使用最大似然法来寻找所有节点的词向量和所有内部节点 θ \theta 。先拿上面的 w 2 w_2 例子来看,我们期望最大化下面的似然函数:
i = 1 3 P ( n ( w i ) , i ) = ( 1 1 1 + e x w T θ 1 ) ( 1 1 1 + e x w T θ 2 ) 1 1 + e x w T θ 3 \prod_{i=1}^3P(n(w_i),i) = (1- \frac{1}{1+e^{-x_w^T\theta_1}})(1- \frac{1}{1+e^{-x_w^T\theta_2}})\frac{1}{1+e^{-x_w^T\theta_3}}

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

为了便于我们后面一般化的描述,我们定义输入的词为 w w ,其从输入层词向量求和平均后的霍夫曼树根节点词向量为 x w x_w , 从根节点到 w w 所在的叶子节点,包含的节点总数为 l w l_w w w 在霍夫曼树中从根节点开始,经过的第 i i 个节点(不包括根节点)表示为 p i w p_i^w ,对应的霍夫曼编码为 d i w { 0 , 1 } d_i^w \in \{0,1\} ,其中 i = 2 , 3 , . . . l w i =2,3,...l_w 。而该节点对应的模型参数表示为 θ i w \theta_i^w , 其中 i = 1 , 2 , . . . l w 1 i =1,2,...l_w-1 ,没有 i = l w i =l_w 是因为模型参数仅仅针对于霍夫曼树的内部节点。

定义 w w 经过的霍夫曼树某一个节点 j j 的逻辑回归概率为 P ( d j w x w , θ j 1 w ) P(d_j^w|x_w, \theta_{j-1}^w) ,其表达式为:
P ( d j w x w , θ j 1 w ) = { σ ( x w T θ j 1 w ) d j w = 0 1 σ ( x w T θ j 1 w ) d j w = 1 P(d_j^w|x_w, \theta_{j-1}^w)= \begin{cases} \sigma(x_w^T\theta_{j-1}^w)& {d_j^w=0}\\ 1- \sigma(x_w^T\theta_{j-1}^w) & {d_j^w = 1} \end{cases}

那么对于某一个目标输出词 w w ,其最大似然为:
j = 2 l w P ( d j w x w , θ j 1 w ) = j = 2 l w [ σ ( x w T θ j 1 w ) ] 1 d j w [ 1 σ ( x w T θ j 1 w ) ] d j w \prod_{j=2}^{l_w}P(d_j^w|x_w, \theta_{j-1}^w) = \prod_{j=2}^{l_w} [\sigma(x_w^T\theta_{j-1}^w)] ^{1-d_j^w}[1-\sigma(x_w^T\theta_{j-1}^w)]^{d_j^w}

注:我们可以看到,每一个词 w w 都对应着自己的一套逻辑回归的参数 θ w \theta^w

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

L = l o g j = 2 l w P ( d j w x w , θ j 1 w ) = j = 2 l w ( ( 1 d j w ) l o g [ σ ( x w T θ j 1 w ) ] + d j w l o g [ 1 σ ( x w T θ j 1 w ) ] ) L= log \prod_{j=2}^{l_w}P(d_j^w|x_w, \theta_{j-1}^w) = \sum\limits_{j=2}^{l_w} ((1-d_j^w) log [\sigma(x_w^T\theta_{j-1}^w)] + d_j^w log[1-\sigma(x_w^T\theta_{j-1}^w)])

要得到模型中 w w 词向量和内部节点的模型参数 θ \theta , 我们使用梯度上升法即可。首先我们求模型参数 θ j 1 w \theta_{j-1}^w 的梯度:

L θ j 1 w = ( 1 d j w ) ( σ ( x w T θ j 1 w ) ( 1 σ ( x w T θ j 1 w ) σ ( x w T θ j 1 w ) x w d j w ( σ ( x w T θ j 1 w ) ( 1 σ ( x w T θ j 1 w ) 1 σ ( x w T θ j 1 w ) x w = ( 1 d j w ) ( 1 σ ( x w T θ j 1 w ) ) x w d j w σ ( x w T θ j 1 w ) x w = ( 1 d j w σ ( x w T θ j 1 w ) ) x w \begin{aligned} \frac{\partial L}{\partial \theta_{j-1}^w} & = (1-d_j^w)\frac{(\sigma(x_w^T\theta_{j-1}^w)(1-\sigma(x_w^T\theta_{j-1}^w)}{\sigma(x_w^T\theta_{j-1}^w)}x_w - d_j^w \frac{(\sigma(x_w^T\theta_{j-1}^w)(1-\sigma(x_w^T\theta_{j-1}^w)}{1- \sigma(x_w^T\theta_{j-1}^w)}x_w \\ & = (1-d_j^w)(1-\sigma(x_w^T\theta_{j-1}^w))x_w - d_j^w\sigma(x_w^T\theta_{j-1}^w)x_w \\& = (1-d_j^w-\sigma(x_w^T\theta_{j-1}^w))x_w \end{aligned}

在这里,用到了逻辑回归里一个公式: σ ( x ) = σ ( x ) ( 1 σ ( x ) ) \sigma' (x)=\sigma(x)(1-\sigma (x))

同样的方法,可以求出 x w x_w 的梯度表达式如下:
L x w = ( 1 d j w σ ( x w T θ j 1 w ) ) θ j 1 w \frac{\partial L}{\partial x_w} = (1-d_j^w-\sigma(x_w^T\theta_{j-1}^w))\theta_{j-1}^w

注: 到这里,细心的读者已经看出问题了:我们的最终目的是求得词典中每个词的词向量,而这里的 x w x_w 表示得是 w w 的上下文 C o n t e x t ( w ) Context(w) 各个词词向量的累加平均,那么,如何利用 L x w \frac{\partial L}{\partial x_w} 来对 w w 的上下文 C o n t e x t ( w ) Context(w) 中的每个词向量 w ~ \tilde{w} 进行更新呢?word2vec的做法很简单,直接取:
L x w = j = 2 l w ( 1 d j w σ ( x w T θ j 1 w ) ) θ j 1 w \frac{\partial L}{\partial x_w} = \sum\limits_{j=2}^{l_w}(1-d_j^w-\sigma(x_w^T\theta_{j-1}^w))\theta_{j-1}^w

即把 j = 2 l w L x w \sum\limits_{j=2}^{l_w}\frac{\partial L}{\partial x_w} 贡献到 C o n t e x t ( w ) Context(w) 中每一个词的词向量中。这个应该很好理解,既然 x w x_w 本身就是 C o n t e x t ( w ) Context(w) 中各个词向量的累加平均,求完梯度后当然也应该贡献到每个分量上去。

3.3 基于Hierarchical Softmax的CBOW模型

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

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

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

对于从输入层到隐藏层(投影层),这一步比较简单,就是对 w w 周围的 2 c 2c 个词向量求和取平均即可,即:
x w = 1 2 c i = 1 2 c x i x_w = \frac{1}{2c}\sum\limits_{i=1}^{2c}x_i

第二步,通过梯度上升法来更新我们的 θ j 1 w \theta_{j-1}^w x w x_w ,注意这里的 x w x_w 是由 2 c 2c 个词向量相加而成,我们做梯度更新完毕后会用梯度项直接更新原始的各个 x i ( i = 1 , 2 , , , , 2 c ) x_i(i=1,2,,,,2c) ,即:
θ j 1 w = θ j 1 w + η ( 1 d j w σ ( x w T θ j 1 w ) ) x w \theta_{j-1}^w = \theta_{j-1}^w + \eta (1-d_j^w-\sigma(x_w^T\theta_{j-1}^w))x_w

x w = x w + η j = 2 l w ( 1 d j w σ ( x w T θ j 1 w ) ) θ j 1 w    ( i = 1 , 2.. , 2 c ) x_w= x_w +\eta \sum\limits_{j=2}^{l_w}(1-d_j^w-\sigma(x_w^T\theta_{j-1}^w))\theta_{j-1}^w \;(i =1,2..,2c)

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

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

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

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

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

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

    ③进行梯度上升迭代过程,对于训练集中的每一个样本 ( c o n t e x t ( w ) , w ) (context(w), w) 做如下处理:
    a) e=0, 计算 x w = 1 2 c i = 1 2 c x i x_w= \frac{1}{2c}\sum\limits_{i=1}^{2c}x_i
    b) for j = 2 j = 2 to l w l_w , 计算:
    f = σ ( x w T θ j 1 w ) g = ( 1 d j w f ) η e = e + g θ j 1 w θ j 1 w = θ j 1 w + g x w f = \sigma(x_w^T\theta_{j-1}^w) \\ g = (1-d_j^w-f)\eta \\ e = e + g\theta_{j-1}^w \\ \theta_{j-1}^w= \theta_{j-1}^w + gx_w

    c) 对于 c o n t e x t ( w ) context(w) 中的每一个词向量 x i x_i (共 2 c 2c 个)进行更新: x i = x i + e x_i = x_i + e
    d) 如果梯度收敛,则结束梯度迭代,否则回到步骤3继续迭代。

注:在③.b)中的第3步和第4步不能交换次序,即 θ j 1 w \theta_{j-1}^w 应该等到贡献到 e e 后再做更新。

3.4 基于Hierarchical Softmax的Skip-Gram模型

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

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

Skip-Gram模型和CBOW模型其实是反过来的,在上一章已经讲过。

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

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

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

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

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

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

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

    ② 随机初始化所有的模型参数 θ \theta ,所有的词向量 w w

    ③ 进行梯度上升迭代过程,对于训练集中的每一个样本 ( w , c o n t e x t ( w ) ) (w,context(w)) 做如下处理:

    a) for i = 1 i =1 to 2 c 2c :

    i) e=0
    ii) for j = 2 j = 2 to l w l_w , 计算: f = σ ( x i T θ j 1 w ) g = ( 1 d j w f ) η e = e + g θ j 1 w θ j 1 w = θ j 1 w + g x i f = \sigma(x_i^T\theta_{j-1}^w) \\ g = (1-d_j^w-f)\eta \\ e = e + g\theta_{j-1}^w \\ \theta_{j-1}^w= \theta_{j-1}^w+ gx_i
    iii) x i = x i + e x_i = x_i + e

    b)如果梯度收敛,则结束梯度迭代,算法结束,否则回到步骤a继续迭代。

注:在③.a).ii)中的第3步和第4步不能交换次序,即 θ j 1 w \theta_{j-1}^w 应该等到贡献到 e e 后再做更新。

以上就是基于Hierarchical Softmax的word2vec模型,下一章我们讨论基于Negative Sampling的word2vec模型。

四、基于Negative Sampling的模型

4.1 Hierarchical Softmax的缺点与改进

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

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

4.2 基于Negative Sampling的模型概述

既然名字叫Negative Sampling(负采样),那么肯定使用了采样的方法。

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

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

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

我们在4.3讨论问题1,在4.4讨论问题2。

4.3 基于Negative Sampling的模型梯度计算

Negative Sampling也是采用了二元逻辑回归来求解模型参数,通过负采样,我们得到了neg个负例 ( c o n t e x t ( w ) , w i ) , i = 1 , 2 , . . n e g (context(w),w_i),i=1,2,..neg 。为了统一描述,我们将正例定义 w 0 w_0

在逻辑回归中,我们的正例应该期望满足:
P ( c o n t e x t ( w 0 ) , w i ) = σ ( x w 0 T θ w i ) , y i = 1 , i = 0 P(context(w_0), w_i) = \sigma(x_{w_0}^T\theta^{w_i}) ,y_i=1, i=0

我们的负例期望满足:
P ( c o n t e x t ( w 0 ) , w i ) = 1 σ ( x w 0 T θ w i ) , y i = 0 , i = 1 , 2 , . . n e g P(context(w_0), w_i) =1- \sigma(x_{w_0}^T\theta^{w_i}), y_i = 0, i=1,2,..neg

我们期望可以最大化下式:
i = 0 n e g P ( c o n t e x t ( w 0 ) , w i ) = σ ( x w 0 T θ w 0 ) i = 1 n e g ( 1 σ ( x w 0 T θ w i ) ) \prod_{i=0}^{neg}P(context(w_0), w_i) = \sigma(x_{w_0}^T\theta^{w_0})\prod_{i=1}^{neg}(1- \sigma(x_{w_0}^T\theta^{w_i}))

利用逻辑回归和上一章的知识,我们容易写出此时模型的似然函数为:因为只有1个正例:
i = 0 n e g σ ( x w 0 T θ w i ) y i ( 1 σ ( x w 0 T θ w i ) ) 1 y i \prod_{i=0}^{neg} \sigma(x_{w_0}^T\theta^{w_i})^{y_i}(1- \sigma(x_{w_0}^T\theta^{w_i}))^{1-y_i}

此时对应的对数似然函数为:
L = i = 0 n e g y i l o g ( σ ( x w 0 T θ w i ) ) + ( 1 y i ) l o g ( 1 σ ( x w 0 T θ w i ) ) L = \sum\limits_{i=0}^{neg}y_i log(\sigma(x_{w_0}^T\theta^{w_i})) + (1-y_i) log(1- \sigma(x_{w_0}^T\theta^{w_i}))

和Hierarchical Softmax类似,我们采用随机梯度上升法,仅仅每次只用一个样本更新梯度,来进行迭代更新得到我们需要的 x w i , θ w i , i = 0 , 1 , . . n e g x_{w_i}, \theta^{w_i}, i=0,1,..neg ,这里我们需要求出 x w 0 , θ w i , i = 0 , 1 , . . n e g x_{w_0}, \theta^{w_i}, i=0,1,..neg 的梯度:

首先我们计算 θ w i \theta^{w_i} 的梯度:
L θ w i = y i ( 1 σ ( x w 0 T θ w i ) ) x w 0 ( 1 y i ) σ ( x w 0 T θ w i ) x w 0 = ( y i σ ( x w 0 T θ w i ) ) x w 0 \begin{aligned} \frac{\partial L}{\partial \theta^{w_i} } &= y_i(1- \sigma(x_{w_0}^T\theta^{w_i}))x_{w_0}-(1-y_i)\sigma(x_{w_0}^T\theta^{w_i})x_{w_0} \\ & = (y_i -\sigma(x_{w_0}^T\theta^{w_i})) x_{w_0} \end{aligned}

同样的方法,我们可以求出 x w 0 x_{w_0} 的梯度如下:
L x w 0 = i = 0 n e g ( y i σ ( x w 0 T θ w i ) ) θ w i \frac{\partial L}{\partial x^{w_0} } = \sum\limits_{i=0}^{neg}(y_i -\sigma(x_{w_0}^T\theta^{w_i}))\theta^{w_i}

有了梯度表达式,我们就可以用梯度上升法进行迭代来一步步的求解我们需要的 x w 0 , θ w i , i = 0 , 1 , . . n e g x_{w_0}, \theta^{w_i}, i=0,1,..neg

4.4 Negative Sampling负采样方法

现在我们来看看如何进行负采样,得到neg个负例。word2vec采样的方法并不复杂,如果词汇表的大小为 V V ,那么我们就将一段长度为1的线段分成 V V 份,每份对应词汇表中的一个词。当然每个词对应的线段长度是不一样的,高频词对应的线段长,低频词对应的线段短。每个词 w w 的线段长度由下式决定:
l e n ( w ) = c o u n t ( w ) u v o c a b c o u n t ( u ) len(w) = \frac{count(w)}{\sum\limits_{u \in vocab} count(u)}

在word2vec中,分子和分母都取了3/4次幂如下:
l e n ( w ) = c o u n t ( w ) 3 / 4 u v o c a b c o u n t ( u ) 3 / 4 len(w) = \frac{count(w)^{3/4}}{\sum\limits_{u \in vocab} count(u)^{3/4}}

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

CSDN图标

在word2vec中, M M 取值默认为 1 0 8 10^8

4.5 基于Negative Sampling的CBOW模型

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

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

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

① 随机初始化所有的模型参数 θ \theta ,所有的词向量 x w x_w

② 对于每个训练样本 ( c o n t e x t ( w 0 ) , w 0 ) (context(w_0),w0) ,负采样出neg个负例中心词 w i , i = 1 , 2 , . . . n e g w_i,i=1,2,...neg

③ 进行梯度上升迭代过程,对于训练集中的每一个样本 ( c o n t e x t ( w 0 ) , w 0 , w 1 , . . . w n e g ) (context(w_0),w_0,w_1,...w_{neg}) 做如下处理:
a) e=0, 计算 x w 0 = 1 2 c i = 1 2 c x i x_{w_0}= \frac{1}{2c}\sum\limits_{i=1}^{2c}x_i   
b) for i= 0 to neg, 计算:

  f = σ ( x w 0 T θ w i )   g = ( y i f ) η   e = e + g θ w i   θ w i = θ w i + g x w 0    f = \sigma(x_{w_0}^T\theta^{w_i}) \\  g = (y_i-f)\eta \\  e = e + g\theta^{w_i} \\  \theta^{w_i}= \theta^{w_i} + gx_{w_0}  

c) 对于 c o n t e x t ( w ) context(w) 中的每一个词向量 x k x_k (共 2 c 2c 个)进行更新:
x k = x k + e x_k = x_k + e

d) 如果梯度收敛,则结束梯度迭代,否则回到步骤3继续迭代。

4.6 基于Negative Sampling的Skip-Gram模型

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

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

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

① 随机初始化所有的模型参数 θ \theta ,所有的词向量 x w x_w

② 对于每个训练样本 ( c o n t e x t ( w 0 ) , w 0 ) (context(w_0),w0) ,负采样出neg个负例中心词 w i , i = 1 , 2 , . . . n e g w_i,i=1,2,...neg

③ 进行梯度上升迭代过程,对于训练集中的每一个样本 ( c o n t e x t ( w 0 ) , w 0 , w 1 , . . . w n e g ) (context(w_0),w_0,w_1,...w_{neg}) 做如下处理:
a) for i = 1 i =1 to 2 c 2c :

  • i) e=0

  • ii) for j = 0 j= 0 to neg, 计算: f = σ ( x w 0 i T θ w j ) g = ( y j f ) η e = e + g θ w j θ w j = θ w j + g x w 0 i f = \sigma(x_{w_{0i}}^T\theta^{w_j}) \\ g = (y_j-f)\eta \\ e = e + g\theta^{w_j} \\ \theta^{w_j}= \theta^{w_j} + gx_{w_{0i}}

  • iii) 对于 c o n t e x t ( w ) context(w) 中的每一个词向量 x k x_k (共 2 c 2c 个)进行更新: x k = x k + e x_k = x_k + e

b) 如果梯度收敛,则结束梯度迭代,算法结束,否则回到步骤a继续迭代。

注:

在CBOW中,直接和 2 c 2c 个真实窗口数据平均得到的词一起,共neg+1个词,进行拟合优化,这些词中只有一个正例,其余全部都是负例。

在skip-gram里则稍微复杂一点,由于没有取平均,所以多了一层循环,也就是每个窗口词都去和neg个负例一起去拟合优化。

五、Hierarchical Softmax和Negative Sampling的一些细节

① 在这2种模型中,每个词 w w 的模型参数有什么区别?

θ w i \theta^{w_i} 是词 w i w_i 对应的模型参数。在negative sampling中每个词 w i w_i 只有一个模型参数

而在Hierarchical Softmax中,每个词 w i w_i l w 1 l_w−1 个模型参数。这些参数即在树结构中的内部节点的参数。

② 为什么Negative Sampling可以达到和Hierarchical Softmax类似的效果?

可以理解为负采样是分层softmax的一种近似。正常我们是需要拿中心词和对应的上下文来一起训练的,只是给出的正例,不是采样得来的。但是为了应付海量数据的训练,同时可以加快训练速度,我们需要做一些精度的牺牲。这时我们直接通过少量的负采样(负采样更容易找到合适的词)样本来训练,可以加快速度同时减少内存负担。

本质上它们都是在一颗二叉树上选择合适的路径下到叶子节点。只是分层softmax里面是正例训练,而负采样是负例训练,同时负词的个数较分层softmax少,不需要那么大一颗哈夫曼树来保存,方便训练快速收敛,同时内存消耗少。

③ 为什么Hierarchical Softmax中,二叉树的使用使得之前计算量为V,现在变成了log2V?

之前每个词都需要计算 V V 个词对应的softmax结果,现在每个词只需要在霍夫曼树中从上到下走一遍到叶子,不用走所有的树节点。所以就成了log2V。

④ 在Hierarchical Softmax中,如果每次都是更新上下文的词向量的话,那skip-gram模也是把上下文作为输入进行更新,而中心词作为输出去训练上下文,那结构不就和CBOW差不多了吗?

迭代更新的过程的确是类似的,只有一些细节上的不同。

在CBOW中,在霍夫曼树从上而下使用的输入是 2 c 2c 个窗口词词向量的均值,只有一个。

而在skip-gram里面,在霍夫曼树从上而下使用的输入分别是各个词向量,一共 2 c 2c 个。

所以可以看到在skip-gram算法里面会多一层循环。

其实在Negative Sampling中也是一样的。

⑤ word2vec就是求解每一个的单词的embedding?那知道一个中心词 w w 求上下文(CBOW)和已知上下文求中心词 w w (Skip-Gram)在其中起到了什么作用呢?

word2vec就是求解每一个的单词的embedding的词向量。CBOW和Skip-Gram只是2个求出似然函数的途径,我们根据这2个思路得到似然函数进行梯度下降的求解。且这2个思路没有本质区别(在梯度更新的公式上),都是更新 2 c 2c 个词向量。

参考文献

【1】word2vec原理(一) CBOW与Skip-Gram模型基础
【2】word2vec在工业界的应用场景
【3】关于word2vec,我有话要说

猜你喜欢

转载自blog.csdn.net/anshuai_aw1/article/details/84241279