【zz】Word2Vec

简介

Word2vec 是 Google 在 2013 年年中开源的一款将词表征为实数值向量的高效工具, 其利用深度学习的思想,可以通过训练,把对文本内容的处理简化为 K 维向量空间中的向量运算,而向量空间上的相似度可以用来表示文本语义上的相似度。Word2vec输出的词向量可以被用来做很多 NLP 相关的工作,比如聚类、找同义词、词性分析等等。如果换个思路, 把词当做特征,那么Word2vec就可以把特征映射到 K 维向量空间,可以为文本数据寻求更加深层次的特征表示 。

 

Word2vec 使用的是 Distributed representation 的词向量表示方式。Distributed representation 最早由 Hinton在 1986 年提出。其基本思想是 通过训练将每个词映射成 K 维实数向量(K 一般为模型中的超参数),通过词之间的距离(比如 cosine 相似度、欧氏距离等)来判断它们之间的语义相似度.其采用一个 三层的神经网络 ,输入层-隐层-输出层。有个核心的技术是 根据词频用Huffman编码 ,使得所有词频相似的词隐藏层激活的内容基本一致,出现频率越高的词语,他们激活的隐藏层数目越少,这样有效的降低了计算的复杂度。而Word2vec大受欢迎的一个原因正是其高效性,Mikolov 在论文中指出,一个优化的单机版本一天可训练上千亿词。

 

这个三层神经网络本身是 对语言模型进行建模 ,但也同时 获得一种单词在向量空间上的表示 ,而这个副作用才是Word2vec的真正目标。

 

与潜在语义分析(Latent Semantic Index, LSI)、潜在狄立克雷分配(Latent Dirichlet Allocation,LDA)的经典过程相比,Word2vec利用了词的上下文,语义信息更加地丰富。

 

0. 词向量是什么

  自然语言理解的问题要转化为机器学习的问题,第一步肯定是要找一种方法把这些符号数学化。
  NLP 中最直观,也是到目前为止最常用的词表示方法是 One-hot Representation,这种方法把每个词表示为一个很长的向量。这个向量的维度是词表大小,其中绝大多数元素为 0,只有一个维度的值为 1,这个维度就代表了当前的词。
  举个栗子,
  “话筒”表示为 [0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 …]
  “麦克”表示为 [0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 …]
  每个词都是茫茫 0 海中的一个 1。
  这种 One-hot Representation 如果采用稀疏方式存储,会是非常的简洁:也就是给每个词分配一个数字 ID。比如刚才的例子中,话筒记为 3,麦克记为 8(假设从 0 开始记)。如果要编程实现的话,用 Hash 表给每个词分配一个编号就可以了。这么简洁的表示方法配合上最大熵、SVM、CRF 等等算法已经很好地完成了 NLP 领域的各种主流任务。
  当然这种表示方法也存在一个重要的问题就是“词汇鸿沟”现象:任意两个词之间都是孤立的。光从这两个向量中看不出两个词是否有关系,哪怕是话筒和麦克这样的同义词也不能幸免于难。

  Deep Learning 中一般用到的词向量并不是刚才提到的用 One-hot Representation 表示的那种很长很长的词向量,而是用 Distributed Representation(不知道这个应该怎么翻译,因为还存在一种叫“Distributional Representation”的表示方法,又是另一个不同的概念)表示的一种低维实数向量。这种向量一般长成这个样子:[0.792, −0.177, −0.107, 0.109, −0.542, …]。维度以 50 维和 100 维比较常见。这种向量的表示不是唯一的,后文会提到目前计算出这种向量的主流方法。
  (个人认为)Distributed representation 最大的贡献就是让相关或者相似的词,在距离上更接近了。向量的距离可以用最传统的欧氏距离来衡量,也可以用 cos 夹角来衡量。用这种方式表示的向量,“麦克”和“话筒”的距离会远远小于“麦克”和“天气”。可能理想情况下“麦克”和“话筒”的表示应该是完全一样的,但是由于有些人会把英文名“迈克”也写成“麦克”,导致“麦克”一词带上了一些人名的语义,因此不会和“话筒”完全一致。

1. 词向量的训练

  要介绍词向量是怎么训练得到的,就不得不提到语言模型。到目前为止我了解到的所有训练方法都是在训练语言模型的同时,顺便得到词向量的
  这也比较容易理解,要从一段无标注的自然文本中学习出一些东西,无非就是统计出词频、词的共现、词的搭配之类的信息。而要从自然文本中统计并建立一个语言模型,无疑是要求最为精确的一个任务(也不排除以后有人创造出更好更有用的方法)。既然构建语言模型这一任务要求这么高,其中必然也需要对语言进行更精细的统计和分析,同时也会需要更好的模型,更大的数据来支撑。目前最好的词向量都来自于此,也就不难理解了。
  这里介绍的工作均为从大量未标注的普通文本数据中无监督地学习出词向量(语言模型本来就是基于这个想法而来的),可以猜测,如果用上了有标注的语料,训练词向量的方法肯定会更多。不过视目前的语料规模,还是使用未标注语料的方法靠谱一些。
  词向量的训练最经典的有 3 个工作,C&W 2008、M&H 2008、Mikolov 2010。当然在说这些工作之前,不得不介绍一下这一系列中 Bengio 的经典之作。

1.0 语言模型简介

  插段广告,简单介绍一下语言模型,知道的可以无视这节。
  语言模型其实就是看一句话是不是正常人说出来的。这玩意很有用,比如机器翻译、语音识别得到若干候选之后,可以利用语言模型挑一个尽量靠谱的结果。在 NLP 的其它任务里也都能用到。
  语言模型形式化的描述就是给定一个字符串,看它是自然语言的概率 P(w1,w2,,wt)P(w1,w2,…,wt)。w1w1 到 wtwt 依次表示这句话中的各个词。有个很简单的推论是:
P(w1,w2,,wt)=P(w1)×P(w2|w1)×P(w3|w1,w2)××P(wt|w1,w2,,wt1)P(w1,w2,…,wt)=P(w1)×P(w2|w1)×P(w3|w1,w2)×…×P(wt|w1,w2,…,wt−1)
  常用的语言模型都是在近似地求 P(wt|w1,w2,,wt1)P(wt|w1,w2,…,wt−1)。比如 n-gram 模型就是用 P(wt|wtn+1,,wt1)P(wt|wt−n+1,…,wt−1) 近似表示前者。
  顺便提一句,由于后面要介绍的每篇论文使用的符号差异太大,本博文里尝试统一使用 Bengio 2003 的符号系统(略做简化),以便在各方法之间做对比和分析。

1.1 Bengio 的经典之作

    训练语言模型的最经典之作,要数 Bengio 等人在 2001 年发表在 NIPS 上的文章《A Neural Probabilistic Language Model》。当然现在看的话,肯定是要看他在 2003 年投到 JMLR 上的同名论文了。

  Bengio 用了一个三层的神经网络来构建语言模型,同样也是 n-gram 模型。如图1。

Bengio 2003

      图中最下方的 wtn+1,,wt2,wt1wt−n+1,…,wt−2,wt−1 就是前 n1n−1 个词。现在需要根据这已知的 n1n−1 个词预测下一个词 wtwt。C(w)C(w) 表示词 ww 所对应的词向量,整个模型中使用的是一套唯一的词向量,存在矩阵 CC(一个 |V|×m|V|×m 的矩阵)中。其中 |V||V| 表示词表的大小(语料中的总词数),mm 表示词向量的维度。ww 到 C(w)C(w) 的转化就是从矩阵中取出一行。

 

     

网络的第一层(输入层)是将 C(wtn+1),,C(wt2),C(wt1)C(wt−n+1),…,C(wt−2),C(wt−1) 这 n1n−1 个向量首尾相接拼起来,形成一个 (n1)m(n−1)m 维的向量,下面记为 xx。
  网络的第二层(隐藏层)就如同普通的神经网络,直接使用 d+Hxd+Hx 计算得到。dd 是一个偏置项。在此之后,使用 tanhtanh 作为激活函数。
  网络的第三层(输出层)一共有 |V||V| 个节点,每个节点 yiyi 表示 下一个词为 ii 的未归一化 log 概率。最后使用 softmax 激活函数将输出值 yy 归一化成概率。最终,yy 的计算公式为:

 

y=b+Wx+Utanh(d+Hx)y=b+Wx+Utanh⁡(d+Hx)

 

  式子中的 UU(一个 |V|×h|V|×h 的矩阵)是隐藏层到输出层的参数,整个模型的多数计算集中在 UU 和隐藏层的矩阵乘法中。后文的提到的 3 个工作,都有对这一环节的简化,提升计算的速度。
  式子中还有一个矩阵 WW(|V|×(n1)m|V|×(n−1)m),这个矩阵包含了从输入层到输出层的直连边。直连边就是从输入层直接到输出层的一个线性变换,好像也是神经网络中的一种常用技巧(没有仔细考察过)。如果不需要直连边的话,将 WW 置为 0 就可以了。在最后的实验中,Bengio 发现直连边虽然不能提升模型效果,但是可以少一半的迭代次数。同时他也猜想如果没有直连边,可能可以生成更好的词向量。

  现在万事俱备,用随机梯度下降法把这个模型优化出来就可以了。需要注意的是,一般神经网络的输入层只是一个输入值,而在这里,输入层 xx 也是参数(存在 CC 中),也是需要优化的。优化结束之后,词向量有了,语言模型也有了。
  这样得到的语言模型自带平滑,无需传统 n-gram 模型中那些复杂的平滑算法。Bengio 在 APNews 数据集上做的对比实验也表明他的模型效果比精心设计平滑算法的普通 n-gram 算法要好 10% 到 20%。

 

2. word2vec原理

传统的统计语言模型是表示语言基本单位(一般为句子)的概率分布函数,这个概率分布也就是该语言的生成模型。一般语言模型可以使用各个词语条件概率的形式表示:

p(s)=p(w_1^T )=p(w_1,w_2,…,w_T )=∏_t p(w_t |context)

Word2vec采用的是__层次化Log-Bilinear语言模型__,其中一种是CBOW(Continuous Bag-of-Words Model)模型,由上下文预测下一个词为w_t的公式为:

p(w_t |context)=p(w_t |w_(t-k),w_(t-k+1),…,w_(t-1),w_(t+1),…,w_(t+k-1),w_(t+k))

CBOW的计算可以用 层次Softmax算法 ,这种算法结合了Huffman编码,每个词 w 都可以从树的根结点root沿着唯一一条路径被访问到,其路径也就形成了其编码code。假设 n(w, j)为这条路径上的第 j 个结点,且 L(w)为这条路径的长度, j 从 1 开始编码,即 n(w, 1)=root,n(w, L(w)) = w。对于第 j 个结点,层次 Softmax 定义的Label 为 1 - code[j]。

 

取一个适当大小的窗口当做语境,输入层读入窗口内的词,将它们的向量(K维,初始随机)加和在一起,形成隐藏层K个节点。输出层是一个巨大的二叉树,叶节点代表语料里所有的词(语料含有V个独立的词,则二叉树有|V|个叶节点)。而这整颗二叉树构建的算法就是Huffman树。这样,对于叶节点的每一个词,就会有一个全局唯一的编码,形如"010011",不妨记左子树为1,右子树为0。接下来,隐层的每一个节点都会跟二叉树的内节点有连边,于是对于二叉树的每一个内节点都会有K条连边,每条边上也会有权值。

 

对于语料库中的某个词w_t,对应着二叉树的某个叶子节点,因此它必然有一个二进制编码,如"010011"。在训练阶段,当给定上下文,要预测后面的词w_t的时候,我们就从二叉树的根节点开始遍历,这里的目标就是预测这个词的二进制编号的每一位。即对于给定的上下文,我们的目标是使得预测词的二进制编码概率最大。形象地说,我们希望在根节点,词向量和与根节点相连经过logistic计算得到bit=1的概率尽量接近0,在第二层,希望其bit=1的概率尽量接近1,这么一直下去,我们把一路上计算得到的概率相乘,即得到目标词w_t在当前网络下的概率P(w_t),那么对于当前这个sample的残差就是1-P(w_t),于是就可以使用梯度下降法训练这个网络得到所有的参数值了。显而易见,按照目标词的二进制编码计算到最后的概率值就是归一化的。

Hierarchical Softmax用Huffman编码构造二叉树,其实借助了分类问题中,使用一连串二分类近似多分类的思想。例如我们是把所有的词都作为输出,那么“桔子”、“汽车”都是混在一起。给定w_t的上下文,先让模型判断w_t是不是名词,再判断是不是食物名,再判断是不是水果,再判断是不是“桔子”。

但是在训练过程中,模型会赋予这些抽象的中间结点一个合适的向量,这个向量代表了它对应的所有子结点。因为真正的单词公用了这些抽象结点的向量,所以Hierarchical Softmax方法和原始问题并不是等价的,但是这种近似并不会显著带来性能上的损失同时又使得模型的求解规模显著上升。

没有使用这种二叉树,而是直接从隐层直接计算每一个输出的概率——即传统的Softmax,就需要对|V|中的每一个词都算一遍,这个过程时间复杂度是O(|V|)的。而使用了二叉树(如Word2vec中的Huffman树),其时间复杂度就降到了O(log2(|V|)),速度大大地加快了。

posted @ 2016-11-11 16:33  ShellyYang  阅读(178)  评论(1)    收藏  举报