看到这篇文章才终于被点醒为什么word2vec用W_in 而不用W_out作为最终的vector了:为了优化算法,word2vec的实现都用了负采样 / 层次化softmax方法,所以并不会直接更新理论上W_out输出矩阵,所以这里用W_out意义不大,当然如果不采用 负采样 / 层次化softmax等优化方法,直接用W_out也是可以的。

from: https://shomy.top/2017/07/28/word2vec-all/

目前需要做Network Embedding方面的课题,而复杂网络本身就经常借鉴NLP的一些算法模型, Embedding也不例外. 因此先从Word Embedding入手。之前对Word Embedding(暂且翻译为词嵌入或者词向量)的理解就是将单词根据某种特征转为数值向量,再来做其他工作比如文本分类的工作。而word2vec则是word embedding的一种模型,也是目前使用最广的词向量模型, 由Google的Mikolov团队2013年提出。之前仅仅能够使用第三方库来训练直接使用, 对其中原理并没有多少理解, 这篇博客则比较完整的从背景知识到原理,参数训练等方面整理一下word2Vec。

Mikolov的两篇文章中涉及word2vec的细节甚少. 有不少人都对此模型作出了更加详细的解释, 本文主要沿着Rong, X.word2vec Parameter Learning Explained这篇文章的思路来整理一下,很多公式参考于这篇文章。

参考:

  • Mikolov, T.(2013). Distributed Representations of Words and Phrases and their Compositionality.
  • Mikolov, T.(2013). Efficient Estimation of Word Representations in Vector Space.
  • Rong, X. (2014). word2vec Parameter Learning Explained.

背景

神经网络

引入word2vec之前需要先对神经网络的知识有一定了解, 这里只贴一张图说明一个简单三层神经网络, 具体的细节不再赘述, 可以参考之前的一篇专门介绍神经网络的博文机器学习技法笔记(1)-神经网络 ; 如下图, h, 从隐层到输出层同理. 这样从输入层到输出层既有线性变换,又有非线性变换, 因此可以更好刻画出输入变量的特征.

[转] Word2vec数学原理全家桶

神经语言模型

作为Word Embedding的背景, 语言模型(Language Model)也是很有必要简要介绍一下.

统计语言模型就是用来计算一个句子的概率分布

简单来说,就是计算一个句子的概率, 语言模型用处很广泛,比如机器翻译中, 如何挑选一个概率尽可能大的句子也就是尽量靠谱的句子返回. 假设一个长度为), 那么这个句子的概率,也就是这m个词共现的概率:

)

 

 

一般情况, 语言模型都是为了使得条件概率: 最大化, 不过考虑到近因效应, 当前词与距离它比较近的n个词更加相关(一般n不超过5),而非前面所有的词都有关, 因此上述公式可以近似为:

)

 

 

上述便是经典的n-gram模型的近似表示方式. 下面需要介绍一下神经语言模型(NNLM), 最初由Bengio提出的A Neural Probabilistic Language Mode最为经典, word2vec便是从其中简化训练而来. Bengio通过下面的一个三层神经网络来计算:

[转] Word2vec数学原理全家桶

首先第一层输入就是前, 这里使用使用softmax函数归一化输出层的值到[0,1], 代表可能的每个词的概率. 此外在原文中, 存在一些直连边, 也就是上图中的虚线, 从输入层直接到输出层, 是一个线性变换, Bingo在文中表示, 直连边的存在大幅降低迭代次数, 但对语言模型效果无提升, 随着计算能力的提高, 后续的工作基本都去掉了直连边.

神经语言模型构建完成之后,就是训练参数了. 这里的参数包括词向量矩阵, 以及三层神经网络的权重, 偏置等参数. 训练数据就是大堆大堆的预料库. 训练结束之后, 语言模型得到了, 词向量也得到了. 换言之, 词向量是这个语言模型的副产品. 但是这个模型的缺点就是速度问题, 因为词汇表往往很大,几十万几百王, 训练起来就很耗时, Bengo仅仅训练5个epoch就花了3周, 这还是40个CPU并行训练的结果. 因此才会有了后续好多的优化工作, word2vec便是其中一个.

Word2Vec

简介

背景介绍完毕, 终于到主角了. word2vec是google于2013年的Distributed Representations ofWords and Phrases and their Compositionality 以及后续的Distributed Representations ofWords and Phrases and their Compositionality 两篇文章中提出的一种高效训练词向量的模型, 基本出发点是上下文相似的两个词,它们的词向量也应该相似, 比如香蕉和梨在句子中可能经常出现在相同的上下文中,因此这两个词的表示向量应该就比较相似.

word2vec模型中比较重要的概念是词汇的上下文, 说白了就是一个词周围的词, 比如. 在word2vec中提出两个模型(假设上下文窗口为3)

  • CBOW(Continuous Bag-of-Word): 以上下文词汇预测当前词: 
  • SkipGram: 以当前词预测其上下文词汇: 

两个模型图示如下

[转] Word2vec数学原理全家桶

下面将会从最简单的上下文只有一个词的情形入手, 然后扩展到CBOW以及Skip-gram, 介绍原理以及参数训练过程. 关于word2vec的训练这里将会从完全的BP神经网络的过程来介绍.

One-Word Model

首先先看简化版入手: 输入输出都只有一个词, 如下图示:

[转] Word2vec数学原理全家桶

首先说明符号:

  • : 隐层神经元个数, 同时也是词向量维度

  • : 输入层到隐层的权重矩阵, 其实就是词向量矩阵,其中每一行代表一个词的词向量

  • : 隐层到输出层的权重矩阵, 其中每一列也可以看作额外的一种词向量

下面从神经网络的前向过程开始介绍:

我们需要做的是用输入的词去预测输出的词. 其中 输入层的单词表示,来作为隐层的值,注意word2vec的隐层并没有激活函数:

I

 

 

然后考虑从隐层的:

h

 

 

其中:

)

 

 

其中,下面就是重点了,介绍如何根据语料库来训练模型,更新参数,得到最终的词向量。


首先明确训练数据的格式,对于一个训练样本(是真实单词在词汇表中的下标,那么根据最大似然或者上面的语言模型,目标函数可以定义如下:

)

 

 

一般我们习惯于最小化损失函数,因此定义损失函数:

)

 

 

然后结合反向传播一层层求梯度,使用梯度下降来更新参数。

先求隐层到输出层的向量矩阵的梯度:

i

 

 

这里面的项。

考虑: 直接对原始求导,如下:

先考虑E的对数部分: 

再看 $ -u_{j^{}}=0= -t_j$

所以综合求导项为预测值与真实值的差

因此梯度下降更新公式为:

i

 

 

整合为的形式如下:

}

 

 

也就是说对每个训练样本都需要做一次复杂度为.

接着考虑隐层:

P

 

 

其中:

P

 

 

得到一个N维的向量,上面已经介绍过,v_{w_I}$的更新公式为:

P

 

 

到此为止, 一个训练样本的反向传播训练过程就为止了。 我们可以看到,对于输入层到隐层的矩阵个元素都需要更新一遍,这里的计算量还是很大的。

这一节主要比较细致的介绍了最简单的输入输出只有一个单词的情况的推理和训练的过程,后面的CBOW(上下文预测单词)以及SG(单词预测上下文)均基于这一节扩展开来。

CBOW Model

这一部分讲word2vec的第一个形式: Continurous Bag-Of-Word,模型图示如下:

[转] Word2vec数学原理全家桶

跟上一个模型唯一的不同就是输入不再是一个词中输入的所有C个单词的词向量,然后直接取平均,如下:

T

 

 

后面隐层到输出层的过程与One-Word Model 一模一样,包括目标函数定义, 反向传播训练等。将的更新公式照抄下来如下,依旧是每次都需要更新所有的行:

}

 

 

隐层神经元的梯度也相同:

P

 

 

下面考虑输入层到隐层稍微有些不同,在One-Word Model里面因为输入只有一个词,因此每次训练只更新这个词对应到中的C行,如下:

C

 

 

到此为止 CBOW 的推理和训练过程也介绍完毕,基本跟One-Word Model 一样。

SkipGram Model

现在开始介绍word2vec的第二种形式: SkipGram(根据单词预测上下文),这个模型与One-Word Model不同的地方在于,SG的输出有多个词,而非One-Word 中输出只有一个词,这样输出层就不是一个多项分布了,而是个多项分布了,模型图示如下:

[转] Word2vec数学原理全家桶

因此从输入层到隐层部分与One-Word Model 相同,隐层神经元的计算方式如下:

I

 

 

因为输出层是有C个单词, 因此有C个多项分布: 个输出单词的预测的多项分布中第j项,相比One-Word Model 多了一个c参数:

)

 

 

需要主要的是这的第j列,同时也是词汇表中第j个单词的一种词向量(虽然实际中不用)。

从前向后 根据上述公式计算出C个输出向量之后,在每个个输出单词,也就达到了根据单词预测上下文的目的。

下面开始介绍SG的反向传播训练的过程,这个跟前面的有些许的不同, 首先是损失函数:

)

 

 

前面说过输出的C个词是相互独立,因此 一样,都代表训练的真实的输出单词在词汇表的下标。下面开始从后向前求梯度,对第c个词对应的多项分布的第j项的梯度:

j

 

 

然后考虑的梯度,考虑到C个多项分布产生影响,因此需要求和:

i

 

 

跟CBOW一样,为了方便书写定义

有了梯度,就可以利用梯度下降 更新:

i

 

 

或者写成词向量的形式,其实就是的一列:

V

 

 

接着考虑对隐层神经元的梯度:

Q

 

 

因此跟One-Word Model一样整合成向量的形式:

Q

 

 

考虑到输入只有一个词,因此跟One-Word Model 一样: 的一行:

Q

 

 

到此SkipGram模型的前向推理与后向参数的训练也介绍完毕了。

优化

复杂度

前面的CBOW与SG模型是标准的word2vec模型,或者说是神经语言模型的简化版,去掉了隐层的激活函数,其余的变化不大,因此训练效率还是很低的。我们分析下训练的复杂度。首先明确需要学习的两个词向量矩阵个单词都需要更新词向量,考虑现实任务词汇表一般是几十万,上百万千万级别的, 这个计算成本是巨大的。

关于计算成本大的原因,除了上面提到的训练部分,还有就是在每次前向计算的时候,隐层到输出层的softmax函数计算输出层个元素,计算量也是很大,这样整个模型现实意义不大。

考虑到计算量大的部分都是在隐层到输出层上,尤其是作为最终词向量。

在多一句,其实上述训练和推理的复杂度很大的根本原因是softmax的分母上的次的计算。因此下面的两种方法其实是对softmax的优化,不仅仅限制在word2vec.

两种优化方式使得word2vec的训练速度大大提升,并且词向量的质量几乎没有下降,这也是word2vec在NLP领域如此流行的原因。 下面依次介绍这两种优化算法。

Hierarchical SoftMax

首先Hierarchical SoftMax(HS)并不是word2vec提出来的, 而是之前Bengio在2005年最早提出来专门为了加速计算神经语言模型中的softmax的一种方式, 这里介绍如何在word2vec中使用. HS主要基于哈夫曼树(一种二叉数)将计算量大的部分变为了一种二分类的问题. 先看下面的图, 原来的模型在隐层之后通过直接与下面的二叉树的root节点相连:

[转] Word2vec数学原理全家桶

其中图中白色的叶子节点表示词汇表中所有的, 结合向量的内积, 来判断该向左还是向右, 如下, 第n个节点向左 以及向右的概率定义:

)

 

 

有了上述的概率, 我们可以重新定义了:

)

 

 

其中.

举个例子, 比如上图中的加粗的黑色路径: 概率最大:

)

 

 

并且在一个非叶子节点处, 向左向右的概率和为1, 因此一直分裂下去,最后的和肯定还是1. 因此可以很容易得到:

1

 

 

这一点的证明是有必要的, 因为在原始的softmax本身保证了所有单词的概率和是1, 而通过上式也知道了通过HS得到的输出层仍然是一个概率多项分布, 输出所有的单词概率和为1.

讲完了从前向后的如何得到输出单词的概率的过程, 下面开始后向传播的训练过程. 首先需要明确的是训练的参数: 输入层与隐层的词向量矩阵.

为了书写方便,下面简化一部分的符号: 用

对于一组训练数据, 损失函数的定义与前面相同, 最大似然(注意这里以One-Word Model为例,CBOW与Skip-Gram按照上述的思路完全一样):

)

 

 

之后便可以逐项求梯度了, 先考虑]

 

 

之后对很类似, 可以理解为预测值与实际值的差别。

有了上述的梯度,就可以很简单的求出的梯度了:

h

 

 

有了梯度,便可以更新了, 具体公式还是梯度下降:

1

 

 

也就是说对于一个训练样本, 我们只需要更新个, 空间复杂度相同, 但是时间复杂度却大大降低。

然后考虑隐层,因此需要对梯度求和:

j

 

 

其实跟前面很一样了, 只需要替代下导数就好了, 后面就不再赘述了。

整个Hierarchical Softmax的优化算法介绍完了,隐层到输出层的计算量从.

Negative Sampling

相比Hierarchical Softmax用复杂的树形结构来对softmax进行优化, 负采样(Negative Sampling)更加直接简单。因为softmax的存在,在使用梯度下降的时候,每个训练样本需要更新个(一般来说V都是几万到几百万的量级)。

再来考虑原始模型的训练过程,对于一个训练样本, 正例就是number这个词, 负例就是不太可能与phone共同出现的词。

负采样的思想便是每次训练只随机取一小部分的负例使他们的概率最小,以及对应的正例概率最大。那么如何对负例进行抽样呢? 这里就需要定义一个noise distribution,有了分布就可以依据概率分布进行带权随机抽样了。 在word2vec中,作者直接使基于词的频次的词的权重分布:

0.75

 

 

相比于直接使用频次作为权重, 取0.75幂的好处可以减弱不同频次差异过大带来的影响,使得小频次的单词被采样的概率变大。

下面基于上面的思想,直接给出具体的损失函数:

)

 

 

是负例单词集合。 上述公式跟原本的负采样的有些许差别,具体的推导细节可以参考这篇Goldberg, Y. and Levy, O. (2014). word2vec explained: deriving mikolov et al.’s negative- sampling word-embedding method. 这里不再赘述了。 只需要记住,NS也是对softmax函数做的优化,除了word2vec,在其他地方涉及到softmax的均可以采用类似的思想来重写目标函数。

有了损失函数,然后跟HS一样,用梯度下降的方法更新来讨论,最终得到的梯度公式与HS一模一样:

j

 

 

这里的的公式:

g

 

 

从公式也可以看出,对于每个训练数据,每次只需要更新很少的几个向量,而非原始的个,大大降低了训练复杂度。

跟HS相同,NS的优化也是对隐层到输出层的优化,因此前面输入层到隐层的梯度只需要做相应修改即可,没有影响,具体可以参考HS的公式,这里不再重复了。

到此为止,两种针对softmax的优化方式介绍完毕,简单对比一下两种方式:

  • HS实现比较复杂, 需要借助哈夫曼树,而且HS是专门优化softmax的方法
  • NS相对简单,仅仅是简单采样,NS则是一种概率采样的方法,属于NCE(Noise Contrastive Estimation)的一种简单形式,代码实现也很简单。 训练得到的词向量质量也很高,相对常用一些。

后话

这篇博客侧重word2vec的数学原理,主要是以神经网络的反向传播思路来解释训练过程,以及介绍了优化的原因和思路,基本上相当于一份对w2v相对全面解释的全家桶了,除了具体的代码实现细节,关于代码实现的话,以后看有没有时间整理吧。google官方的c实现的word2vec的代码还是非常非常值得一读的,只有600多行,里面用到了很多技巧,包括快速计算, 还有负采样的实现等等,使用异步梯度下降多线程训练(ASGD)等。

在4月份花了几天时间看完了google的两篇原始paper以及word2vec Parameter Learning Explained这篇以后,就想结合论文按照自己的理解整理一下, 不过当时仅仅列出了目录,写了个开头背景,之后因为课程,读NE的paper等各种事情,一直拖着到现在了。 这段时间趁出差期间反正没事,现在算是就把这个坑补上了。 有什么写的不严谨的地方,烦请指出。

相关文章: