词向量—Word2Vec入门及Gensim实践

在机器学习领域,语音识别和图像识别都比较容易做到。语音识别的输入数据可以是音频频谱序列向量所构成的matrix,图像识别的输入数据是像素点向量构成的矩阵。但是文本是一种抽象的非结构化的数据,显然不能直接把文本数据喂给机器当做输入,因此这里就需要对文本数据进行处理。

Word2vec是google在2013年推出的一个词向量实现工具(注意,不是词向量模型),它的特点是将所有的词向量化,这样词与词之间就可以定量的去度量他们之间的关系.  本文将从统计语言模型开始,尽可能详细地介绍word2vec工具背后的算法模型。

词向量的表现形式主要分为两种,一种是one-hot(one-hot representation)表示方式,将词表示成一个很长的向量,向量的长度就是词典的长度;另一种表示方法是分布式表示(distributed representation)。同时,分布式表示方法又可以分为基于矩阵的表示方法、基于聚类的表示方法和基于神经网络的表示方法。

one-hot表示

one-hot表示方法,将词表示成一个很长的向量,向量的分量只有一个1,其他全为0,1。词典索引的下标对应的方向上是1,其余方向上都是0。例如,对于一个大小为5的词典:{"I", "love", "nature", "luaguage", "processing"},“nature”对应的one-hot向量为:[0,0,1,0,0]。显然,one-hot向量的维度等于词典的大小。这在动辄上万甚至百万词典的实际应用中,面临着巨大的维度灾难问题(the curse of dimensionality)

这样表示有两个缺点:

  1. 容易受维度灾难(the curse of dimentionality)的困扰;

  2. 没有考虑到词之间的关系(similarity)。

分布式表示

       统计语言模型

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

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

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

        N-gram模型

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

常见的如bigram模型(N=2)和trigram模型(N=3)。事实上,由于模型复杂度预测精度的限制,我们很少会考虑N>3的模型。

这种方法下,就可以用最大似然法去求解Ngram模型的参数,等价于去统计每个Ngram的条件词频。

为了避免统计中出现的零概率问题(一段从未在训练集中出现过的Ngram片段会使得整个序列的概率为0),人们基于原始的Ngram模型进一步发展出了back-off trigram模型(用低阶的bigram和unigram代替零概率的trigram)和interpolated trigram模型(将条件概率表示为unigram、bigram、trigram三者的线性函数)[3]

N-gram模型不足:

  1.  参数空间的爆炸式增长,它无法处理更长程的context(N>3)。
  2. 它没有考虑词与词之间内在的联系性。例如,考虑"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模型做不到。
  3. N-gram本质上是将词当做一个个孤立的原子单元(atomic unit)去处理的。这种处理方式对应到数学上的形式是一个个离散的one-hot向量。

       神经网络语言模型

鉴于Ngram等模型的不足,2003年,Bengio等人发表了一篇开创性的文章:《A neural probabilistic language model 》。在这篇文章里,他们总结出了一套用神经网络建立统计语言模型的框架(Neural Network Language Model,NNLM),并首次提出了word embedding的概念,从而奠定了包括word2vec在内后续研究word representation learning的基础。

NNLM模型的基本思想如下:

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

Bengio等人《A neural probabilistic language model》论文中采用了一个简单的前向反馈神经网络 f(wt−n+1,...,wt) 来拟合一个词序列的条件概率p(wt|w1,w2,...,wt−1)。整个模型的网络结构见下图:

 

模型的整个网络结构我们可以得到:

  1. 首先是一个线性的embedding层。它将输入的 N−1个one-hot词向量,通过一个共享的D×V的矩阵C,映射为N−1个分布式的词向量(distributed vector)。其中,V是词典的大小,D是embedding向量的维度(一个先验参数)。C矩阵里存储了要学习的word vector。
  2. 其次是一个简单的前向反馈神经网络g。它由一个tanh隐层和一个softmax输出层组成。通过将embedding层输出的N−1个词向量映射为一个长度为V的概率分布向量,从而对词典中的word在输入context下的条件概率做出预估:

通过最小化一个cross-entropy的正则化损失函数来调整模型的参数θ:

其中,模型的参数θ包括了embedding层矩阵C的元素和前向反馈神经网络模型g里的权重。这是一个巨大的参数空间。不过,在用SGD学习更新模型的参数时,并不是所有的参数都需要调整(例如,未在输入的context中出现的词对应的词向量)。计算的瓶颈主要是在softmax层的归一化函数上(需要对词典中所有的word计算一遍条件概率)

NNLM 相比较 N-gram优点:

  • 通过引入连续的词向量和平滑的概率模型,我们就可以在一个连续空间里对序列概率进行建模,从而根本上缓解数据稀疏性维度灾难的问题。
  • 另一方面,以条件概率p(wt|context)为学习目标去更新词向量的权重,具有更强的导向性,同时也与VSM里的Distributional Hypothesis不谋而合。

CBoW & Skip-gram 模型

NNLM 模型不足

  • 同N-gram模型一样,NNLM模型只能处理定长的序列。03年,Bengio等人在其论文《A neural probabilistic language model》将模型能够一次处理的序列长度N提高到了55,虽然相比bigram和trigram已经是很大的提升,但依然缺少灵活性。因此,2010年,Mikolov等人《Recurrent neural network based language model》中提出了一种RNNLM模型,用递归神经网络RNN代替原始模型里的前向反馈神经网络,并将embedding层与RNN里的隐藏层合并,从而解决了变长序列的问题。
  • NNLM 模型训练太慢。softmax层在归一化时需要对词典中所有的word计算一遍条件概率即便是在百万量级的数据集上,即便是借助了几十个CPU进行训练,NNLM也需要耗时数周才能给出一个稍微靠谱的结果。显然,对于现在动辄上千万甚至上亿的真实语料库,训练一个NNLM模型几乎是一个impossible mission。

面对NNLM的不足,Mikolov 继续对此进行了。他注意到,原始的NNLM模型的训练其实可以拆分成两个步骤:

  1. 用一个简单模型训练出连续的词向量
  2. 基于词向量的表达,训练一个连续的N-gram神经网络模型

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

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

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

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

得到的模型称之为CBOW模型(Continuous Bag-of-Words Model)。

从直观上理解,Skip-Gram是给定input word来预测上下文或者说在其附近的词神经网络将会计算出我们从词汇表中选出的每个“候选”邻居词的概率。实际上在算法里有一个"window size"参数,用来控制窗口大小,如果选择参数的值为6,则会预测该词前后各6个词的概率。而CBOW是给定上下文,来预测input word。

skip-gram模型是十分简单的。我们将会训练一个只有一个隐藏层的简单的神经网络来完成我们的任务,但是当神经网络训练完成以后,我们实际上并不使用这个网络做什么,而是获得隐藏层的权重矩阵,这个矩阵实际上就是我们需要得到的词向量(word vectors)。

将Skip-gram模型的前向计算过程的数学形式:

其中,Vi是embedding层矩阵里的向量,也被称为wi的input vector。Uj是softmax层矩阵里的向量,称为wj的output vector。

        然而,直接对词典里的V个词计算 相似度 归一化,显然是一件极其耗时的 impossible mission。 我们的词汇表一般在百万级别以上,这意味着我们DNN的输出层需要进行softmax计算各个词的输出概率的的计算量非常大(词汇表中的词都需计算一遍),有没有简化一点点的方法呢?为此,Mikolov引入了两种优化算法:层次Softmax(Hierarchical Softmax)和负采样(Negative Sampling)。

层次Softmax

层次Softmax是一个很巧妙的模型,最早由Bengio在05年引入到语言模型中。它通过构造一颗二叉树,将目标概率的计算复杂度从最初的V降低到了log⁡V的量级层次,它的基本思想是将复杂的归一化概率分解为一系列条件概率乘积的形式:

其中,每一层条件概率对应一个二分类问题,可以通过一个简单的逻辑回归函数去拟合。这样,我们将对 V个词的概率归一化问题,转化成了对 logV 个词的概率拟合问题。

我们可以通过构造一颗分类二叉树来直观地理解这个过程。首先,我们将原始字典D划分为两个子集D1、D2,并假设在给定context下,target word属于子集D1的概率p(wt∈D1|context)服从logistical function的形式

其中,UDroot和Vwt都是模型的参数

接下来,对子集D1和D2进一步划分。重复这一过程,直到集合里只剩下一个word。这样就将原始大小为V的字典D转换成了一颗深度为log⁡V的二叉树。树中的叶子节点与原始字典里的word依次对应;非叶节点则对应着某一类word的集合。显然,从根节点出发到任意一个叶子节点都只有一条唯一路径,这条路径也编码了这个叶子节点所属的类别。

同时,从根节点出发到叶子节点也是一个随机游走的过程。因此,我们可以基于这颗二叉树对叶子节点出现的似然概率进行计算。例如,对于训练样本里的一个target word (wt),假设其对应的二叉树编码为{1,0,1,...,1},则我们构造的似然函数为:

乘积中的每一项都是一个逻辑回归的函数。通过最大化这个似然函数来求解二叉树上的参数——非叶节点上的向量,用来计算游走到某一个子节点的概率。

因此,构造一颗最优的二叉树就显得十分重要。在实际的应用中,最先优化使用的数据结构是用 霍夫曼树 来代替隐藏层和输出层的神经元,霍夫曼树的内部节点则起到隐藏层神经元的作用; 叶子节点起到输出层神经元的作用,叶子节点的个数即为词汇表的大小 。

具体如何用 霍夫曼树 来进行CBOW和Skip-Gram的训练,我们先复习下霍夫曼树。

Word2vec—霍夫曼树

1. 霍夫曼树的建立

    霍夫曼树的建立其实并不难,过程如下:

    输入:权值为(w1,w2,...wn)的n个节点

    输出:对应的霍夫曼树

    1)将(w1,w2,...wn)看做是有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合并,得到新子树,依次类推,最终得到下面的霍夫曼树。

 2. 霍夫曼树有什么好处

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

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

负采样

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

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

式中的p(wt+j|wt)是一个在整个字典上归一化了的概率。

而在NCE算法中,构造了这样一个问题:对于一组训练样本<context, word>,我们想知道target word的出现,是来自于context 的驱动,还是一个事先假定的背景噪声的驱动?显然,我们可以用一个逻辑回归的函数来回答这个问题:

这个式子给出了一个target word (w)来自于context驱动的概率。其中,k是一个先验参数,表示噪声的采样频率。p(w|context)是一个非归一化的概率分布,这里采用softmax归一化函数中的分子部分,pn(w)则是背景噪声的词分布。通常采用word的unigram分布。

通过对噪声分布的k采样,我们得到一个新的数据集:<context, word, label>。其中,label标记了数据的来源(真实数据分布还是背景噪声分布)。在这个新的数据集上,我们就可以用最大化上式中逻辑回归的似然函数来求解模型的参数。

Mikolov在2013年的论文《Linguistic Regularities in Continuous Space Word Representations》里提出的负采样算法[8], 是NCE的一个简化版本。在这个算法里,Mikolov抛弃了NCE似然函数中对噪声分布的依赖,直接用原始softmax函数里的分子 定义了逻辑回归的函数,进一步简化了计算:

此时,模型相应的目标函数变为:

除了这里介绍的 层次Softma x和 负采样 的优化算法,Mikolov在13年的论文里还介绍了另一个trick:下采样(subsampling)。其基本思想是在训练时依概率随机丢弃掉那些高频词

其中,t是一个先验参数,一般取为10^−5。f(w)表示 w在语料中出现的频率。

实验证明,这种下采样技术可以显著提高低频词的词向量的准确度

实践

可参考我之前利用Gensim来处理的搜狗实验室发布的语料

GitHub:https://github.com/YangCharles

参考:

Bengio, Y., Ducharme, R., Vincent, P., & Janvin, C. (2003). A neural probabilistic language model. The Journal of Machine Learning Research, 3, 1137–1155.

Mnih, A., & Kavukcuoglu, K. (2013). Learning word embeddings efficiently with noise-contrastive estimation, 2265–2273.

Mikolov, T., Yih, W., & Zweig, G. (2013). Linguistic Regularities in Continuous Space Word Representations. Hlt-Naacl.

Mikolov, T., Le, Q. V., & Sutskever, I. (2013, September 17). Exploiting Similarities among Languages for Machine Translation. arXiv.org.

猜你喜欢

转载自blog.csdn.net/u010899985/article/details/81510261
今日推荐