当前位置:   article > 正文

词向量Word2Vec(深度细致分析)_word2vec词向量

word2vec词向量

本文以博客园刘建平Pinard对于word2vec的解释为基础,同时参考了其他相关博客的分析并加入了自己的理解,希望站在巨人的肩膀上进行一定的学习输出。至于本片文章的属性,个人认为是伪原创吧,有需要的同学可以自行转到相应的链接。
word2vec是google在2013年推出的一个NLP工具,它的特点是将所有的词向量化,这样词与词之间就可以定量的去度量他们之间的关系,挖掘词之间的联系。有兴趣可以看看word2vec在github上的源码

一,自然语言的基础——词向量
自然语言是一套用来表达含义的复杂系统。在这套系统中,词语(这里常见的是把词语理解为英文中的单词或者汉语中的字)是表义的基本单元。而词语是人类的抽象总结,是符号形式的。而计算机的基础是数学,是不能直接处理抽象符号的,所以一个很自然的想法是能不能把人类的抽象符号词语转换为数学语言以供计算机使用呢?答案是可以的,也就是把抽象的词语转换成数值形式,或者说嵌入到一个数学空间里,这种嵌入方式,就叫词嵌入(word embedding),而 word2vec,就可以认为是词嵌入( word embedding) 的一种(准确的说word2vec整个流程是处理语义联系的,只不过在这个处理流程的产物之一便是词向量)。简单点来说就是把一个词语转换成对应向量的表达形式,来让计算机读取数据。
用词向量来表示词并不是word2vec的首创,在很久之前就出现了。最早的词向量是使用哑编码(one-hot)产生的,它所产生的词向量维度大小为整个词汇表的大小,对于每个具体的词汇表中的词,将对应的位置置为1。比如我们有下面的5个词组成的词汇表,词"Queen"的序号为2, 那么它的词向量就是(0,1,0,0,0)。同样的道理,词"Woman"的词向量就是(0,0,0,1,0)。这种词向量的编码方式我们一般叫做1-of-N representation或者one hot representation.
在这里插入图片描述
哑编码表示(One hot representation)用来表示词向量非常简单,但是却有很多问题。最大的问题是我们的词汇表一般都非常大,比如英语单词总量达到百万级别(百度百科显示英语单词总数为99万),这样每个词都用百万维的向量来表示简直是内存的灾难,而且也带来了巨量的计算。并且这样的向量其实除了一个位置是1,其余的位置全部都是0,表达的效率不高,能不能把词向量的维度变小呢?

词的分布式表示(Distributed representation)可以解决One hot representation的问题,它的思路是通过训练,将每个词都映射到一个较短的词向量上来。所有的这些词向量就构成了向量空间,进而可以用普通的统计学的方法来研究词与词之间的关系。这个较短的词向量维度是多大呢?这个一般需要我们在训练时自己来指定。
比如下图我们将词汇表里的词用"Royalty",“Masculinity”, "Femininity"和"Age"4个维度来表示(这四个单词的意思分别是王权,男性,女性,年龄),King这个词对应的词向量可能是(0.99,0.99,0.05,0.7)。大致可以理解为国王为具有王权的男性,这样就从原来非常稀疏的one hot产生的词向量转变成了现在的稠密向量,大大节约了内存和减少了计算量。当然在实际情况中,我们并不能对词向量的每个维度做一个很好的解释。
在这里插入图片描述
有了用Distributed Representation表示的较短的词向量,我们就可以较容易的分析词之间的关系了,比如我们将词的维度降维到2维,有一个有趣的研究表明,用下图的词向量表示我们的词时,我们可以发现:
在这里插入图片描述
在这里插入图片描述
可见我们只要得到了词汇表里所有词对应的词向量,就可以用数学的方式来计算词语之间的关系了,进而也就可以进一步的对自然语言进行处理了。那么关键是如何训练得到合适的词向量呢?一个很常见的方法是使用神经网络语言模型。

二,神经网络语言模型
神经网络语言模型采用的方法一般是三层的神经网络结构(当然也可以多层),分为输入层,隐藏层和输出层(softmax层)。从数据输入输出的角度看,该模型分为连续词袋模型(Continuous Bag-of-Words,简称CBOW) 与和跳字模型(Skip-Gram)两种模型。
CBOW的训练模型如图所示
在这里插入图片描述
图中的V表示文档词汇表的大小,也即单词的维度(单词维度与词汇表大小一致),k表示第几个中心词(k<V),C表示中心词的上下文单词数量,也就是窗口的2倍,N表示我们希望生成的稠密词向量的维度。
CBOW模型的训练输入是某一个中心词的上下文相关的词对应的词向量,而输出就是这中心词的词向量。比如下面这段话,结合上图的CBOW训练图,
在这里插入图片描述
1,输入层:假设该句子所在的文档的词汇表是V = 1万,我们的上下文大小取值为4(也即窗口为4),中心词是"Learning",也就是我们需要的输出词向量,上下文对应的词有8个,前后各4个,每个词由于均采用one-hot编码,每个单词向量均有1万维,形状为1*V,这8个one-hot词向量是我们模型的输入。
2,然后每个词向量乘以一个共享的输入权重矩阵W,该矩阵的形状为V*N(N<<V,是我们人为设置的数,由此可对稀疏的one-hot原始词向量进行显著的降维,这里假设V=200)。相乘之后每个单词的词向量形状为1*N
3,隐藏层:将这8个单词的词向量在列的方向上拼接,形成一个向量,形状为1*8N
4,将这个向量再乘以输出权重矩阵W`,形状为8N*V(注意此时图中矩阵的形状有误),得到一个形状为1*V的向量。
5,上述得到的向量经过softmax处理,得到预测中心词属于词汇表每个词概率值。
6,概率最大的index所指示的单词为预测出的中心词,该预测中心词与真实中心词(true label)作比较得出误差值,通过反向传播不断减小该误差值。
所以,需要定义loss function(一般为交叉熵损失函数),采用梯度下降法(或者梯度上升法)更新W和W’。训练完毕后,输入层的每个单词的one-hot向量与输入权重矩阵W相乘得到的向量的就是我们想要的词向量,这个矩阵W(所有单词的词向量)也叫做look up table,也就是说,任何一个单词的one-hot乘以这个矩阵都将得到自己的词向量。换句话说,输入权重矩阵其实就是词汇表在数学空间的表示。而输出权重矩阵相当于上下文单词到中心词的一种映射。

这里我们可以看到,原来我们使用one-hot编码产生的词向量,词汇表中的每个单词都是1*V的向量,现在我们训练出look up table矩阵后,词汇表便可以使用一个矩阵完全表示了。
通过不断喂样本给上述CBOW模型,我们训练出比较合适的输入和输出权重矩阵。这样当我们有新的需求,要求出某8个词对应的最可能的输出中心词时,我们可以通过一次神经网络前向传播算法并通过softmax处理找到概率最大的词即可。
搞懂了CBOW模型,skip-gram模型便比较容易理解了。
Skip-Gram模型和CBOW的思路是反着来的,即输入是中心词的one-hot向量,而输出是中心词对应的上下文词向量。还是上面的例子,我们的上下文大小取值为4, 特定的这个词"Learning"是我们的输入,而这8个上下文词是我们的输出。

这样我们这个Skip-Gram的例子里,我们的输入是中心词, 输出是softmax概率排前8的8个词,对应的Skip-Gram神经网络模型输入层有1个神经元,输出层有词汇表大小个神经元。隐藏层的神经元个数我们可以自己指定。通过神经网络的反向传播算法,我们可以求出神经网络模型的参数,同时得到所有的词对应的词向量。这样当我们有新的需求,要求出某1个词对应的最可能的8个上下文词时,我们可以通过一次神经网络前向传播算法得到概率大小排前8的softmax概率对应的词即可。

以上就是神经网络语言模型中如何用CBOW与Skip-Gram来训练模型与得到词向量的大概过程。但是这和word2vec中用CBOW与Skip-Gram来训练模型与得到词向量的过程有很多的不同。

word2vec为什么 不用现成的神经网络模型,要继续优化出新方法呢?最主要的问题是神经网络模型的这个处理过程非常耗时。我们的词汇表一般是百万级别,这意味着我们神经网络的输出层需要进行softmax计算各个词的输出概率的的计算量很大。有没有简化一点点的方法呢?
三,word2vec相对于传统的神经网络的改进
word2vec可以认为是在神经网络的基础上做了一系列的改进以克服传统神经网络计算量过大的缺点。word2vec同样也分为CBOW和skip-gram两种模型,我们以前文的CBOW模型为例,看看word2vec相对于传统的神经网络有哪些改进:
1,输入层:传统的神经网络输入的是单词的one-hot向量,然后乘以输入权重矩阵W才能到达隐藏层。单词one-hot向量是高度稀疏的,高度稀疏的高维单词one-hot向量与VN维的W矩阵相乘的计算量为VN,再加上需要8个单词的one-hot向量需要这样计算,所以这一步的计算量为8VN = 16000000。上文也提到每个单词的one-hot向量乘以矩阵W得到该单词的词向量,那么有没有什么方法可以避免使用乘法运算呢?答案是有的,那就是对词汇表的每个单词指定一个索引,再把这个索引作为输入权重矩阵W的行索引就可以直接得到这个单词的词向量了,前文我们也说过,矩阵W实际上对应着词汇表,所以词汇表中的单词当然可以像查字典一样在矩阵W中通过索引查得啦。这样通过查索引而不是矩阵相乘的方法大大减少了这一步的计算量。
2,投影层(隐藏层):这一层也叫做隐藏层,在上文传统的神经网络里,词向量在这一层是通过列向量拼接的方式形成一个中间向量的,中间向量的形状为18N。由于N不是很小,这个向量的维度也不会太小,而向量维度越高就意味着下一步的计算量越大,所以能不能在这一步将新向量的维度减小呢?word2vec的做法是在词向量合并的时候不通过列的拼接,而是在相同的列上直接求和(或者求和后再求平均)的方式将投影层得到的中间向量的维度降低为1N,也就是维度没有增加。
3,输出层:传统的神经网络是将隐藏层得到的中间向量再乘以输出权重矩阵W`,再经过softmax得到预测单词的。如上例所述,预测一个中心词(相当于一个样本)的计算量为8NV=16000000,又由于输出权重矩阵不是共享的,每个中心词均有一个特定的输出权重矩阵,这对于内存的占用是十分巨大的。但是在word2vec中,投影层得到的中间向量是通过层级softmax(Hierarchical Softmax)或者是负采样(negative sampling)的方式得到预测词极其概率的。这大大减少了这一步骤的计算量和所需内存。

由于层级softmax和负采样比较复杂,所以需要单独来讲。
首先来看看层级softmax,传统的神经网络模型在输出层使用的可以认为是单层的softmax,单层softmax形象直观,便于理解,在向量进行softmax转换后,只需要选择概率值最大的单词作为中心词的预测词即可,但是上文也提到这将会带来巨量的计算。所以我们将单层softmax进行改进得到层级softmax。而说到层级softmax,就不得不提到哈夫曼树(Huffman tree,也称霍夫曼树),哈夫曼树是带权路径长度最短的二叉树,特点是权值较大的结点离根较近。有些文章认为哈夫曼树就是层级softmax,个人不是很赞同。我个人的理解是层级softmax这种方法是需要哈夫曼树这种方式来实现的,也就是说层级softmax是目的,而哈夫曼树只是实现目的的途径而已。

好了,下面我们来看看哈夫曼树的构建。哈夫曼树的构建过程如下:
输入:权值为(w1,w2,…wn)的n个节点
输出:对应的哈夫曼树
1)将(w1,w2,…wn)看做是有n棵树的森林,每个树仅有一个节点。
2)在森林中选择根节点权值最小的两棵树进行合并,得到一个新的树,这两颗树分布作为新树的左右子树。新树的根节点权重为左右子树的根节点权重之和。
3) 将之前的根节点权值最小的两棵树从森林删除,并把新树加入森林。
4)重复步骤2)和3)直到森林里只有一棵树为止。
下面我们用一个具体的例子来说明哈夫曼树建立的过程,我们有(a,b,c,d,e,f)共6个节点,节点的权值分布是(20,4,8,6,16,3)。
首先是最小的b和f合并,得到的新树根节点权重是7.此时森林里5棵树,根节点权重分别是20,8,6,16,7。此时根节点权重最小的6,7合并,得到新子树,依次类推,最终得到下面的哈夫曼树。

在这里插入图片描述
值得注意的是,当c:8与13结合后权重变为21时,此时的森林只有权重为21,16,20三棵树,所以选权重最小的两棵树便是e和a了。
 那么霍夫曼树有什么好处呢?一般得到霍夫曼树后我们会对叶子节点进行霍夫曼编码,由于权重高的叶子节点越靠近根节点,而权重低的叶子节点会远离根节点,这样我们的高权重节点编码值较短,而低权重值编码值较长。这保证的树的带权路径最短,也符合我们的信息论,即我们希望越常用的词拥有更短的编码。如何编码呢?一般对于一个霍夫曼树的节点(根节点除外),可以约定左子树编码为0,右子树编码为1。如上图,则可以得到c的编码是00。
在word2vec中,约定编码方式和上面的例子相反,即约定左子树编码为1,右子树编码为0,同时约定左子树的权重不小于右子树的权重。
那如何使用哈夫曼树来实现层级softmax呢?
由于我们把之前所有都要计算的从输出softmax层的概率计算变成了一颗哈夫曼树,那么我们的softmax概率计算只需要沿着树形结构进行就可以了。如下图所示,我们可以沿着哈夫曼树从根节点一直走到我们的叶子节点的词w2。
在这里插入图片描述
和之前的神经网络语言模型相比,哈夫曼树的根节点的向量对应我们的投影层的中间向量,哈夫曼树的所有内部节点就类似之前神经网络投影层到输出层的输出权重矩阵W`,而所有叶子节点就类似于之前神经网络softmax输出层的词汇表单词的概率,叶子节点的个数就是词汇表的大小。在哈夫曼树中,隐藏层到输出层的softmax映射不是一下子完成的,而是沿着哈夫曼树一步步完成的。这也就是层级softmax名称的由来。
如何“沿着哈夫曼树一步步完成”呢?在word2vec中,我们采用了二元逻辑回归的方法,即规定沿着左子树走,那么就是负类(哈夫曼树编码1),沿着右子树走,那么就是正类(哈夫曼树编码0)。判别正类和负类的方法是使用sigmoid函数,即:
在这里插入图片描述
其中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本身,我们的目标就是找到合适的所有节点的词向量和所有内部节点θ, 使训练样本达到最大似然。那么如何达到最大似然呢?这一点请参考:
https://www.cnblogs.com/pinard/p/7243513.html
本文不做太多的深入。
虽然层级softmax相比单层softmax优势明显,但是层级softmax仍然有着一个比较大的缺点,就是针对很生僻的词,在哈夫曼树中,这个词所在的位置非常低,也就是路径非常长。
负采样(Negative Sampling)就是这么一种求解word2vec模型的方法,它摒弃了哈夫曼树,下面我们就来看看负采样的求解思路。
负采样的思想最初来源于一种叫做Noise-Contrastive Estimation的算法,原本是为了解决那些无法归一化的概率模型的参数预估问题。与改造模型输出概率的层级softmax算法不同,NCE算法改造的是模型的似然函数。
在CBOW模型中,假设词汇表的单词数为N(可以认为有N个类别),中心词为w,它周围上下文共有2c个单词,记为context(w)。由于这个中心词w的确和context(w)相关存在,因此在词汇表中,这个中心词w是一个正类别,而词汇表中的其他N-1个词为负类别。负采样的做法就是在这N-1个负类别中进行采样,采样数量我们记为k(k<< N-1)。利用这一个正类别和k个负类别,我们进行二元逻辑回归,得到负采样对应每个词w对应的模型参数θ,和每个词的词向量。
下面我们来看看如何进行负采样以得到k个负类别。
在这里插入图片描述
如上图,N表示词汇表单词的数量,I表示词汇表具体的单词,M表示划分区间的数目,m表示某个划分区间,这里的划分区间是等间距的,而单词区间的大小和其出现的次数或者说出现的概率成正比,一个单词区间对应一个或多个划分区间。这里M一般是远大于N的,在word2vec中,M取值默认为10的8次方。以保证任何一个单词至少可以对应一个划分区间。在采样的时候,我们只需要在M个位置随机采取k个区间即可,这k个区间对应的线段所属的词就是负例词。
word2vec最终得到的是词汇表中每个词对应的词向量xw(首先得到的是词典所对应的词向量矩阵,然后对于每个词,使用词对应的索引查询上述词典矩阵从而得到词所对应词向量)和该词向量路径对应的参数θj-1w
以下为参考的文章,有兴趣可以看下:
1
2
3
4
5
6

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/514709
推荐阅读
  

闽ICP备14008679号