一.简介
Word2Vec是google在2013年推出的一个NLP工具,它的特点是能够将单词转化为向量来表示,这样词与词之间就可以定量的去度量他们之间的关系,挖掘词之间的联系。用词向量来表示词并不是Word2Vec的首创,在很久之前就出现了。最早的词向量采用One-Hot编码,又称为一位有效编码,每个词向量维度大小为整个词汇表的大小,对于每个具体的词汇表中的词,将对应的位置置为1。比如我们有下面的5个词组成的词汇表
采用One-Hot编码方式来表示词向量非常简单,但缺点也是显而易见的,一方面我们实际使用的词汇表很大,经常是百万级以上,这么高维的数据处理起来会消耗大量的计算资源与时间。另一方面,One-Hot编码中所有词向量之间彼此正交,没有体现词与词之间的相似关系。
Distributed representation可以解决One-Hot编码存在的问题,它的思路是通过训练,将原来One-Hot编码的每个词都映射到一个较短的词向量上来,而这个较短的词向量的维度可以由我们自己在训练时根据任务需要来自己指定。
下图是采用Distributed representation的一个例子,我们将词汇表里的词用"Royalty","Masculinity", "Femininity"和"Age"4个维度来表示,King这个词对应的词向量可能是(0.99,0.99,0.05,0.7)。当然在实际情况中,我们并不能对词向量的每个维度做一个很好的解释。
有了用Distributed Representation表示的较短的词向量,我们就可以较容易的分析词之间的关系了,比如我们将词的维度降维到2维,有一个有趣的研究表明,用下图的词向量表示我们的词时,我们可以发现:
可见我们只要得到了词汇表里所有词对应的词向量,那么我们就可以做很多有趣的事情了。不过,怎么训练才能得到合适的词向量呢?针对这个问题,Google的Tomas Mikolov在他的论文中提出了CBOW和Skip-gram两种神经网络模型。
二.原理
Word2Vec 的训练模型本质上是只具有一个隐含层的神经元网络,词向量表示即为矩阵(如下图)。
隐层的激活函数其实是线性的,相当于没做任何处理(这也是 Word2vec 简化之前语言模型的独到之处),我们要训练这个神经网络,用反向传播算法,本质上是链式求导。
它的输入是采用One-Hot编码的词汇表向量,它的输出也是One-Hot编码的词汇表向量。使用所有的样本,训练这个神经元网络,等到收敛之后,从输入层到隐含层的那些权重,便是每一个词的采用Distributed Representation的词向量。这样我们就把原本维数为V的词向量变成了维数为N的词向量(N远小于V),并且词向量间保留了一定的相关关系。
Google的Mikolov在关于Word2Vec的论文中提出了CBOW和Skip-gram两种模型,CBOW适合于数据集较小的情况,而Skip-Gram在大型语料中表现更好。其中CBOW如下图左部分所示,使用围绕目标单词的其他单词(语境)作为输入,在映射层做加权处理后输出目标单词。与CBOW根据语境预测目标单词不同,Skip-gram根据当前单词预测语境,如下图右部分所示。假如我们有一个句子“There is an apple on the table”作为训练数据,CBOW的输入为(is,an,on,the),输出为apple。而Skip-gram的输入为apple,输出为(is,an,on,the)。
1.CBOW
1、输入层:上下文单词的One-Hot编码词向量,V为词汇表单词个数,C为上下文单词个数。以上文那句话为例,这里C=4,所以模型的输入是(is,an,on,the)4个单词的One-Hot编码词向量。
2、初始化一个权重矩阵,然后用所有输入的One-Hot编码词向量左乘该矩阵,得到维数为N的向量 ,这里的N由自己根据任务需要设置。
3、将所得的向量相加求平均作为隐藏层向量。
4、初始化另一个权重矩阵 ,用隐藏层向量左乘,再经激活函数处理得到V维的向量,的每一个元素代表相对应的每个单词的概率分布。
5、中概率最大的元素所指示的单词为预测出的中间词(target word)与true label的One-Hot编码词向量做比较,误差越小越好(根据误差更新两个权重矩阵)
在训练前需要定义好损失函数(一般为交叉熵代价函数),采用梯度下降算法更新W和W'。训练完毕后,输入层的每个单词与矩阵W相乘得到的向量的就是我们想要的Distributed Representation表示的词向量,也叫做word embedding。
2.Skip-gram
在前面的章节中,我们已经介绍过Skip-Gram是给定input word来预测上下文,其模型结构如上图所示。它的做法是,将一个词所在的上下文中的词作为输出,而那个词本身作为输入,也就是说,给出一个词,希望预测可能出现的上下文的词。通过在一个大的语料库训练,得到一个从输入层到隐含层的权重模型。“apple”的上下文词是(’there’,’is’,’an’,’on’,’the’,’table’).那么以apple的One-Hot词向量作为输入,输出则是(’there’,’is’,’an’,’on’,’the’,’table’)的One-Hot词向量。训练完成后,就得到了每个词到隐含层的每个维度的权重,就是每个词的向量(和CBOW中一样)。接下来具体介绍如何训练我们的神经网络。
假如我们有一个句子“There is an apple on the table”。
1、首先我们选句子中间的一个词作为我们的输入词,例如我们选取“apple”作为input word;
2、有了input word以后,我们再定义一个叫做skip_window的参数,它代表着我们从当前input word的一侧(左边或右边)选取词的数量。如果我们设置skip_window=2,那么我们最终获得窗口中的词(包括input word在内)就是[‘is’,’an’,’apple’,’on’,’the’ ]。skip_window=2代表着选取左input word左侧2个词和右侧2个词进入我们的窗口,所以整个窗口大小span=2x2+1=5。另一个参数叫num_skips,它代表着我们从整个窗口中选取多少个不同的词作为我们的output word,当skip_window=2,num_skips=2时,我们将会从窗口中随机选择两个非中心单词,得到两组 (input word, output word) 形式的训练数据,例如选择an和on我们得到('apple', 'an'),('apple', 'on')。
3、神经网络基于这些训练数据中每对单词出现的次数习得统计结果,并输出一个概率分布,这个概率分布代表着到我们词典中每个词有多大可能性跟input word同时出现。举个例子,如果我们向神经网络模型中输入一个单词“中国“,那么最终模型的输出概率中,像“英国”, ”俄罗斯“这种相关词的概率将远高于像”苹果“,”蝈蝈“非相关词的概率。因为”英国“,”俄罗斯“在文本中更大可能在”中国“的窗口中出现。我们将通过给神经网络输入文本中成对的单词来训练它完成上面所说的概率计算。
4、通过梯度下降和反向传播更新矩阵W
5、W中的行向量即为每个单词的Word embedding表示
三.改进
我们介绍了CBOW和Skip-gram最理想情况下的实现,即训练迭代两个矩阵W和W’,之后在输出层采用softmax函数来计算输出各个词的概率。但在实际应用中这种方法的训练开销很大,不具有很强的实用性,因为Word2vec 本质上是一个语言模型,它的输出节点数是 V 个,对应了 V 个词语,本质上是一个多分类问题,但实际当中,词语的个数非常非常多,会给计算造成很大困难,所以需要用技巧来加速训练。为了使得模型便于训练,有学者提出了Hierarchical Softmax和Negative Sampling两种改进方法。
1.hierarchical softmax
改进点1
改进输入向量求和方式
第一点是从输入层到隐藏层的映射,没有采用原先的与矩阵W相乘然后相加求平均的方法,而是直接对所有输入的词向量求和。假设输入的词向量为(0,1,0,0)和(0,0,0,1),那么隐藏层的向量为(0,1,0,1)。
改进点2
通过采用Huffman tree把N分类问题变成 log(N)次二分类
还记得最原始的网络模型是长这样的:
而在Hierarchical softmax中,我们不再需要的部分了,变成了下面的样子:
我们把传统的Softmax换成Hierarchical softmax,而Hierarchical softmax由一个Huffman树构成。这个Huffman树的叶节点是所有的单词,构造规则按照词频排序。
也就是说,现在输入的单词经过这个矩阵,转化成了隐层的一堆参数。
这些参数输入到一个Huffman树中,最终期望它选到对应的单词。
这是不是很像决策树?
当然这个Huffman树的结构是已经实现决定好的(按照词频)。
那么如何决定每个节点的选择的呢?如何判断在某个节点上是往左走还是往右走?
实际上,在每个节点上,都有一组不同的参数,我们设隐层的参数是 ,那么到第个节点上,由sigmoid函数决定往左走还是往右走:
而Hierarchical softmax的好处是加快了选择的速度,同时它不会更改所有的节点的参数。
例如下面的例子:
如果groun truth(也就是正例)是would,那么Huffman编码是11100,梯度下降会改变经过的节点上的参数 θ ,而不会改变其他参数。
而传统的Softmax则是调整了所有的参数的。
我们再说回来,Hierarchical softmax为什么更快?
传统的Softmax要计算下面的式子:
而当V很大的时候, 其计算量是非常大的。
但是在Hierarchical softmax中,我们只需要计算有限个,这样就减少了计算量,同时因为不会更改其他的θ,因此更有优势。
2.Negative Sampling
Negative Sampling和Hierarchical softmax的想法类似,也是想只更新一部分参数。
负采样这个词在神经网络中并不经常出现。因为通常的分类任务中并没有负采样的概念。
在分类任务中,我们用one-hot表示类别,我们通过极大似然优化网络参数,自然希望的值尽可能大,而其他的的值都变小。
这意味这,在传统的Softmax中,如果我们的vocabulary大小为10000时,在输出层,我们期望某一个神经元结点输出1,其余9999个都应该输出0。在这里,这9999个我们期望输出为0的神经元结点所对应的单词我们称为negative word。
而Negative Sampling的想法是不全部计算所有的negative word,只从中采样一部分进行计算。我们在整个字典中采样 K个词作为负样本,用原来的target word作为正样本,就得到了K+1个样本。接下来,我们依旧需要个参数,但是它们不再整体作为Softmax的一部分了,而是分别形成V个Sigmoid。
接下来就很简单了,我们只要对K+1个Sigmoid更新参数就可以了。
随着对Sigmoid参数的更新,经过反向传播过程,前面的矩阵也会被更新,这就起到了训练的作用。
而K个负样本的选择也是有说道的,往往按照词频选择。(这体现了Negative Sampling和Hierarchical softmax的相似之处)
设是第个词的频率,这个词被选择到的概率为:
当然这个3/4完全是经验上的数值。
Negative Sampling之所以有效,还是因为我们最前面说到的,这个模型根本用不着真的去分类,而它的副产品——词向量矩阵 才是我们需要的。
参考:
https://zhuanlan.zhihu.com/p/61635013
https://zhuanlan.zhihu.com/p/95204186
https://zhuanlan.zhihu.com/p/26306795