word2vec

转自:http://www.cnblogs.com/iloveai/p/word2vec.html 系列参考文献:http://www.cnblogs.com/Determined22/p/5804455.html http://blog.csdn.net/itplus/article/details/37969817 http://techblog.youdao.com/?p=915 http://www.cnblogs.com/tina-smile/p/5176441.html

word2vec前世今生

2013年,Google开源了一款用于词向量计算的工具——word2vec,引起了工业界和学术界的关注。首先,word2vec可以在百万数量级的词典和上亿的数据集上进行高效地训练;其次,该工具得到的训练结果——词向量(word embedding),可以很好地度量词与词之间的相似性。随着深度学习(Deep Learning)在自然语言处理中应用的普及,很多人误以为word2vec是一种深度学习算法。其实word2vec算法的背后是一个浅层神经网络。另外需要强调的一点是,word2vec是一个计算word vector的开源工具。当我们在说word2vec算法或模型的时候,其实指的是其背后用于计算word vector的CBoW模型和Skip-gram模型。很多人以为word2vec指的是一个算法或模型,这也是一种谬误。接下来,本文将从统计语言模型出发,尽可能详细地介绍word2vec工具背后的算法模型的来龙去脉。

Statistical Language Model

在深入word2vec算法的细节之前,我们首先回顾一下自然语言处理中的一个基本问题:如何计算一段文本序列在某种语言下出现的概率?之所为称其为一个基本问题,是因为它在很多NLP任务中都扮演着重要的角色。例如,在机器翻译的问题中,如果我们知道了目标语言中每句话的概率,就可以从候选集合中挑选出最合理的句子做为翻译结果返回。

统计语言模型给出了这一类问题的一个基本解决框架。对于一段文本序列S=w1,w2,...,wT” role=”presentation” style=”position: relative;”>S=w1,w2,...,wTS=w1,w2,...,wT,它的概率可以表示为:

P(S)=P(w1,w2,...,wT)=∏t=1Tp(wt|w1,w2,...,wt−1)” role=”presentation” style=”position: relative;”>P(S)=P(w1,w2,...,wT)=t=1Tp(wt|w1,w2,...,wt1)P(S)=P(w1,w2,...,wT)=∏t=1Tp(wt|w1,w2,...,wt−1)

即将序列的联合概率转化为一系列条件概率的乘积。问题变成了如何去预测这些给定previous words下的条件概率 p(wt|w1,w2,...,wt−1)” role=”presentation” style=”position: relative;”>p(wt|w1,w2,...,wt1)p(wt|w1,w2,...,wt−1)

由于其巨大的参数空间,这样一个原始的模型在实际中并没有什么卵用。我们更多的是采用其简化版本——Ngram模型:

p(wt|w1,w2,...,wt−1)≈p(wt|wt−n+1,...,wt−1)” role=”presentation” style=”position: relative;”>p(wt|w1,w2,...,wt1)p(wt|wtn+1,...,wt1)p(wt|w1,w2,...,wt−1)≈p(wt|wt−n+1,...,wt−1)

常见的如bigram模型( N=2” role=”presentation” style=”position: relative;”>N=2N=2的模型。

我们可以用最大似然法去求解Ngram模型的参数——等价于去统计每个Ngram的条件词频。

为了避免统计中出现的零概率问题(一段从未在训练集中出现过的Ngram片段会使得整个序列的概率为0” role=”presentation” style=”position: relative;”>00),人们基于原始的Ngram模型进一步发展出了back-off trigram模型(用低阶的bigram和unigram代替零概率的trigram)和interpolated trigram模型(将条件概率表示为unigram、bigram、trigram三者的线性函数)。此处不再赘述。感兴趣者可进一步阅读相关的文献[3]。

Distributed Representation

不过,Ngram模型仍有其局限性。首先,由于参数空间的爆炸式增长,它无法处理更长程的context(N>3” role=”presentation” style=”position: relative;”>N>3N>3)。其次,它没有考虑词与词之间内在的联系性。例如,考虑”the cat is walking in the bedroom”这句话。如果我们在训练语料中看到了很多类似“the dog is walking in the bedroom”或是“the cat is running in the bedroom”这样的句子,那么,即使我们没有见过这句话,也可以从“cat”和“dog”(“walking”和“running”)之间的相似性,推测出这句话的概率[3]。然而, Ngram模型做不到。

这是因为,Ngram本质上是将词当做一个个孤立的原子单元(atomic unit)去处理的。这种处理方式对应到数学上的形式是一个个离散的one-hot向量(除了一个词典索引的下标对应的方向上是1” role=”presentation” style=”position: relative;”>11。显然,one-hot向量的维度等于词典的大小。这在动辄上万甚至百万词典的实际应用中,面临着巨大的维度灾难问题(the curse of dimensionality)

于是,人们就自然而然地想到,能否用一个连续的稠密向量去刻画一个word的特征呢?这样,我们不仅可以直接刻画词与词之间的相似度,还可以建立一个从向量到概率的平滑函数模型,使得相似的词向量可以映射到相近的概率空间上。这个稠密连续向量也被称为word的distributed representation[3]。

事实上,这个概念在信息检索(Information Retrieval)领域早就已经被广泛地使用了。只不过,在IR领域里,这个概念被称为向量空间模型(Vector Space Model,以下简称VSM)。

VSM是基于一种Statistical Semantics Hypothesis[4]:语言的统计特征隐藏着语义的信息(Statistical pattern of human word usage can be used to figure out what people mean)。例如,两篇具有相似词分布的文档可以被认为是有着相近的主题。这个Hypothesis有很多衍生版本。其中,比较广为人知的两个版本是Bag of Words Hypothesis和Distributional Hypothesis。前者是说,一篇文档的词频(而不是词序)代表了文档的主题;后者是说,上下文环境相似的两个词有着相近的语义。后面我们会看到,word2vec算法也是基于Distributional的假设。

那么,VSM是如何将稀疏离散的one-hot词向量映射为稠密连续的distributional representation的呢?

简单来说,基于Bag of Words Hypothesis,我们可以构造一个term-document矩阵A” role=”presentation” style=”position: relative;”>AA中出现的次数(或频率)。那么,我们就可以提取行向量做为word的语义向量(不过,在实际应用中,我们更多的是用列向量做为文档的主题向量)。

类似地,我们可以基于Distributional Hypothesis构造一个word-context的矩阵。此时,矩阵的列变成了context里的word,矩阵的元素也变成了一个context窗口里word的共现次数。

注意,这两类矩阵的行向量所计算的相似度有着细微的差异:term-document矩阵会给经常出现在同一篇document里的两个word赋予更高的相似度;而word-context矩阵会给那些有着相同context的两个word赋予更高的相似度。后者相对于前者是一种更高阶的相似度,因此在传统的信息检索领域中得到了更加广泛的应用。

不过,这种co-occurrence矩阵仍然存在着数据稀疏性和维度灾难的问题。为此,人们提出了一系列对矩阵进行降维的方法(如LSI/LSA等)。这些方法大都是基于SVD的思想,将原始的稀疏矩阵分解为两个低秩矩阵乘积的形式。

关于VSM更多的介绍,可以进一步阅读文末的参考文献[4]。

Neural Network Language Model

接下来,让我们回到对统计语言模型的讨论。鉴于Ngram等模型的不足,2003年,Bengio等人发表了一篇开创性的文章:A neural probabilistic language model[3]。在这篇文章里,他们总结出了一套用神经网络建立统计语言模型的框架(Neural Network Language Model,以下简称NNLM),并首次提出了word embedding的概念(虽然没有叫这个名字),从而奠定了包括word2vec在内后续研究word representation learning的基础。

NNLM模型的基本思想可以概括如下:

  1. 假定词表中的每一个word都对应着一个连续的特征向量;
  2. 假定一个连续平滑的概率模型,输入一段词向量的序列,可以输出这段序列的联合概率;
  3. 同时学习词向量的权重和概率模型里的参数。

值得注意的一点是,这里的词向量也是要学习的参数。

在03年的论文里,Bengio等人采用了一个简单的前向反馈神经网络f(wt−n+1,...,wt)” role=”presentation” style=”position: relative;”>f(wtn+1,...,wt)f(wt−n+1,...,wt)。整个模型的网络结构见下图:

the neural network language model

我们可以将整个模型拆分成两部分加以理解:

  1. 首先是一个线性的embedding层。它将输入的N−1” role=”presentation” style=”position: relative;”>N1N−1矩阵里存储了要学习的word vector。
  2. 其次是一个简单的前向反馈神经网络g” role=”presentation” style=”position: relative;”>gg的概率分布向量,从而对词典中的word在输入context下的条件概率做出预估:
    p(wi|w1,w2,...,wt−1)≈f(wi,wt−1,...,wt−n+1)=g(wi,C(wt−n+1),...,C(wt−1))” role=”presentation” style=”position: relative;”>p(wi|w1,w2,...,wt1)f(wi,wt1,...,wtn+1)=g(wi,C(wtn+1),...,C(wt1))p(wi|w1,w2,...,wt−1)≈f(wi,wt−1,...,wt−n+1)=g(wi,C(wt−n+1),...,C(wt−1))

我们可以通过最小化一个cross-entropy的正则化损失函数来调整模型的参数θ” role=”presentation” style=”position: relative;”>θθ

L(θ)=1T∑tlog⁡f(wt,wt−1,...,wt−n+1)+R(θ)” role=”presentation” style=”position: relative;”>L(θ)=1Ttlogf(wt,wt1,...,wtn+1)+R(θ)L(θ)=1T∑tlog⁡f(wt,wt−1,...,wt−n+1)+R(θ)

其中,模型的参数θ” role=”presentation” style=”position: relative;”>θθ里的权重。这是一个巨大的参数空间。不过,在用SGD学习更新模型的参数时,并不是所有的参数都需要调整(例如未在输入的context中出现的词对应的词向量)。计算的瓶颈主要是在softmax层的归一化函数上(需要对词典中所有的word计算一遍条件概率)。

然而,抛却复杂的参数空间,我们不禁要问,为什么这样一个简单的模型会取得巨大的成功呢?

仔细观察这个模型就会发现,它其实在同时解决两个问题:一个是统计语言模型里关注的条件概率p(wt|context)” role=”presentation” style=”position: relative;”>p(wt|context)p(wt|context)为学习目标去更新词向量的权重,具有更强的导向性,同时也与VSM里的Distributional Hypothesis不谋而合。

CBoW & Skip-gram Model

铺垫了这么多,终于要轮到主角出场了。

不过在主角正式登场前,我们先看一下NNLM存在的几个问题。

一个问题是,同Ngram模型一样,NNLM模型只能处理定长的序列。在03年的论文里,Bengio等人将模型能够一次处理的序列长度N” role=”presentation” style=”position: relative;”>NN,虽然相比bigram和trigram已经是很大的提升,但依然缺少灵活性。

因此,Mikolov等人在2010年提出了一种RNNLM模型[7],用递归神经网络代替原始模型里的前向反馈神经网络,并将embedding层与RNN里的隐藏层合并,从而解决了变长序列的问题。

另一个问题就比较严重了。NNLM的训练太慢了。即便是在百万量级的数据集上,即便是借助了40个CPU进行训练,NNLM也需要耗时数周才能给出一个稍微靠谱的解来。显然,对于现在动辄上千万甚至上亿的真实语料库,训练一个NNLM模型几乎是一个impossible mission。

这时候,还是那个Mikolov站了出来。他注意到,原始的NNLM模型的训练其实可以拆分成两个步骤:

  1. 用一个简单模型训练出连续的词向量;
  2. 基于词向量的表达,训练一个连续的Ngram神经网络模型。
    而NNLM模型的计算瓶颈主要是在第二步。

如果我们只是想得到word的连续特征向量,是不是可以对第二步里的神经网络模型进行简化呢?

Mikolov是这么想的,也是这么做的。他在2013年一口气推出了两篇paper,并开源了一款计算词向量的工具——至此,word2vec横空出世,主角闪亮登场。

下面,我将带领大家简单剖析下word2vec算法的原理。有了前文的基础,理解word2vec算法就变得很简单了。

首先,我们对原始的NNLM模型做如下改造:

  1. 移除前向反馈神经网络中非线性的hidden layer,直接将中间层的embedding layer与输出层的softmax layer连接;
  2. 忽略上下文环境的序列信息:输入的所有词向量均汇总到同一个embedding layer;
  3. 将future words纳入上下文环境

得到的模型称之为CBoW模型(Continuous Bag-of-Words Model),也是word2vec算法的第一个模型:

CBoW Model

从数学上看,CBoW模型等价于一个词袋模型的向量乘以一个embedding矩阵,从而得到一个连续的embedding向量。这也是CBoW模型名称的由来。

CBoW模型依然是从context对target word的预测中学习到词向量的表达。反过来,我们能否从target word对context的预测中学习到word vector呢?答案显然是可以的:

Skip-gram Model

这个模型被称为Skip-gram模型(名称源于该模型在训练时会对上下文环境里的word进行采样)。

如果将Skip-gram模型的前向计算过程写成数学形式,我们得到:

p(wo|wi)=eUo⋅Vi∑jeUj⋅Vi” role=”presentation” style=”position: relative;”>p(wo|wi)=eUoVijeUjVip(wo|wi)=eUo⋅Vi∑jeUj⋅Vi

其中, Vi” role=”presentation” style=”position: relative;”>ViVi的output vector。

因此,Skip-gram模型的本质是计算输入word的input vector与目标word的output vector之间的余弦相似度,并进行softmax归一化。我们要学习的模型参数正是这两类词向量。

然而,直接对词典里的V” role=”presentation” style=”position: relative;”>VV个词计算相似度并归一化,显然是一件极其耗时的impossible mission。为此,Mikolov引入了两种优化算法:层次Softmax(Hierarchical Softmax)和负采样(Negative Sampling)。

Hierarchical Softmax[5]

层次Softmax的方法最早由Bengio在05年引入到语言模型中。它的基本思想是将复杂的归一化概率分解为一系列条件概率乘积的形式:

p(v|context)=∏i=1mp(bi(v)|b1(v),...,bi−1(v),context)” role=”presentation” style=”position: relative;”>p(v|context)=i=1mp(bi(v)|b1(v),...,bi1(v),context)p(v|context)=∏i=1mp(bi(v)|b1(v),...,bi−1(v),context)

其中,每一层条件概率对应一个二分类问题,可以通过一个简单的逻辑回归函数去拟合。这样,我们将对 V” role=”presentation” style=”position: relative;”>VV个词的概率拟合问题。

我们可以通过构造一颗分类二叉树来直观地理解这个过程。首先,我们将原始字典D” role=”presentation” style=”position: relative;”>DD服从logistical function的形式:

p(wt∈D1|context)=11+e−UDroot⋅Vwt” role=”presentation” style=”position: relative;”>p(wtD1|context)=11+eUDrootVwtp(wt∈D1|context)=11+e−UDroot⋅Vwt

其中, UDroot” role=”presentation” style=”position: relative;”>UDrootUDroot都是模型的参数。

接下来,我们可以对子集D1” role=”presentation” style=”position: relative;”>D1D1的二叉树。树的叶子节点与原始字典里的word一一对应;非叶节点则对应着某一类word的集合。显然,从根节点出发到任意一个叶子节点都只有一条唯一路径——这条路径也编码了这个叶子节点所属的类别。

同时,从根节点出发到叶子节点也是一个随机游走的过程。因此,我们可以基于这颗二叉树对叶子节点出现的似然概率进行计算。例如,对于训练样本里的一个target word wt” role=”presentation” style=”position: relative;”>wtwt,则我们构造的似然函数为:

p(wt|context)=p(D1=1|context)p(D2=0|D1=1)…p(wt|Dk=1)” role=”presentation” style=”position: relative;”>p(wt|context)=p(D1=1|context)p(D2=0|D1=1)p(wt|Dk=1)p(wt|context)=p(D1=1|context)p(D2=0|D1=1)…p(wt|Dk=1)

乘积中的每一项都是一个逻辑回归的函数。

我们可以通过最大化这个似然函数来求解二叉树上的参数——非叶节点上的向量,用来计算游走到某一个子节点的概率。

层次Softmax是一个很巧妙的模型。它通过构造一颗二叉树,将目标概率的计算复杂度从最初的V” role=”presentation” style=”position: relative;”>VV的量级。不过付出的代价是人为增强了词与词之间的耦合性。例如,一个word出现的条件概率的变化,会影响到其路径上所有非叶节点的概率变化,间接地对其他word出现的条件概率带来不同程度的影响。因此,构造一颗有意义的二叉树就显得十分重要。实践证明,在实际的应用中,基于Huffman编码的二叉树可以满足大部分应用场景的需求。

Negative Sampling[6]

负采样的思想最初来源于一种叫做Noise-Contrastive Estimation的算法[6],原本是为了解决那些无法归一化的概率模型的参数预估问题。与改造模型输出概率的层次Softmax算法不同,NCE算法改造的是模型的似然函数。

以Skip-gram模型为例,其原始的似然函数对应着一个Multinomial的分布。在用最大似然法求解这个似然函数时,我们得到一个cross-entropy的损失函数:

J(θ)=−1T∑t=1T∑−c≤j≤c,j≠0log⁡p(wt+j|wt)” role=”presentation” style=”position: relative;”>J(θ)=1Tt=1Tcjc,j0logp(wt+j|wt)J(θ)=−1T∑t=1T∑−c≤j≤c,j≠0log⁡p(wt+j|wt)

式中的 p(wt+j|wt)” role=”presentation” style=”position: relative;”>p(wt+j|wt)p(wt+j|wt)是一个在整个字典上归一化了的概率。

而在NCE算法中,我们构造了这样一个问题:对于一组训练样本

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值