word2vec概述

2020-06-30  本文已影响0人  yousa_

文本表示是自然语言处理中的基础工作,文本表示的好坏直接影响到整个自然语言处理系统的性能。文本向量化是文本表示的一种重要方式。文本向量化就是讲文本表示成一系列能够表达文本语义的向量。

当前阶段,对文本向量化的大部分研究都是通过词向量化来实现的。与此同时,也有相当一部分研究者将文章或者句子作为文本基本处理单元,提出了doc2vec和ste2vec技术。

word2vec

基于embedding的词表示,其核心思想是:上下文相似的词,其语义也相似。这就是著名的词空间模型(word space model),词向量通常使用神经网络模型训练得到,神经网络模型就是根据上下文与目标词之间的关系进行建模。

Word2vec主要有CBOW和Skip-gram两种模式,其中CBOW是从原始语句推测目标字词,而Skip-gram是从目标字词推测出原始语句(滑动窗口范围内),其中CBOW对小型数据比较合适,Skip-fram在大型语料中表现得更好。

词向量最简单的方式是1-of-N的one-hot方式。onehot对于同学们来说都很熟悉了,也就是从很大的词库corpus里选V个频率最高的词(忽略其他的) ,V一般比较大,比如V=10W,固定这些词的顺序,然后每个词就可以用一个V维的稀疏向量表示了,这个向量只有一个位置的元素是1,其他位置的元素都是0。One hot方式其实就是简单的直接映射,所以缺点也很明显,维数很大,也没啥计算上的意义。
在上图中,
1、Input layer输出层:是上下文单词的one hot。假设单词向量空间的维度为V,即整个词库corpus大小为V,上下文单词窗口的大小为C。
2、假设最终词向量的维度大小为N,则图中的权值共享矩阵为W。W的大小为 V * N,并且初始化。
3、假设语料中有一句话"我爱你"。如果我们现在关注"爱"这个词,令C=2,则其上下文为"我",“你”。模型把"我" "你"的onehot形式作为输入。易知其大小为1V。C个1V大小的向量分别跟同一个V * N大小的权值共享矩阵W相乘,得到的是C个1N大小的隐层hidden layer。
4.C个1N大小的hidden layer取平均,得到一个1N大小的向量,即图中的Hidden layer。
5.输出权重矩阵W’为N V,并进行相应的初始化工作。
6.将得到的Hidden layer向量 1N与W’相乘,并且用softmax处理,得到1V的向量,此向量的每一维代表corpus中的一个单词。概率中最大的index所代表的单词为预测出的中间词。
7.与groud truth中的one hot比较,求loss function的的极小值。

Skip-gram的训练方法与CBOW如出一辙,唯一区别就是Skip-gram的输入是单个词的向量,而不是C个词的求和平均。同时,训练的话对于一个中心词,要训练C次,每一次是一个不同的上下文词,比如中心词是北京,窗口词是来到天安门这两个,那么Skip-gram要对北京-来到北京-天安门进行分别训练。




接下来介绍两种优化方法
在木有进行优化前,word2vec的隐藏层有V*N个参数,其中V是全局词的数量,比如10W个,N是预设的词嵌入向量维度,如300,那么这个计算量太大了,因此要进行优化。

先复习一下霍夫曼树,这里我图省事,直接把刘建平老师的博客贴上来。



注意,在word2vec的霍夫曼树中,左边的是1,代表负例,右边的是0,代表正例。

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


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

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


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

 如何“沿着霍夫曼树一步步完成”呢?在word2vec中,我们采用了二元逻辑回归的方法,即规定沿着左子树走,那么就是负类(霍夫曼树编码1),沿着右子树走,那么就是正类(霍夫曼树编码0)。判别正类和负类的方法是使用sigmoid函数,即:
      P(+) = \sigma(x_w^T\theta) = \frac{1}{1+e^{-x_w^T\theta}}
 其中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例子来看,我们期望最大化下面的似然函数:
   \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,其从输入层词向量求和平均后的霍夫曼树根节点词向量为xw, 从根节点到w所在的叶子节点,包含的节点总数为lw, w在霍夫曼树中从根节点开始,经过的第i个节点表示为p_i^w,对应的霍夫曼编码为d_i^w \in \{0,1\},其中i=2,3,...lw。而该节点对应的模型参数表示为\theta_i^w, 其中i =1,2,...l_w-1,没有i=lw是因为模型参数仅仅针对于霍夫曼树的内部节点。
 定义w经过的霍夫曼树某一个节点j的逻辑回归概率为P(d_j^w|x_w, \theta_{j-1}^w),其表达式为:
   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,其最大似然为:
   \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}
在word2vec中,由于使用的是随机梯度上升法,所以并没有把所有样本的似然乘起来得到真正的训练集最大似然,仅仅每次只用一个样本更新梯度,这样做的目的是减少梯度计算量。这样我们可以得到w的对数似然函数L如下:
   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词向量和内部节点的模型参数θ, 我们使用梯度上升法即可。首先我们求模型参数\theta_{j-1}^w的梯度:
   \begin{align} \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{align}
如果大家看过之前写的逻辑回归原理小结,会发现这里的梯度推导过程基本类似。
同样的方法,可以求出x_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
 有了梯度表达式,我们就可以用梯度上升法进行迭代来一步步的求解我们需要的所有的\theta_{j-1}^wx_w

基于Hierarchical Softmax的CBOW模型
由于word2vec有两种模型:CBOW和Skip-Gram,我们先看看基于CBOW模型时, Hierarchical Softmax如何使用。
 首先我们要定义词向量的维度大小M,以及CBOW的上下文大小2c,这样我们对于训练样本中的每一个词,其前面的c个词和后面的c个词作为了CBOW模型的输入,该词本身作为样本的输出,期望softmax概率最大。
在做CBOW模型前,我们需要先将词汇表建立成一颗霍夫曼树。
对于从输入层到隐藏层(投影层),这一步比较简单,就是对w周围的2c个词向量求和取平均即可,即
   x_w = \frac{1}{2c}\sum\limits_{i=1}^{2c}x_i
第二步,通过梯度上升法来更新我们的\theta_{j-1}^wx_w,注意这里的x_w是由2c个词向量相加而成,我们做梯度更新完毕后会用梯度项直接更新原始的各个x_i(i=1,2,,,,2c),即:
   \theta_{j-1}^w = \theta_{j-1}^w + \eta (1-d_j^w-\sigma(x_w^T\theta_{j-1}^w))x_w
   x_i= x_i +\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的语料训练样本,词向量的维度大小Mcount,CBOW的上下文大小2c,步长η
输出:霍夫曼树内部节点模型参数θ,所有的词向量w
1.基于语料训练样本建立霍夫曼树(基于词频)
2.随机初始化所有的模型参数θ,所有的词向量w
3.进行梯度上升迭代过程,对于训练集中的每一个样本(context(w),w)做如下处理:
   a) e=0, 计算x_{w_0}= \frac{1}{2c}\sum\limits_{i=1}^{2c}x_i
   b) for j= 2 to l_w, 计算:
      f = \sigma(x_w^T\theta_{j-1}^w)
      g = (1-d_j^w-f)\eta
这里g是梯度,e是词向量x_i的梯度下降方向及步长,要累加l_w-1次\theta^w_{j-1}是树内节点的梯度下降及步长,这里我们可以看出,word2vec不仅更新隐层节点,还更新输入节点。
      e = e + g\theta_{j-1}^w
      \theta_{j-1}^w= \theta_{j-1}^w + gx_w
   c) 对于context(w)中的每一个词向量x_i(共2c个)进行更新:
      x_i = x_i + e
   d) 如果梯度收敛,则结束梯度迭代,否则回到步骤3继续迭代。

基于Hierarchical Softmax的Skip-Gram模型
现在我们先看看基于Skip-Gram模型时, Hierarchical Softmax如何使用。此时输入的只有一个词w,输出的为2c个词向量context(w)
我们对于训练样本中的每一个词,该词本身作为样本的输入, 其前面的c个词和后面的c个词作为了Skip-Gram模型的输出,,期望这些词的softmax概率比其他的词大。
Skip-Gram模型和CBOW模型其实是反过来的,在上一篇已经讲过。
Skip-Gram模型和CBOW模型其实是反过来的,在上一篇已经讲过。
对于从输入层到隐藏层(投影层),这一步比CBOW简单,由于只有一个词,所以,即x_w就是词w对应的词向量。
第二步,通过梯度上升法来更新我们的\theta_{j-1}^wx_w,注意这里的x_w周围有2c个词向量,此时如果我们期望P(x_i|x_w), i=1,2...2c最大。此时我们注意到由于上下文是相互的,在期望P(x_i|x_w), i=1,2...2c最大化的同时,反过来我们也期望P(x_w|x_i), i=1,2...2c最大。那么是使用P(x_i|x_w)好还是P(x_w|x_i)好呢,word2vec使用了后者,这样做的好处就是在一个迭代窗口内,我们不是只更新x_w一个词,而是x_i, i=1,2...2c2c个词。这样整体的迭代会更加的均衡。因为这个原因,Skip-Gram模型并没有和CBOW模型一样对输入进行迭代更新,而是对2c个输出进行迭代更新。
这里总结下基于Hierarchical Softmax的Skip-Gram模型算法流程,梯度迭代使用了随机梯度上升法:
输入:基于Skip-Gram的语料训练样本,词向量的维度大小M,Skip-Gram的上下文大小2c,步长为\eta
输出:霍夫曼树的内部节点模型参数\theta,所有的词向量w
1.基于语料训练样本建立霍夫曼树。
2.随机初始化所有的模型参数\theta,所有的词向量w
3.进行梯度上升迭代过程,对于训练集中的每一个样本(w, context(w))做如下处理:
   a) for i =1 to 2c:
      i) e=0
      ii)for j = 2 to l_w, 计算:
         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
   b)如果梯度收敛,则结束梯度迭代,算法结束,否则回到步骤a继续迭代。

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

Negative Sampling就是这么一种求解word2vec模型的方法,它摒弃了霍夫曼树,采用了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个负例(context(w),wi)i=1,2,..neg。为了统一描述,我们将正例定义为w0。
在逻辑回归中,我们的正例应该期望满足:
      P(context(w_0), w_i) = \sigma(x_{w_0}^T\theta^{w_i}) ,y_i=1, i=0
我们的负例期望满足:
      P(context(w_0), w_i) =1- \sigma(x_{w_0}^T\theta^{w_i}), y_i = 0, i=1,2,..neg
我们期望可以最大化下式:
      \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}))
利用逻辑回归和上一节的知识,我们容易写出此时模型的似然函数为:
      \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 = \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类似,我们采用随机梯度上升法,仅仅每次只用一个样本w0更新梯度,来进行迭代更新得到我们需要的xwi,θwi,i=0,1,..neg, 这里我们需要求出xw0,θwi,i=0,1,..neg的梯度。
首先我们计算θwi的梯度:
      \begin{align} \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{align}
同样的方法,我们可以求出xw0的梯度如下:
      \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}
有了梯度表达式,我们就可以用梯度上升法进行迭代来一步步的求解我们需要的xw0,θwi,i=0,1,..neg。

Negative Sampling负采样方法
前面的求梯度没什么好说的,关键是怎么进行负采样的呢?这里我们本质上采用基于词频的划分方法。
现在我们来看看如何进行负采样,得到neg个负例。word2vec采样的方法并不复杂,如果词汇表的大小为V,那么我们就将一段长度为1的线段分成V份,每份对应词汇表中的一个词。当然每个词对应的线段长度是不一样的,高频词对应的线段长,低频词对应的线段短。每个词w的线段长度由下式决定:
      len(w) = \frac{count(w)}{\sum\limits_{u \in vocab} count(u)}
在word2vec中,分子和分母都取了3/4次幂如下:
      len(w) = \frac{count(w)^{3/4}}{\sum\limits_{u \in vocab} count(u)^{3/4}}
采样前,我们将这段长度为1的线段划分成M等份,这里M>>V,这样可以保证每个词对应的线段都会划分成对应的小块。而M份中的每一份都会落在某一个词对应的线段上。在采样的时候,我们只需要从M个位置中采样出neg个位置就行,此时采样到的每一个位置对应到的线段所属的词就是我们的负例词。


如上图,一共有N个词线段(不等长的,与其词频呈线性关系),被平均分成M
在word2vec中,M取值默认为10^8。

基于Negative Sampling的CBOW模型
有了上面Negative Sampling负采样的方法和逻辑回归求解模型参数的方法,我们就可以总结出基于Negative Sampling的CBOW模型算法流程了。梯度迭代过程使用了随机梯度上升法:
输入:基于CBOW的语料训练样本,词向量的维度大小Mcount,CBOW的上下文大小2c,步长η, 负采样的个数neg
输出:词汇表每个词对应的模型参数θ,所有的词向量xw
1.随机初始化所有的模型参数θ,所有的词向量w
2.对于每个训练样本(context(w0),w0),负采样出neg个负例中心词wi,i=1,2,...neg
3.进行梯度上升迭代过程,对于训练集中的每一个样本(context(w0),w0,w1,...wneg)做如下处理:
   a) e=0, 计算x_{w_0}= \frac{1}{2c}\sum\limits_{i=1}^{2c}x_i
   b) for i= 0 to neg, 计算:
      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) 对于context(w)中的每一个词向量xk(共2c个)进行更新:
      x_k = x_k + e
   d) 如果梯度收敛,则结束梯度迭代,否则回到步骤3继续迭代。

基于Negative Sampling的Skip-Gram模型
有了上一节CBOW的基础和上一篇基于Hierarchical Softmax的Skip-Gram模型基础,我们也可以总结出基于Negative Sampling的Skip-Gram模型算法流程了。梯度迭代过程使用了随机梯度上升法:
输入:基于CBOW的语料训练样本,词向量的维度大小Mcount,CBOW的上下文大小2c,步长η, 负采样的个数neg
输出:词汇表每个词对应的模型参数θ,所有的词向量xw
1.随机初始化所有的模型参数θ,所有的词向量w
2.对于每个训练样本(context(w0),w0),负采样出neg个负例中心词wi,i=1,2,...neg
3.进行梯度上升迭代过程,对于训练集中的每一个样本(context(w0),w0,w1,...wneg)做如下处理:
a) for i =1 to 2c:
   i) e=0
   ii) for j= 0 to neg, 计算:
      f = \sigma(x_{w_{0i}}^T\theta^{w_j})
      g = (y_i-f)\eta
      e = e + g\theta^{w_j}
      \theta^{w_j}= \theta^{w_j} + gx_{w_{0i}}
   iii) 对于词向量w0i(共1个)进行更新:
      x_{w_{0i}} = x_{w_{0i}} + e
d) 如果梯度收敛,则结束梯度迭代,否则回到步骤3继续迭代。


一些细节:

注意!!word2vec要训练两组参数:一个是网络隐藏层的参数,一个是输入单词的参数(1*dim)

在skip gram和CBOW中,中心词词向量在迭代过程中是不会更新的,只更新窗口词向量,这个中心词对应的词向量需要下一次在作为非中心词的时候才能进行迭代更新。

参考文章
1.刘建平老师的博客园,地址
2.bitcarmanlee的CSDN博客

上一篇下一篇

猜你喜欢

热点阅读