Home

gentlesnow

28 Oct 2018

【NLP】【CS224N】2 word2vec

cs224n-2-1 cs224n-2-2

词向量

自然语言处理(NLP)相关任务中,要将自然语言交给机器学习中的算法来处理,通常需要首先将语言数学化,因为机器不是人,机器只认数学符号。向量是人把自然界的东西抽象出来交给机器处理的东西,基本上可以说向量是人对机器输入的主要方式了。

词向量就是用来将语言中的词进行数学化的一种方式,顾名思义,词向量就是把一个词表示成一个向量。

One-Hot Representation

独热词汇向量是一种存储在某处的本地表示。

一种最简单的词向量方式是 one-hotrepresentation,就是用一个很长的向量来表示一个词,向量的长度为词典的大小,向量的分量只有一个 1,其他全为 0, 1 的位置对应该词在词典中的位置。举个例子,

“话筒”表示为 [0 0 0 1 00 0 0 0 0 0 0 0 0 0 0 ...]  
“麦克”表示为 [0 0 0 0 00 0 0 1 0 0 0 0 0 0 0 ...]   每个词都是茫茫 0 海中的一个 1。

这种 One-hotRepresentation 如果采用稀疏方式存储,会是非常的简洁:也就是给每个词分配一个数字 ID。比如刚才的例子中,话筒记为 3,麦克记为 8(假设从 0 开始记)。如果要编程实现的话,用 Hash 表给每个词分配一个编号就可以了。这么简洁的表示方法配合上最大熵、SVM、CRF 等等算法已经很好地完成了 NLP 领域的各种主流任务。

但这种词表示有两个缺点:

  1. 容易受维数灾难的困扰,尤其是将其用于 Deep Learning 的一些算法时
  2. 不能很好地刻画词与词之间的相似性(术语好像叫做“词汇鸿沟”):任意两个词之间都是孤立的。光从这两个向量中看不出两个词是否有关系,哪怕是话筒和麦克这样的同义词也不能幸免于难。

Distributed Representation

另一种就是DistributedRepresentation 这种表示,它最早是 Hinton 于 1986 年提出的,可以克服 one-hot representation 的缺点。其基本想法是直接用一个普通的向量表示一个词,这种向量一般长成这个样子:

[0.792, −0.177, −0.107, 0.109, −0.542, ...]

也就是普通的向量表示形式。维度以 50 维和 100 维比较常见。

当然一个词怎么表示成这么样的一个向量是要经过一番训练的,训练方法较多,word2vec是其中一种,在后面会提到,这里先说它的意义。还要注意的是每个词在不同的语料库和不同的训练方法下,得到的词向量可能是不一样的。

词向量一般维数不高,很少有人闲着没事训练的时候定义一个10000维以上的维数,所以用起来维数灾难的机会现对于one-hot representation表示就大大减少了。

由于是用向量表示,而且用较好的训练算法得到的词向量的向量一般是有空间上的意义的,也就是说,将所有这些向量放在一起形成一个词向量空间,而每一向量则为该空间中的一个点,在这个空间上的词向量之间的距离度量也可以表示对应的两个词之间的“距离”。所谓两个词之间的“距离”,就是这两个词之间的语法,语义之间的相似性。

一个比较爽的应用方法是,得到词向量后,假如对于某个词A,想找出这个词最相似的词,这个场景对人来说都不轻松,毕竟比较主观,但是对于建立好词向量后的情况,对计算机来说,只要拿这个词的词向量跟其他词的词向量一一计算欧式距离或者cos距离,得到距离最小的那个词,就是它最相似的。

这样的特性使得词向量很有意义,自然就会吸引比较多的人去研究,前有Bengio发表在JMLR上的论文《A Neural Probabilistic Language Model》,又有Hinton的层次化Log-Bilinear模型,还有google的TomasMikolov 团队搞的word2vec,等等。

词向量在机器翻译领域的一个应用,就是google的TomasMikolov 团队开发了一种词典和术语表的自动生成技术,该技术通过向量空间,把一种语言转变成另一种语言,实验中对英语和西班牙语间的翻译准确率高达90%。

介绍算法工作原理的时候举了一个例子:考虑英语和西班牙语两种语言,通过训练分别得到它们对应的词向量空间 E 和 S。从英语中取出五个词 one,two,three,four,five,设其在 E 中对应的词向量分别为 v1,v2,v3,v4,v5,为方便作图,利用主成分分析(PCA)降维,得到相应的二维向量 u1,u2,u3,u4,u5,在二维平面上将这五个点描出来,如下图左图所示。类似地,在西班牙语中取出(与 one,two,three,four,five 对应的) uno,dos,tres,cuatro,cinco,设其在 S 中对应的词向量分别为 s1,s2,s3,s4,s5,用 PCA 降维后的二维向量分别为 t1,t2,t3,t4,t5,将它们在二维平面上描出来(可能还需作适当的旋转),如下图右图所示:

词向量在机器翻译领域的一个应用

观察左、右两幅图,容易发现:五个词在两个向量空间中的相对位置差不多,这说明两种不同语言对应向量空间的结构之间具有相似性,从而进一步说明了在词向量空间中利用距离刻画词之间相似性的合理性。

语言模型

p(s)=p(w1,w2,w3,w4,…,wn)
=p(w1)p(w2|w1)p(w3\w1,w2)…p(wn|w1,w2,…,wn-1)
p(S)被称为语言模型,即用来计算一个句子概率的模型。
1.数据过于稀疏
2.参数空间太大

语言模型其实就是看一句话是不是正常人说出来的。这玩意很有用,比如机器翻译、语音识别得到若干候选之后,可以利用语言模型挑一个尽量靠谱的结果。在 NLP 的其它任务里也都能用到。

语言模型形式化的描述就是给定一个T个词的字符串s,看它是自然语言的概率P(w1,w2,…,wt)。w1 到 wT 依次表示这句话中的各个词。有个很简单的推论是:

 词向量在机器翻译领域的一个应用                 (1)

上面那个概率表示的意义是:第一个词确定后,看后面的词在前面的词出现的情况下出现的概率。如一句话“大家喜欢吃苹果”,总共四个词“大家”,“喜欢”,“吃”,“苹果”,怎么分词现在不讨论,总之词已经分好,就这四个。那么这句话是一个自然语言的概率是:

P(大家,喜欢,吃,苹果)=p(大家)p(喜欢|大家)p(吃|大家,喜欢)p(苹果|大家,喜欢,吃)

p(大家)表示“大家”这个词在语料库里面出现的概率;

p(喜欢|大家)表示“喜欢”这个词出现在“大家”后面的概率;

p(吃|大家,喜欢)表示“吃”这个词出现在“大家喜欢”后面的概率;

p(苹果|大家,喜欢,吃)表示“苹果”这个词出现在“大家喜欢吃”后面的概率。

把这些概率连乘起来,得到的就是这句话平时出现的概率。

如果这个概率特别低,说明这句话不常出现,那么就不算是一句自然语言,因为在语料库里面很少出现。如果出现的概率高,就说明是一句自然语言。

看到了上面的计算,看有多麻烦:只有四个词的一句话,需要计算的是p(大家),p(喜欢|大家),p(吃|大家,喜欢),p(苹果|大家,喜欢,吃)这四个概率,这四个概率还要预先计算好,考虑词的数量,成千上万个,再考虑组合数,p(吃|大家,喜欢)这个有“大家”、“喜欢”和“吃”的组合,总共会上亿种情况吧;再考虑p(苹果|大家,喜欢,吃)这个概率,总共也会超过万亿种。

从上面的情况看来,计算起来是非常麻烦的,一般都用偷懒的方式。

为了表示简单,上面的公式(1)用下面的方式表示

其中,如果Contexti是空的话,就是它自己p(w),另外如“吃”的Context就是“大家”、“喜欢”,其余的对号入座。

N-gram模型

接下来说怎么计算P(wi|Contexti),上面看的是跟据这句话前面的所有词来计算,那么就得计算很多了,比如就得把语料库里面p(苹果 大家,喜欢,吃)这种情况全部统计一遍,那么为了计算这句话的概率,就上面那个例子,都得扫描四次语料库。这样一句话有多少个词就得扫描多少趟,语料库一般都比较大,越大的语料库越能提供准确的判断。这样的计算速度在真正使用的时候是万万不可接受的,线上扫描一篇文章是不是一推乱七八糟的没有序列的文字都得扫描很久,这样的应用根本没人考虑。

最好的办法就是直接把所有的P(wi|Contexti)提前算好了,那么根据排列组上面的来算,对于一个只有四个词的语料库,总共就有4!+3!+2!+1!个情况要计算,那就是24个情况要计算;换成1000个词的语料库,就是Σ1000,i=1(i!)个情况需要统计,对于计算机来说,计算这些东西简直是开玩笑。

这就诞生了很多偷懒的方法,N-gram模型是其中之一了。N-gram什么情况呢?上面的context都是这句话中这个词前面的所有词作为条件的概率,N-gram就是只管这个词前面的n-1个词,加上它自己,总共n个词,计算P(wi|Contexti)只考虑用这n个词来算,换成数学的公式来表示,就是

这里如果n取得比较小的话,就比较省事了,当然也要看到n取得太小,会特别影响效果的,有可能计算出来的那个概率很不准。怎么平衡这个效果和计算就是大牛们的事情了,据大牛们的核算,n取2效果都还凑合,n取3就相当不错了,n取4就顶不住了。看下面的一些数据,假设词表中词的个数 V = 20,000 词,那么有下面的一些数据。

照图中的数据看去,取n=3是目前计算能力的上限了。在实践中用的最多的就是bigram和trigram了,而且效果也基本够了。

N-gram模型也会有写问题,总结如下:

  1. n不能取太大,取大了语料库经常不足,所以基本是用降级的方法
  2. 无法建模出词之间的相似度,就是有两个词经常出现在同一个context后面,但是模型是没法体现这个相似性的。
  3. 有些n元组(n个词的组合,跟顺序有关的)在语料库里面没有出现过,对应出来的条件概率就是0,这样一整句话的概率都是0了,这是不对的,解决的方法主要是两种:平滑法(基本上是分子分母都加一个数)和回退法(利用n-1的元组的概率去代替n元组的概率)

N-pos模型

当然学术是无止境的,有些大牛觉得这还不行,因为第i个词很多情况下是条件依赖于它前面的词的语法功能的,所以又弄出来一个n-pos模型,n-pos模型也是用来计算的,但是有所改变,先对词按照词性(Part-of-Speech,POS)进行了分类,具体的数学表达是

其中c是类别映射函数,功能是把V个词映射到K个类别(1=<K<=V)。这样搞的话,原来的V个词本来有种n元组减少到了种。

其他的模型还很多。

模型的问题与目标

如果是原始的直接统计语料库的语言模型,那是没有参数的,所有的概率直接统计就得到了。但现实往往会带一些参数,所有语言模型也能使用极大似然作为目标函数来建立模型。下面就讨论这个。

假设语料库是一个由T个词组成的词序列s(这里可以保留疑问的,因为从很多资料看来是不管什么多少篇文档,也不管句子什么的,整个语料库就是一长串词连起来的,或许可以根据情况拆成句子什么的,这里就往简单里说),其中有V个词,则可以构建下面的极大似然函数

另外,做一下对数似然

对数似然还有些人称为交叉熵,这里不纠结也不介绍。

上面的问题跟正常的情况不太符合,来看看下一种表达。假设语料库是有S个句子组成的一个句子序列(顺序不重要),同样是有V个词,似然函数就会构建成下面的样子

对数似然就会是下面的样子

为啥要注意这个问题呢?原因有多种,计算P(wi|Contexti)这个东西的参数是主要的原因。

为啥会有参数呢?在计算P(wi|Contexti)这个东西的过程中,有非常多的方法被开发出来了,如上面的平滑法,回退法上面的,但这些都是硬统计一下基本就完了;这就带来一些需要求的参数,如平滑法中使用的分子分母分别加上的常数是什么?

这还不够,假如用的是trigram,还得存储一个巨大的元组与概率的映射(如果不存储,就得再进行使用的时候实际统计,那太慢了),存这个东西可需要很大的内存,对计算机是个大难题。

这都难不倒大牛们,他们考虑的工作是利用函数来拟合计算P(wi|Contexti),换句话说,不是根据语料库统计出来的,而是直接把context和wi代到一个函数里面计算出来的,这样在使用的时候就不用去查那个巨大的映射集了(或者取语料库里面统计这个概率)。用数学的方法描述就是

那么探索这个函数的具体形式就是主要的工作了,也是后面word2vec的工作的主要内容。函数的形式实在太多了,线性的还好,非线性真叫一个多,高维非线性的就更多了。

探索一个函数的具体形式的术语叫做拟合。

然后就有人提出了用神经网络来拟合这个函数,就有了各种方法,word2vec是其中的一种。

CBOW加层次的网络结构与使用说明

Word2vec总共有两种类型,每种类型有两个策略,总共4种。这里先说最常用的一种。这种的网络结构如下图。

其中第一层,也就是最上面的那一层可以称为输入层。输入的是若干个词的词向量(词向量的意思就是把一个词表示成一个向量的形式表达,后面会介绍)。中间那个层可以成为隐层,是输入的若干个词向量的累加和,注意是向量的累加和,结果是一个向量。
第三层是方框里面的那个二叉树,可以称之为输出层,隐层的那个节点要跟输出层的那个二叉树的所有非叶节点链接的,线太多画不过来了。第三层的这个二叉树是一个霍夫曼树,每个非叶节点也是一个向量,但是这个向量不代表某个词,代表某一类别的词;每个叶子节点代表一个词向量,为了简单只用一个w表示,没有下标。另外要注意的是,输入的几个词向量其实跟这个霍夫曼树中的某几个叶子节点是一样的,当然输入的那几个词跟它们最终输出的到的那个词未必是同一个词,而且基本不会是同一个词,只是这几个词跟输出的那个词往往有语义上的关系。
还有要注意的是,这个霍夫曼树的所有叶子节点就代表了语料库里面的所有词,而且是每个叶子节点对应一个词,不重复。
这个网络结构的功能是为了完成一个的事情——判断一句话是否是自然语言。怎么判断呢?使用的是概率,就是计算一下这句话的“一列词的组合”的概率的连乘(联合概率)是多少,如果比较低,那么就可以认为不是一句自然语言,如果概率高,就是一句正常的话。这个其实也是语言模型的目标。前面说的“一列词的组合”其实包括了一个词跟它的上下文的联合起来的概率,一种普通的情况就是每一个词跟它前面所有的词的组合的概率的连乘,这个后面介绍。
对于上面的那个网络结构来说,网络训练完成后,假如给定一句话s,这句话由词w1,w2,w3,…,wT组成,就可以利用计算这句话是自然语言的概率了,计算的公式是下面的公式

其中的Context表示的是该词的上下文,也就是这个词的前面和后面各若干个词,这个“若干”(后面简称c)一般是随机的,也就是一般会从1到5之间的一个随机数;每个代表的意义是前后的c个词分别是那几个的情况下,出现该词的概率。举个例子就是:“大家 喜欢 吃 好吃 的 苹果”这句话总共6个词,假设对“吃”这个词来说c随机抽到2,则“吃”这个词的context是“大家”、“喜欢”、“好吃”和“的”,总共四个词,这四个词的顺序可以乱,这是word2vec的一个特点。
计算的时候都要用到上面的那个网络,具体计算的方法用例子说明,假设就是计算“吃”这个词的在“大家”、“喜欢”、“好吃”和“的”这四个词作为上下文的条件概率,又假设“吃”这个词在霍夫曼树中是的最右边那一个叶子节点,那么从根节点到到达它就有两个非叶节点,根节点对应的词向量命名为A,根节点的右孩子节点对应的词向量命名为B,另外再假设“大家”、“喜欢”、“好吃”和“的”这四个词的词向量的和为C,则

其中,是sigmoid公式。为什么这么算,看完后面就明白了,这里仅仅说个流程,让后面的描述流畅起来。要注意的是,如果“吃”这个词在非叶节点B的左孩子节点(假设称为E)的右边的那个叶子节点,也就是在图中右边的三个叶子的中间那个,则有

上面的那句话的每个词都计算后连乘起来得到联合概率,这个概率如果大于某个阈值,就认为是正常的话;否则就认为不是自然语言,要排除掉。
对于这个神经网络的描述索然无味,因为主角也不是这个概率,这个神经网络最重要的是输出层的那个霍夫曼树的叶子节点上的那些向量,那些向量被称为词向量,词向量就是另外一篇博文里面介绍的,是个好东西。
至于霍夫曼树,其实是一个优化的解法,后面再提。

优化目标与解问题

从霍夫曼树到条件概率的计算

前面已经提过语言模型的目标就是判断一句话是否是正常的,至于怎么判断则需要计算很多条件概率如,然后还要把这些条件概率连乘起来得到联合概率。这样就带来了问题了——怎么去计算,有很多办法的,后面的章节会介绍。这里的word2vec的计算这个条件概率的方法是利用神经网络的能量函数,因为在能量模型中,能量函数的功能是把神经网络的状态转化为概率表示,这在另外一篇博文RBM里面有提到,具体要看hinton的论文来了解了。能量模型有个特别大的好处,就是能拟合所有的指数族的分布。那么,如果认为这些条件概率是符合某个指数族的分布的话,是可以用能量模型去拟合的。总之word2vec就认为这个条件概率可以用能量模型来表示了。
既然是能量模型,那么就需要能量函数,word2vec定义了一个非常简单的能量函数

E(A,C)=-(A∙C)

其中A可以认为是某个词的词向量,C是这个词的上下文的词向量的和(向量的和),基本上就可以认为C代表Context;中间的点号表示两个向量的内积。
然后根据能量模型(这个模型假设了温度一直是1,所以能量函数没有分母了),就可以表示出词A的在上下文词向量C下的概率来了。
P(Y|X) = $\sum_i{p(Y, D=i|X)} = $\sum_i{P(Y|D=i, X)P(D=i|X)} = P(Y|D=d(Y), X)P(D=d(Y)|X)

式子(2.1.3)说明了一个问题,计算一个词A在上下文C的情况下出现的概率,可以先对语料库中的词分成两簇,然后能节省计算。现在来展示一下怎么节省计算,假设G,H这两类的簇就用G和H表示(G和H也是一个词向量,意思就是G表示了其中一簇的词,H表示了另外一簇的词,G和H只是一个代表,也不是什么簇中心的说法。其实如果情况极端点,只有两个词,看下面的式子就完全没问题了。在多个词的情况下,就可以认为词被分成了两团,G和H个表示一团的词,计算概率什么的都以一整团为单位),那么式子(2.3)中的p(G|C)可以用下面的式子计算

P(G|C) = $\frac{e^{-E(G,C)}}{e^{-E(G,C)} + e^{-E(H,C)}} = $\frac{1}{1 + e^{-E(-(H-G)*C)}} = $\frac{1}{1 + e^{-E(H-G,C)}}

也就是说,可以不用关心这两个簇用什么表示,只要利用一个F=H-G的类词向量的一个向量就可以计算P(G|C)了,所以这一步是很节省时间的。再看另外一步

P(A|G,C) = $\frac{e^(-E(A,C))}{\sum_{W->G}{e^{-E(W,C)}}}

由于在G内的词数量只有V/2个,也就是说计算分母的时候只要计算V/2个词的能量就可以了。这已经省了一半的计算量了,可惜科学家们是贪得无厌的,所以还要继续省,怎么来呢?把G类词再分成两个簇GG,GH,A在GH里面,然后 p(A│G,C)=p(A|GH,G,C)p(GH|G,C) 同样有

同样可以把GG-GH用一个类词向量表达,这时候 p(A│C)=p(A|GH,G,C)p(GH|G,C)p(G|C) 继续下去假设继续分到GHG簇的时候只剩两个词了,再分两簇为GHGG和GHGH,其中的簇GHGG就只有一个词A,那么p(A│C)可以用下面的式子算 p(A│C)=p(A│GHGG,GHG,GH,G,C)p(GHGG|GHG,GH,G,C)p(GHG|GH,G,C)p(GH|G,C)p(G|C) 其中p(A|GHGG,GHG,GH,G)是1,因为只有一个单词,代到公式(2.2)就可以得到,那么就有 p(A│C)=p(GHGG|GHG,GH,G,C)p(GHG|GH,G,C)p(GH|G,C)p(G|C) 也就是

假设再令FFF=GHH-GHG,FF=GG-GH,F=H-G,那么p(A|C)只要算这三个词与上下文C的能量函数了,确实比原来的要节省很多计算的。 对于上面的霍夫曼树来说假设G表示向右,H表示向左,那么A就是从右边开始数的第二个叶子节点,就是图中右边的三个W的中间那个。那么F,FF,FFF就是这个叶子节点路径上的三个非叶节点。 但是一个词总是会一会向左,一会向右的,也就是在根节点那里,一会是p(G|C)那么F=H-G,一会又是p(H|C)那么F=G-H,如果F在每个节点都是唯一一个值,就可以直接用一次词向量表示这个非叶节点了。这下难不倒科学家的,令F一直是等于H-G,那么一直有

并且有p(G|C)=1-p(H|C)。 这样每个非叶节点就可以用唯一一个词向量表示了。 看到这里,总该明白为啥p(A|C)要这么算了吧。再换种情况,上面的概率这个概率的计算方法是不是也是同样的道理? 总结下来,可以用下面的公式计算了

其中C表示上下文的词向量累加后的向量,qk表示从根节点下来到叶子节点的路径上的那些非叶节点,dk就是编码了,也可以说是分类,因为在霍夫曼树的每个非叶节点都只有两个孩子节点,那可以认为当wi在这个节点的左子树的叶子节点上时dk=0,否则dk=1。这样的话每个词都可以用一组霍夫曼编码来表示,就有了上面的那个式子中间的那个dk,整个p(w\│Context)就可以用霍夫曼树上的若干个非叶节点和词w的霍夫曼编码来计算了。 看到这务必想明白,因为开始要讨论怎么训练了。

霍夫曼树

上面输出层的二叉树是霍夫曼树,其实并没有要求是霍夫曼树,随便一个不太离谱的二叉树都可以的,但是用霍夫曼树能达到最优的计算效果。 根据之前的讨论,已经知道了语料库里面每个词都要从根节点下来,一直走到叶子节点,每经过一个非叶节点,就要计算一个sigmoid函数。 随便乱分也能达到效果,但是信息熵理论给出了最优的方案——霍夫曼树。具体可以查看其它资料。

目标函数

what’s word2vec?

一种通用的方法来学习神经词嵌入的问题。

  1. 定义一个根据中心词汇预测上下文的词汇(概率的方法,给定单词预测上下文单词出现的概率)
  2. 用损失函数来判断预测的准确性
  3. 调整词向量使得损失函数最小化

word2vec 模型的核心是构建一个很简单的、可扩展的、快速的训练模型,让我们可以处理数十亿单词的文本,并生成非常棒的单词表示。

Mian idea of word2vec

Predict between every word and its context words!

Two algorithms

  1. Skip-grams(SG) Predict context words given target (position independent)
  2. Continuous Bag of Word(CBOW) Predict target word from bag-of-words context
Skip-gram prediction

skip-grams
Skip-grams的概念是在每个估算步都取一个词作为中心词汇。
这个模型将定义一个概率分布,即给定一个中心词汇,某个单词在他上下文出现的概率。
我们会选取词汇的向量表示,以让概率分布最大化.

这个模型只有一个概率分布,对于一个词汇,有且仅有一个概率分布.

Details of word2vec:

  1. 对于每个单词t,预测周围单词. 范围为从中心词开始,到距离为m的位置.
  2. Objective function: Maximize the probability of any context word given the center word:
    function
    theta是词汇向量的表示.

objective function = cost function = loss function

skip-grams model


参考资料:

  1. cs224n
  2. S的视频
  3. A的视频
  4. Efficient Estimation of Word Representations in Vector Space
  5. A Neural Probabilistic Language Model
  6. 深度学习word2vec笔记之基础篇
  7. 深度学习word2vec笔记之算法篇
  8. CS224N NLP with Deep Learning(二):词向量之Word2Vec

Til next time,
gentlesnow at 19:42

scribble