2. 历史突破——梯子来了
自NNLM于2003年被提出后,后面又出现了很多类似和改进的工作,诸如LBL, C&W和RNNLM模型等等,这些方法主要从两个方面去优化NNLM的思想,其一是NNLM只用了左边的个词,如何利用更多的上下文信息便成为了很重要的一个优化思路(如Mikolov等人提出的RNNLM);其二是NNLM的一个非常大的缺点是输出层计算量太大,如何减小计算量使得大规模语料上的训练变得可行,这也是工程应用上至关重要的优化方向(如Mnih和Hinton提出的LBL以及后续的一系列模型)。
为了更好理解NNLM之后以及word2vec诞生前的情况,我们先来看看现有的几个主要模型都有哪些优缺点。
NNLM虽然将N-Gram的阶n提高到了5,相比原来的统计语言模型是一个很大的进步,但是为了获取更好的长程依赖关系,5显然是不够的。再者,因为NNLM只对词的左侧文本进行建模,所以得到的词向量并不是语境的充分表征。还有一个问题就更严重了,NNLM的训练依然还是太慢,在论文中,Bengio说他们用了40块CPU,在含有1400万个词,只保留词频相对较高的词之后词典大小为17964个词,只训练了5个epoch,但是耗时超过3周。按这么来算,如果只用一块CPU,可能需要2年多,这还是在仅有1400万个词的语料上。如此耗时的训练过程,显然严重限制了NNLM的应用。
2007年Mnih和Hinton提出的LBL以及后续的一系列相关模型,省去了NNLM中的激活函数,直接把模型变成了一个线性变换,尤其是后来将Hierarchical Softmax引入到LBL后,训练效率进一步增强,但是表达能力不如NNLM这种神经网络的结构;2008年Collobert和Weston
提出的C&W模型不再利用语言模型的结构,而是将目标文本片段整体当做输入,然后预测这个片段是真实文本的概率,所以它的工作主要是改变了目标输出,由于输出只是一个概率大小,不再是词典大小,因此训练效率大大提升,但由于使用了这种比较“别致”的目标输出,使得它的词向量表征能力有限;2010年Mikolov(对,还是同一个Mikolov)提出的RNNLM主要是为了解决长程依赖关系,时间复杂度问题依然存在。
而这一切,似乎都在预示着一个新时代的来临。
2.1 CBOW和Skip-gram
2013年,Tomas Mikolov连放几篇划时代的论文,其中最为重要的是两篇,《Efficient estimation of word representations in vector space》首次提出了CBOW和Skip-gram模型,进一步的在《Distributed Representations of Words and Phrases and their Compositionality》中,又介绍了几种优化训练的方法,包括Hierarchical Softmax(当然,这个方法早在2003年,Bengio就在他提出NNLM论文中的Future Work部分提到了这种方法,并于2005年把它系统化发表了一篇论文), Negative Sampling和Subsampling技术。放出两篇论文后,当时仍在谷歌工作的Mikolov又马不停蹄的放出了大杀器——word2vec工具,并在其中开源了他的方法。顺便提一下的是,很多人以为word2vec是一种模型和方法,其实word2vec只是一个工具,背后的模型是CBOW或者Skip-gram,并且使用了Hierarchical Softmax或者Negative Sampling这些训练的优化方法。所以准确说来,word2vec并不是一个模型或算法,只不过Mikolov恰好在当时把他开源的工具包起名叫做word2vec而已。不过为了叙述简单,在下文我将用word2vec来指代上面提到Mikolov两篇论文中的一整个相关的优化思想。
word2vec对于前人的优化,主要是两方面的工作:模型的简化和训练技巧的优化。我们先来看看模型的简化方面,也就是耳熟能详的CBOW和Skip-gram。
对于CBOW而言,我们可以从它的名字上一窥究竟,它的全称是Continuous Bag-of-Words,也就是连续的词袋模型,为什么取这个名字,先来看看它的目标函数。
首先,CBOW没有隐藏层,本质上只有两层结构,输入层将目标词语境中的每一个词向量简单求和(当然,也可以求平均)后得到语境向量,然后直接与目标词的输出向量求点积,目标函数也就是要让这个与目标词向量的点积取得最大值,对应的与非目标词的点积尽量取得最小值。从这可以看出,CBOW的第一个特点是取消了NNLM中的隐藏层,直接将输入层和输出层相连;第二个特点便是在求语境context向量时候,语境内的词序已经丢弃(这个是名字中Continuous的来源);第三,因为最终的目标函数仍然是语言模型的目标函数,所以需要顺序遍历语料中的每一个词(这个是名字中Bag-of-Words的来源)。因此有了这些特点(尤其是第二点和第三点),Mikolov才把这个简单的模型取名叫做CBOW,简单却有效的典范。
需要注意的是这里每个词对应到两个词向量,在上面的公式中都有体现,其中是词的输入向量,而则是词的输出向量,或者更准确的来讲,前者是CBOW输入层中跟词所在位置相连的所有边的权值(其实这就是词向量)组合成的向量,而是输出层中与词所在位置相连的所有边的权值组合成的向量,所以把这一向量叫做输出向量。
同样地,和CBOW对应,Skip-gram的模型基本思想和CBOW非常类似,只是换了一个方向:CBOW是让目标词的输出向量拟合语境的向量;而Skip-gram则是让语境中每个词的输出向量尽量拟合当前输入词的向量,和CBOW的方向相反,因此它的目标函数如下:
可以看出目标函数中有两个求和符号,最里面的求和符号的意义便是让当前的输入词分别和该词对应语境中的每一个词都尽量接近,从而便可以表现为该词与其上下文尽量接近。
和CBOW类似,Skip-gram本质上也只有两层:输入层和输出层,输入层负责将输入词映射为一个词向量,输出层负责将其经过线性映射计算得到每个词的概率大小。再细心一点的话,其实无论CBOW还是Skip-gram,本质上都是两个全连接层的相连,中间没有 任何其他的层。因此,这两个模型的参数个数都是
,其中分别是词向量的维度和词典的大小,相比上文中我们计算得到NNLM的参数个数已经大大减小,且与上下文所取词的个数无关了,也就是终于避免了N-gram中随着阶数N增大而使得计算复杂度急剧上升的问题。
然而,Mikolov大神说了,这些公式是“impractical”的,他的言下之意是计算复杂度依然和词典大小有关,而这通常都意味着非常非常大,以下是他的原话
…, and W is the number of words in the vocabulary. This formulation is impractical because the cost of computing ∇ log p(wO|wI ) is proportional to W, which is often large (10^5–10^7 terms).
不得不说,大神就是大神,将模型已经简化到了只剩两个全连接层(再脱就没了),依然不满足,还“得寸进尺”地打起了词典的“小算盘”,那么Mikolov的“小算盘”是什么呢?
他在论文中首先提到了Hierachical Softmax,认为这是对full softmax的一种优化手段,而Hierachical Softmax的基本思想就是首先将词典中的每个词按照词频大小构建出一棵Huffman树,保证词频较大的词处于相对比较浅的层,词频较低的词相应的处于Huffman树较深层的叶子节点,每一个词都处于这棵Huffman树上的某个叶子节点;第二,将原本的一个分类问题变成了次的二分类问题,做法简单说来就是,原先要计算的时候,因为使用的是普通的softmax,势必要求词典中的每一个词的概率大小,为了减少这一步的计算量,在Hierachical Softmax中,同样是计算当前词在其上下文中的概率大小,只需要把它变成在Huffman树中的路径预测问题就可以了,因为当前词在Huffman树中对应到一条路径,这条路径由这棵二叉树中从根节点开始,经过一系列中间的父节点,最终到达当前这个词的叶子节点而组成,那么在每一个父节点上,都对应的是一个二分类问题(本质上就是一个LR分类器),而Huffman树的构造过程保证了树的深度为,所以也就只需要做次二分类便可以求得的大小,这相比原来次的计算量,已经大大减小了。
接着,Mikolov又提出了负采样的思想,而这一思想也是受了C&W模型中构造负样本方法启发,同时参考了Noise Contrastive Estimation (NCE)的思想,用CBOW的框架简单来讲就是,负采样每遍历到一个目标词,为了使得目标词的概率最大,根据softmax函数的概率公式,也就是让分子中的最大,而分母中其他非目标词的最小,普通softmax的计算量太大就是因为它把词典中所有其他非目标词都当做负例了,而负采样的思想特别简单,就是每次按照一定概率随机采样一些词当做负例,从而就只需要计算这些负采样出来的负例了,那么概率公式便相应变为
仔细和普通softmax进行比较便会发现,将原来的分类问题变成了分类问题,这便把词典大小对时间复杂度的影响变成了一个常数项,而改动又非常的微小,不可谓不巧妙。
除此之外,Mikolov还提到了一些其他技巧,比如对于那些超高频率的词,尤其是停用词,可以使用Subsampling的方法进行处理,不过这已经不是word2vec最主要的内容了。
自此,经过模型和训练技巧的双重优化,终于使得大规模语料上的训练成为了现实,更重要的是,得到的这些词向量能够在语义上有非常好的表现,能将语义关系通过向量空间关系表征出来。
word2vec的出现,极大的促进了NLP的发展,尤其是促进了深度学习在NLP中的应用(不过有意思的是,word2vec算法本身其实并不是一个深度模型,它只有两层全连接),利用预训练好的词向量来初始化网络结构的第一层几乎已经成了标配,尤其是在只有少量监督数据的情况下,如果不拿预训练的embedding初始化第一层,几乎可以被认为是在蛮干。在此之后,一大批word embedding方法大量涌现,比较知名的有GloVe和fastText等等,它们各自侧重不同的角度,并且从不同的方向都得到了还不错的embedding表征。
2.2 GloVe
先来看看GloVe的损失函数,
其中是两个词和在某个窗口大小中的共现频率(不过GloVe对其做了一些改进,共现频率相应有一个衰减系数,使得距离越远的词对共现频率越小一些),是一个权重系数,主要目的是共现越多的pair对于目标函数贡献应该越大,但是又不能无限制增大,所以对共现频率过于大的pair限定最大值,以防训练的时候被这些频率过大的pair主导了整个目标函数,剩下的就是算法的核心部分了,两个值是两个偏置项,撇去不谈,那么剩下的其实就是一个普通的均方误差函数,是当前词的向量,对应的是与其在同一个窗口中出现的共现词的词向量,两者的向量点乘要去尽量拟合它们共现频率的对数值。从直观上理解,如果两个词共现频率越高,那么其对数值当然也越高,因而算法要求二者词向量的点乘也越大,而二个词向量的点乘越大,其实包含了两层含义:第一,要求各自词向量的模越大,通常来说,除去频率非常高的词(比如停用词),对于有明确语义的词来说,它们的词向量模长会随着词频增大而增大,因此两个词共现频率越大,要求各自词向量模长越大是有直觉意义的,比如“魑魅魍魉”假如能被拆分成两个词,那么“魑魅”和“魍魉”这两个词的共现频率相比““魑魅”和其他词的共现频率要大得多,对应到“魑魅”的词向量,便会倾向于在某个词向量维度上持续更新,进而使得它的模长也会比较偏大;第二,要求这两个词向量的夹角越小,这也是符合直觉的,因为出现在同一个语境下频率越大,说明这两个词的语义越接近,因而词向量的夹角也偏向于越小。
此外,可以从GloVe使用的损失函数中发现,它的训练主要是两个步骤:统计共现矩阵和训练获取词向量,这个过程其实是没有我们通常理解当中的模型的,更遑论神经网络,它整个的算法框架都是基于矩阵分解的做法来获取词向量的,本质上和诸如LSA这种基于SVD的矩阵分解方法没有什么不同,只不过SVD分解太过于耗时,运算量巨大,相同点是LSA也是输入共现矩阵,不过一般主要以词-文档共现矩阵为主,另外,LSA中的共现矩阵没有做特殊处理,而GloVe考虑到了对距离较远的词对做相应的惩罚等等。然而,相比word2vec,GloVe却更加充分的利用了词的共现信息,word2vec中则是直接粗暴的让两个向量的点乘相比其他词的点乘最大,至少在表面上看来似乎是没有用到词的共现信息,不像GloVe这里明确的就是拟合词对的共现频率。
不过更有意思的还是,GloVe和word2vec似乎有种更为内在的联系,再来看看他们的目标函数有什么不一样,这是Skip-gram的目标函数(这里在原来的基础上加上了对于语料的遍历N)
而这个目标函数是按照语料的顺序去遍历,如果先把语料当中的相关词进行合并,然后按照词典序进行遍历,便可以证明Skip-gram实际上和GloVe的优化目标一致,有兴趣的可以参考引用11中的证明细节,这里不再赘述。
2.3 fastText
word2vec和GloVe都不需要人工标记的监督数据,只需要语言内部存在的监督信号即可以完成训练。而与此相对应的,fastText则是利用带有监督标记的文本分类数据完成训练,本质上没有什么特殊的,模型框架就是CBOW,只不过与普通的CBOW有两点不一样,分别是输入数据和预测目标的不同,在输入数据上,CBOW输入的是一段区间中除去目标词之外的所有其他词的向量加和或平均,而fastText为了利用更多的语序信息,将bag-of-words变成了bag-of-features,也就是下图中的输入不再仅仅是一个词,还可以加上bigram或者是trigram的信息等等;第二个不同在于,CBOW预测目标是语境中的一个词,而fastText预测目标是当前这段输入文本的类别,正因为需要这个文本类别,因此才说fastText是一个监督模型。而相同点在于,fastText的网络结构和CBOW基本一致,同时在输出层的分类上也使用了Hierachical Softmax技巧来加速训练。
这里的便是语料当中第n篇文档的第i个词以及加上N-gram的特征信息。从这个损失函数便可以知道fastText同样只有两个全连接层,分别是A和B,其中A便是最终可以获取的词向量信息。
fastText最大的特点在于快,论文中对这一点也做了详细的实验验证,在一些分类数据集上,fastText通常都可以把CNN结构的模型要耗时几小时甚至几天的时间,急剧减少到只需要消耗几秒钟,不可谓不“fast”.
不过说个八卦,为什么fastText结构和CBOW如此相似(感兴趣的读者想要继续深挖的话,还可以看看2015年ACL上的一篇论文《Deep Unordered Composition Rivals Syntactic Methods for Text Classification》,结构又是何其相似,并且比fastText的论文探讨的更为深入一些,但是fastText是2016年的文章,剩下的大家自己去想好了),这里面大概一个特别重要的原因就是fastText的作者之一便是3年前CBOW的提出者Mikolov本人,只不过昔日的Mikolov还在谷歌,如今3年时间一晃而过,早已是Facebook的人了。
2.4 爬上第一级梯子的革命意义
那为什么word2vec的出现极大的促进了NLP领域的发展?
通常以为,word2vec一类的方法会给每一个词赋予一个向量的表征,不过似乎从来没有人想过这个办法为什么行之有效?难道是皇帝的新衣?按理来说,包括NNLM在内,这些方法的主要出发点都是将一个词表示成词向量,并将其语义通过上下文来表征。其实,这背后是有理论基础的,1954年Harris提出分布假说(distributional hypothesis),这一假说认为:上下文相似的词,其语义也相似,1957年Firth对分布假说进行了进一步阐述和明确:词的语义由其上下文决定(a word is characterized by the company it keeps),30年后,深度学习的祖师爷Hinton也于1986年尝试过词的分布式表示。
而word2vec的贡献远不止是给每一个词赋予一个分布式的表征,私以为,它带来了一种全新的NLP模型建立方法,在这之前,大多数NLP任务都要在如何挖掘更多文本语义特征上花费大量时间,甚至这一部分工作占去了整个任务工作量的绝大部分,而以word2vec为代表的distributed representation方法大量涌现后(尤其是因为大规模语料上的预训练词向量成为现实,并且被证明确实行之有效之后),算法人员发现利用word2vec在预训练上学习到的词向量,初始化他们自己模型的第一层,会带来极大效果的提升,以至于这五年以来,几乎一个业内的默认做法便是要用了无论word2vec还是GloVe预训练的词向量,作为模型的第一层,如果不这么做,大约只有两个原因:
你很土豪,有钱标注大量监督数据;
你在蛮干。
而这一个思想,绝不是如它表象所显示的一样,似乎和过去做文本特征没什么太大区别,是的,表象确实是这样,无非是把一个词用了一堆数字来表征而已,这和离散化的特征有什么本质区别吗?有,因为它开启了一种全新的NLP模型训练方式——迁移学习。基本思想便是利用一切可以利用的现成知识,达到快速学习的目的,这和人类的进化历程何其相似。
虽然咿咿呀呀囫囵吞枣似的刚开始能够说得三两个词,然而这是“NLP的一小步,人类AI的一大步”。正如人类语言产生之初,一旦某个原始人类的喉部发出的某个音节,经无比智慧和刨根问底考证的史学家研究证实了它具有某个明确的指代意义(无论它指代什么,即便是人类的排泄物),这便无比庄严的宣示着一个全新物种的诞生,我想迁移学习在NLP中的这一小步,大概与此同担当。
领取专属 10元无门槛券
私享最新 技术干货