[NLP] 秒懂词向量Word2vec的本质

1. 引子

大家好
我叫数据挖掘机
皇家布鲁斯特大学肄业
我喝最烈的果粒橙,钻最深的牛角尖
——执着如我

今天我要揭开Word2vec的神秘面纱
直窥其本质

相信我,这绝对是你看到的
最浅白易懂的 Word2vec 中文总结

(蛤?你问我为啥有这个底气?
且看下面,我的踩坑血泪史。。。)

2. Word2vec参考资料总结

(以下都是我踩过的坑,建议先跳过本节,阅读正文部分,读完全文回头再来看)

先大概说下我深挖 word2vec 的过程:先是按照惯例,看了 Mikolov 关于 Word2vec 的两篇原始论文,然而发现看完依然是一头雾水,似懂非懂,主要原因是这两篇文章省略了太多理论背景和推导细节;然后翻出 Bengio 03年那篇JMLR和 Ronan 11年那篇JMLR,看完对语言模型、用CNN处理NLP任务有所了解,但依然无法完全吃透 word2vec;这时候我开始大量阅读中英文博客,其中 北漂浪子 的一篇阅读量很多的博客吸引了我的注意,里面非常系统地讲解了 Word2vec 的前因后果,最难得的是深入剖析了代码的实现细节,看完之后细节方面了解了很多,不过还是觉得有些迷雾;终于,我在 quora 上看到有人推荐 Xin Rong 的那篇英文paper,看完之后只觉醍醐灌顶,酣畅淋漓,相见恨晚,成为我首推的 Word2vec 参考资料。下面我将详细列出我阅读过的所有 Word2vec 相关的参考资料,并给出评价

  1. Mikolov 两篇原论文:
    1. 『Distributed Representations of Sentences and Documents』
  • 在前人基础上提出更精简的语言模型(language model)框架并用于生成词向量,这个框架就是 Word2vec
  • 『Efficient estimation of word representations in vector space』
  • 专门讲训练 Word2vec 中的两个trick:hierarchical softmax 和 negative sampling
  • 优点:Word2vec 开山之作,两篇论文均值得一读
  • 缺点:只见树木,不见森林和树叶,读完不得要义。这里『森林』指 word2vec 模型的理论基础——即 以神经网络形式表示的语言模型,『树叶』指具体的神经网络形式、理论推导、hierarchical softmax 的实现细节等等

 

  1. 北漂浪子的博客:『深度学习word2vec 笔记之基础篇』
  • 优点:非常系统,结合源码剖析,语言平实易懂
  • 缺点:太啰嗦,有点抓不住精髓

 

  1. Yoav Goldberg 的论文:『word2vec Explained- Deriving Mikolov et al.’s Negative-Sampling Word-Embedding Method』
  • 优点:对 negative-sampling 的公式推导非常完备
  • 缺点:不够全面,而且都是公式,没有图示,略显干枯

 

  1. Xin Rong 的论文:『word2vec Parameter Learning Explained』:
  • !重点推荐!
  • 理论完备由浅入深非常好懂,且直击要害,既有 high-level 的 intuition 的解释,也有细节的推导过程
  • 一定要看这篇paper!一定要看这篇paper!一定要看这篇paper!
评论区 

@huichan

 告知了一条沉重的信息,Rong Xin 于2017年驾驶飞机失事,永远离开了我们。缅怀,R.I.P,愿他能在天堂继续开心地科研

 

  1. 来斯惟的博士论文『基于神经网络的词和文档语义向量表示方法研究』以及他的博客(网名:licstar)
  • 可以作为更深入全面的扩展阅读,这里不仅仅有 word2vec,而是把词嵌入的所有主流方法通通梳理了一遍

 

  1. 几位大牛在知乎的回答:『word2vec 相比之前的 Word Embedding 方法好在什么地方?』
  • 刘知远、邱锡鹏、李韶华等知名学者从不同角度发表对 Word2vec 的看法,非常值得一看

 

  1. Sebastian 的博客:『On word embeddings - Part 2: Approximating the Softmax』
  • 详细讲解了 softmax 的近似方法,Word2vec 的 hierarchical softmax 只是其中一种

 

3. 正文

你会在本文看到:
  1. 提纲挈领地讲解 word2vec 的理论精髓
  2. 学会用gensim训练词向量,并寻找相似词

你不会在本文看到

  1. 神经网络训练过程的推导
  2. hierarchical softmax/negative sampling 等 trick 的理论和实现细节

 

3.1. 什么是 Word2vec?

在聊 Word2vec 之前,先聊聊 NLP (自然语言处理)。NLP 里面,最细粒度的是 词语,词语组成句子,句子再组成段落、篇章、文档。所以处理 NLP 的问题,首先就要拿词语开刀。

举个简单例子,判断一个词的词性,是动词还是名词。用机器学习的思路,我们有一系列样本(x,y),这里 x 是词语,y 是它们的词性,我们要构建 f(x)->y 的映射,但这里的数学模型 f(比如神经网络、SVM)只接受数值型输入,而 NLP 里的词语,是人类的抽象总结,是符号形式的(比如中文、英文、拉丁文等等),所以需要把他们转换成数值形式,或者说——嵌入到一个数学空间里,这种嵌入方式,就叫词嵌入(word embedding),而 Word2vec,就是词嵌入( word embedding) 的一种

我在前作『都是套路: 从上帝视角看透时间序列和数据挖掘』提到,大部分的有监督机器学习模型,都可以归结为:

f(x)->y

在 NLP 中,把 x 看做一个句子里的一个词语,y 是这个词语的上下文词语,那么这里的 f,便是 NLP 中经常出现的『语言模型』(language model),这个模型的目的,就是判断 (x,y) 这个样本,是否符合自然语言的法则,更通俗点说就是:词语x和词语y放在一起,是不是人话。

Word2vec 正是来源于这个思想,但它的最终目的,不是要把 f 训练得多么完美,而是只关心模型训练完后的副产物——模型参数(这里特指神经网络的权重),并将这些参数,作为输入 x 的某种向量化的表示,这个向量便叫做——词向量(这里看不懂没关系,下一节我们详细剖析)。

我们来看个例子,如何用 Word2vec 寻找相似词:

  • 对于一句话:『她们 夸 吴彦祖 帅 到 没朋友』,如果输入 x 是『吴彦祖』,那么 y 可以是『她们』、『夸』、『帅』、『没朋友』这些词
  • 现有另一句话:『她们 夸 我 帅 到 没朋友』,如果输入 x 是『我』,那么不难发现,这里的上下文 y 跟上面一句话一样
  • 从而 f(吴彦祖) = f(我) = y,所以大数据告诉我们:我 = 吴彦祖(完美的结论)

3.2. Skip-gram 和 CBOW 模型

上面我们提到了语言模型

  • 如果是用一个词语作为输入,来预测它周围的上下文,那这个模型叫做『Skip-gram 模型』
  • 而如果是拿一个词语的上下文作为输入,来预测这个词语本身,则是 『CBOW 模型』

3.2.1 Skip-gram 和 CBOW 的简单情形

我们先来看个最简单的例子。上面说到, y 是 x 的上下文,所以 y 只取上下文里一个词语的时候,语言模型就变成:

用当前词 x 预测它的下一个词 y

但如上面所说,一般的数学模型只接受数值型输入,这里的 x 该怎么表示呢? 显然不能用 Word2vec,因为这是我们训练完模型的产物,现在我们想要的是 x 的一个原始输入形式。

答案是:one-hot encoder

所谓 one-hot encoder,其思想跟特征工程里处理类别变量的 one-hot 一样(参考我的前作『数据挖掘比赛通用框架』、『深挖One-hot和Dummy背后的玄机』)。本质上是用一个只含一个 1、其他都是 0 的向量来唯一表示词语。

我举个例子,假设全世界所有的词语总共有 V 个,这 V 个词语有自己的先后顺序,假设『吴彦祖』这个词是第1个词,『我』这个单词是第2个词,那么『吴彦祖』就可以表示为一个 V 维全零向量、把第1个位置的0变成1,而『我』同样表示为 V 维全零向量、把第2个位置的0变成1。这样,每个词语都可以找到属于自己的唯一表示。

OK,那我们接下来就可以看看 Skip-gram 的网络结构了,x 就是上面提到的 one-hot encoder 形式的输入,y 是在这 V 个词上输出的概率,我们希望跟真实的 y 的 one-hot encoder 一样。

[NLP] 秒懂词向量Word2vec的本质

 

首先说明一点:隐层的**函数其实是线性的,相当于没做任何处理(这也是 Word2vec 简化之前语言模型的独到之处),我们要训练这个神经网络,用反向传播算法,本质上是链式求导,在此不展开说明了,

当模型训练完后,最后得到的其实是神经网络的权重,比如现在输入一个 x 的 one-hot encoder: [1,0,0,…,0],对应刚说的那个词语『吴彦祖』,则在输入层到隐含层的权重里,只有对应 1 这个位置的权重被**,这些权重的个数,跟隐含层节点数是一致的,从而这些权重组成一个向量 vx 来表示x,而因为每个词语的 one-hot encoder 里面 1 的位置是不同的,所以,这个向量 vx 就可以用来唯一表示 x。

注意:上面这段话说的就是 Word2vec 的精髓!!

此外,我们刚说了,输出 y 也是用 V 个节点表示的,对应V个词语,所以其实,我们把输出节点置成 [1,0,0,…,0],它也能表示『吴彦祖』这个单词,但是**的是隐含层到输出层的权重,这些权重的个数,跟隐含层一样,也可以组成一个向量 vy,跟上面提到的 vx 维度一样,并且可以看做是词语『吴彦祖』的另一种词向量。而这两种词向量 vx 和 vy,正是 Mikolov 在论文里所提到的,『输入向量』和『输出向量』,一般我们用『输入向量』。

需要提到一点的是,这个词向量的维度(与隐含层节点数一致)一般情况下要远远小于词语总数 V 的大小,所以 Word2vec 本质上是一种降维操作——把词语从 one-hot encoder 形式的表示降维到 Word2vec 形式的表示。

3.2.2. Skip-gram 更一般的情形

上面讨论的是最简单情形,即 y 只有一个词,当 y 有多个词时,网络结构如下:

[NLP] 秒懂词向量Word2vec的本质

 

可以看成是 单个x->单个y 模型的并联,cost function 是单个 cost function 的累加(取log之后)

如果你想深入探究这些模型是如何并联、 cost function 的形式怎样,不妨仔细阅读参考资料4. 在此我们不展开。

3.2.3 CBOW 更一般的情形

跟 Skip-gram 相似,只不过:

Skip-gram 是预测一个词的上下文,而 CBOW 是用上下文预测这个词

网络结构如下

[NLP] 秒懂词向量Word2vec的本质

 

更 Skip-gram 的模型并联不同,这里是输入变成了多个单词,所以要对输入处理下(一般是求和然后平均),输出的 cost function 不变,在此依然不展开,建议你阅读参考资料4.

3.3. Word2vec 的训练trick

相信很多初次踩坑的同学,会跟我一样陷入 Mikolov 那篇论文(参考资料1.)里提到的 hierarchical softmax 和 negative sampling 里不能自拔,但其实,它们并不是 Word2vec 的精髓,只是它的训练技巧,但也不是它独有的训练技巧。 Hierarchical softmax 只是 softmax 的一种近似形式(详见参考资料7.),而 negative sampling 也是从其他方法借鉴而来。

为什么要用训练技巧呢? 如我们刚提到的,Word2vec 本质上是一个语言模型,它的输出节点数是 V 个,对应了 V 个词语,本质上是一个多分类问题,但实际当中,词语的个数非常非常多,会给计算造成很大困难,所以需要用技巧来加速训练。

这里我总结了一下这两个 trick 的本质,有助于大家更好地理解,在此也不做过多展开,有兴趣的同学可以深入阅读参考资料1.~7.

  • hierarchical softmax
    • 本质是把 N 分类问题变成 log(N)次二分类

 

  • negative sampling
    • 本质是预测总体类别的一个子集

 

3.4. 扩展

很多时候,当我们面对林林总总的模型、方法时,我们总希望总结出一些本质的、共性的东西,以构建我们的知识体系,比如我在前作『分类和回归的本质』里,原创性地梳理了分类模型和回归模型的本质联系,比如在词嵌入领域,除了 Word2vec之外,还有基于共现矩阵分解的 GloVe 等等词嵌入方法。

深入进去我们会发现,神经网络形式表示的模型(如 Word2vec),跟共现矩阵分解模型(如 GloVe),有理论上的相通性,这里我推荐大家阅读参考资料5. ——来斯惟博士在它的博士论文附录部分,证明了 Skip-gram 模型和 GloVe 的 cost fucntion 本质上是一样的。是不是一个很有意思的结论? 所以在实际应用当中,这两者的差别并不算很大,尤其在很多 high-level 的 NLP 任务(如句子表示、命名体识别、文档表示)当中,经常把词向量作为原始输入,而到了 high-level 层面,差别就更小了。

鉴于词语是 NLP 里最细粒度的表达,所以词向量的应用很广泛,既可以执行词语层面的任务,也可以作为很多模型的输入,执行 high-level 如句子、文档层面的任务,包括但不限于:

  • 计算相似度
    • 寻找相似词
    • 信息检索

 

  • 作为 SVM/LSTM 等模型的输入
    • 中文分词
    • 命名体识别

 

  • 句子表示
    • 情感分析

 

  • 文档表示
    • 文档主题判别

 

4. 实战

上面讲了这么多理论细节,其实在真正应用的时候,只需要调用 Gensim (一个 Python 第三方库)的接口就可以。但对理论的探究仍然有必要,你能更好地知道参数的意义、模型结果受哪些因素影响,以及举一反三地应用到其他问题当中,甚至更改源码以实现自己定制化的需求。

这里我们将使用 Gensim 和 NLTK 这两个库,来完成对生物领域的相似词挖掘,将涉及:

  • 解读 Gensim 里 Word2vec 模型的参数含义
  • 基于相应语料训练 Word2vec 模型,并评估结果
  • 对模型结果调优

语料我已经放出来了,可以关注我的公众号『数据挖掘机养成记』,并回复 Sherlocked 获取语料,包含5000行生物医学领域相关文献的摘要(英文)

 

我将在下一篇文章里详细讲解实战步骤,敬请关注本人公众号。友情建议:请先自行安装 Gensim 和 NLTK 两个库,并建议使用 jupyter notebook 作为代码运行环境

 

欢迎各路大神猛烈拍砖,共同交流

 

====评论区答疑节选====

Q1. gensim 和 google的 word2vec 里面并没有用到onehot encoder,而是初始化的时候直接为每个词随机生成一个N维的向量,并且把这个N维向量作为模型参数学习;所以word2vec结构中不存在文章图中显示的将V维映射到N维的隐藏层。

A1. 其实,本质是一样的,加上 one-hot encoder 层,是为了方便理解,因为这里的 N 维随机向量,就可以理解为是 V 维 one-hot encoder 输入层到 N 维隐层的权重,或者说隐层的输出(因为隐层是线性的)。每个 one-hot encoder 里值是 1 的那个位置,对应的 V 个权重被**,其实就是『从一个V*N的随机词向量矩阵里,抽取某一行』。学习 N 维向量的过程,也就是优化 one-hot encoder 层到隐含层权重的过程

Q2. hierarchical softmax 获取词向量的方式和原先的其实基本完全不一样,我初始化输入的也不是一个onehot,同时我是直接通过优化输入向量的形式来获取词向量?如果用了hierarchical 结构我应该就没有输出向量了吧?

 

A2. 初始化输入依然可以理解为是 one-hot,同上面的回答;确实是只能优化输入向量,没有输出向量了。具体原因,我们可以梳理一下不用 hierarchical (即原始的 softmax) 的情形:

隐含层输出一个 N 维向量 x, 每个x 被一个 N 维权重 w 连接到输出节点上,有 V 个这样的输出节点,就有 V 个权重 w,再套用 softmax 的公式,变成 V 分类问题。这里的类别就是词表里的 V 个词,所以一个词就对应了一个权重 w,从而可以用 w 作为该词的词向量,即文中的输出词向量。

PS. 这里的 softmax 其实多了一个『自由度』,因为 V 分类只需要 V-1 个权重即可

我们再看看 hierarchical softmax 的情形:

隐含层输出一个 N 维向量 x, 但这里要预测的目标输出词,不再是用 one-hot 形式表示,而是用 huffman tree 的编码,所以跟上面 V 个权重同时存在的原始 softmax 不一样, 这里 x 可以理解为先接一个输出节点,即只有一个权重 w1 ,输出节点输出 1/1+exp(-w*x),变成一个二分类的 LR,输出一个概率值 P1,然后根据目标词的 huffman tree 编码,将 x 再输出到下一个 LR,对应权重 w2,输出 P2,总共遇到的 LR 个数(或者说权重个数)跟 huffman tree 编码长度一致,大概有 log(V) 个,最后将这 log(V) 个 P 相乘,得到属于目标词的概率。但注意因为只有 log(V) 个权重 w 了,所以跟 V 个词并不是一一对应关系,就不能用 w 表征某个词,从而失去了词向量的意义

PS. 但我个人理解,这 log(V) 个权重的组合,可以表示某一个词。因为 huffman tree 寻找叶子节点的时候,可以理解成是一个不断『二分』的过程,不断二分到只剩一个词为止。而每一次二分,都有一个 LR 权重,这个权重可以表征该类词,所以这些权重拼接在一起,就表示了『二分』这个过程,以及最后分到的这个词的『输出词向量』。

我举个例子:

假设现在总共有 (A,B,C)三个词,huffman tree 这么构建:
第一次二分: (A,B), (C)
假如我们用的 LR 是二分类 softmax 的情形(比常见 LR 多了一个自由度),这样 LR 就有俩权重,权重 w1_1 是属于 (A,B) 这一类的,w1_2 是属于 (C) 的, 而 C 已经到最后一个了,所以 C 可以表示为 w1_2

第二次二分: (A), (B)
假设权重分别对应 w2_1 和 w2_2,那么 A 就可以表示为 [w1_1, w2_1], B 可以表示为 [w1_1, w2_2]

这样, A,B,C 每个词都有了一个唯一表示的词向量(此时他们长度不一样,不过可以用 padding 的思路,即在最后补0)

当然了,一般没人这么干。。。开个脑洞而已

 

 

Q3. 是否一定要用Huffman tree?

A3. 未必,比如用完全二叉树也能达到O(log(N))复杂度。但 Huffman tree 被证明是更高效、更节省内存的编码形式,所以相应的权重更新寻优也更快。 举个简单例子,高频词在Huffman tree中的节点深度比完全二叉树更浅,比如在Huffman tree中深度为3,完全二叉树中深度为5,则更新权重时,Huffmantree只需更新3个w,而完全二叉树要更新5个,当高频词频率很高时,算法效率高下立判

编辑于 2018-02-02

___________________________________

Doc 2:https://www.leiphone.com/news/201706/PamWKpfRFEI42McI.html

这次的分享主要是对Word2Vec模型的两篇英文文档的翻译、理解和整合,这两篇英文文档都是介绍Word2Vec中的Skip-Gram模型。下一篇专栏文章将会用TensorFlow实现基础版Word2Vec的skip-gram模型,所以本篇文章先做一个理论铺垫。

原文英文文档请参考链接:

- Word2Vec Tutorial - The Skip-Gram Model

http://t.cn/Rc5RfJ2

- Word2Vec (Part 1): NLP With Deep Learning with Tensorflow (Skip-gram)

http://t.cn/RoVEiUB

什么是Word2Vec和Embeddings?

Word2Vec是从大量文本语料中以无监督的方式学习语义知识的一种模型,它被大量地用在自然语言处理(NLP)中。那么它是如何帮助我们做自然语言处理呢?Word2Vec其实就是通过学习文本来用词向量的方式表征词的语义信息,即通过一个嵌入空间使得语义上相似的单词在该空间内距离很近。Embedding其实就是一个映射,将单词从原先所属的空间映射到新的多维空间中,也就是把原先词所在空间嵌入到一个新的空间中去。

我们从直观角度上来理解一下,cat这个单词和kitten属于语义上很相近的词,而dog和kitten则不是那么相近,iphone这个单词和kitten的语义就差的更远了。通过对词汇表中单词进行这种数值表示方式的学习(也就是将单词转换为词向量),能够让我们基于这样的数值进行向量化的操作从而得到一些有趣的结论。比如说,如果我们对词向量kitten、cat以及dog执行这样的操作:kitten - cat + dog,那么最终得到的嵌入向量(embedded vector)将与puppy这个词向量十分相近。

模型

Word2Vec模型中,主要有Skip-Gram和CBOW两种模型,从直观上理解,Skip-Gram是给定input word来预测上下文。而CBOW是给定上下文,来预测input word。本篇文章仅讲解Skip-Gram模型。

[NLP] 秒懂词向量Word2vec的本质

Skip-Gram模型的基础形式非常简单,为了更清楚地解释模型,我们先从最一般的基础模型来看Word2Vec(下文中所有的Word2Vec都是指Skip-Gram模型)。

Word2Vec模型实际上分为了两个部分,第一部分为建立模型,第二部分是通过模型获取嵌入词向量。Word2Vec的整个建模过程实际上与自编码器(auto-encoder)的思想很相似,即先基于训练数据构建一个神经网络,当这个模型训练好以后,我们并不会用这个训练好的模型处理新的任务,我们真正需要的是这个模型通过训练数据所学得的参数,例如隐层的权重矩阵——后面我们将会看到这些权重在Word2Vec中实际上就是我们试图去学习的“word vectors”。基于训练数据建模的过程,我们给它一个名字叫“Fake Task”,意味着建模并不是我们最终的目的。

上面提到的这种方法实际上会在无监督特征学习(unsupervised feature learning)中见到,最常见的就是自编码器(auto-encoder):通过在隐层将输入进行编码压缩,继而在输出层将数据解码恢复初始状态,训练完成后,我们会将输出层“砍掉”,仅保留隐层。

The Fake Task

我们在上面提到,训练模型的真正目的是获得模型基于训练数据学得的隐层权重。为了得到这些权重,我们首先要构建一个完整的神经网络作为我们的“Fake Task”,后面再返回来看通过“Fake Task”我们如何间接地得到这些词向量。

接下来我们来看看如何训练我们的神经网络。假如我们有一个句子“The dog barked at the mailman”

  • 首先我们选句子中间的一个词作为我们的输入词,例如我们选取“dog”作为input word;

  • 有了input word以后,我们再定义一个叫做skip_window的参数,它代表着我们从当前input word的一侧(左边或右边)选取词的数量。如果我们设置skip_window=2,那么我们最终获得窗口中的词(包括input word在内)就是['The', 'dog','barked', 'at']。skip_window=2代表着选取左input word左侧2个词和右侧2个词进入我们的窗口,所以整个窗口大小span=2x2=4。另一个参数叫num_skips,它代表着我们从整个窗口中选取多少个不同的词作为我们的output word,当skip_window=2,num_skips=2时,我们将会得到两组 (input word, output word) 形式的训练数据,即 ('dog', 'barked')('dog', 'the')

  • 神经网络基于这些训练数据将会输出一个概率分布,这个概率代表着我们的词典中的每个词是output word的可能性。这句话有点绕,我们来看个栗子。第二步中我们在设置skip_window和num_skips=2的情况下获得了两组训练数据。假如我们先拿一组数据 ('dog', 'barked') 来训练神经网络,那么模型通过学习这个训练样本,会告诉我们词汇表中每个单词是“barked”的概率大小。

模型的输出概率代表着到我们词典中每个词有多大可能性跟input word同时出现。举个栗子,如果我们向神经网络模型中输入一个单词“Soviet“,那么最终模型的输出概率中,像“Union”, ”Russia“这种相关词的概率将远高于像”watermelon“,”kangaroo“非相关词的概率。因为”Union“,”Russia“在文本中更大可能在”Soviet“的窗口中出现。我们将通过给神经网络输入文本中成对的单词来训练它完成上面所说的概率计算。下面的图中给出了一些我们的训练样本的例子。我们选定句子“The quick brown fox jumps over lazy dog”,设定我们的窗口大小为2(window_size=2),也就是说我们仅选输入词前后各两个词和输入词进行组合。下图中,蓝色代表input word,方框内代表位于窗口内的单词。

[NLP] 秒懂词向量Word2vec的本质

我们的模型将会从每对单词出现的次数中习得统计结果。例如,我们的神经网络可能会得到更多类似(“Soviet“,”Union“)这样的训练样本对,而对于(”Soviet“,”Sasquatch“)这样的组合却看到的很少。因此,当我们的模型完成训练后,给定一个单词”Soviet“作为输入,输出的结果中”Union“或者”Russia“要比”Sasquatch“被赋予更高的概率。

模型细节

我们如何来表示这些单词呢?首先,我们都知道神经网络只能接受数值输入,我们不可能把一个单词字符串作为输入,因此我们得想个办法来表示这些单词。最常用的办法就是基于训练文档来构建我们自己的词汇表(vocabulary)再对单词进行one-hot编码。

假设从我们的训练文档中抽取出10000个唯一不重复的单词组成词汇表。我们对这10000个单词进行one-hot编码,得到的每个单词都是一个10000维的向量,向量每个维度的值只有0或者1,假如单词ants在词汇表中的出现位置为第3个,那么ants的向量就是一个第三维度取值为1,其他维都为0的10000维的向量(ants=[0, 0, 1, 0, ..., 0])。

还是上面的例子,“The dog barked at the mailman”,那么我们基于这个句子,可以构建一个大小为5的词汇表(忽略大小写和标点符号):("the", "dog", "barked", "at", "mailman"),我们对这个词汇表的单词进行编号0-4。那么”dog“就可以被表示为一个5维向量[0, 1, 0, 0, 0]。

模型的输入如果为一个10000维的向量,那么输出也是一个10000维度(词汇表的大小)的向量,它包含了10000个概率,每一个概率代表着当前词是输入样本中output word的概率大小。

下图是我们神经网络的结构:

[NLP] 秒懂词向量Word2vec的本质

隐层没有使用任何**函数,但是输出层使用了sotfmax。

我们基于成对的单词来对神经网络进行训练,训练样本是 ( input word, output word ) 这样的单词对,input word和output word都是one-hot编码的向量。最终模型的输出是一个概率分布。

隐层

说完单词的编码和训练样本的选取,我们来看下我们的隐层。如果我们现在想用300个特征来表示一个单词(即每个词可以被表示为300维的向量)。那么隐层的权重矩阵应该为10000行,300列(隐层有300个结点)。

Google在最新发布的基于Google news数据集训练的模型中使用的就是300个特征的词向量。词向量的维度是一个可以调节的超参数(在Python的gensim包中封装的Word2Vec接口默认的词向量大小为100, window_size为5)。

看下面的图片,左右两张图分别从不同角度代表了输入层-隐层的权重矩阵。左图中每一列代表一个10000维的词向量和隐层单个神经元连接的权重向量。从右边的图来看,每一行实际上代表了每个单词的词向量。

[NLP] 秒懂词向量Word2vec的本质

所以我们最终的目标就是学习这个隐层的权重矩阵。

我们现在回来接着通过模型的定义来训练我们的这个模型。

上面我们提到,input word和output word都会被我们进行one-hot编码。仔细想一下,我们的输入被one-hot编码以后大多数维度上都是0(实际上仅有一个位置为1),所以这个向量相当稀疏,那么会造成什么结果呢。如果我们将一个1 x 10000的向量和10000 x 300的矩阵相乘,它会消耗相当大的计算资源,为了高效计算,它仅仅会选择矩阵中对应的向量中维度值为1的索引行(这句话很绕),看图就明白。

[NLP] 秒懂词向量Word2vec的本质

我们来看一下上图中的矩阵运算,左边分别是1 x 5和5 x 3的矩阵,结果应该是1 x 3的矩阵,按照矩阵乘法的规则,结果的第一行第一列元素为0 x 17 + 0 x 23 + 0 x 4 + 1 x 10 + 0 x 11 = 10,同理可得其余两个元素为12,19。如果10000个维度的矩阵采用这样的计算方式是十分低效的。

为了有效地进行计算,这种稀疏状态下不会进行矩阵乘法计算,可以看到矩阵的计算的结果实际上是矩阵对应的向量中值为1的索引,上面的例子中,左边向量中取值为1的对应维度为3(下标从0开始),那么计算结果就是矩阵的第3行(下标从0开始)—— [10, 12, 19],这样模型中的隐层权重矩阵便成了一个”查找表“(lookup table),进行矩阵计算时,直接去查输入向量中取值为1的维度下对应的那些权重值。隐层的输出就是每个输入单词的“嵌入词向量”。

输出层

经过神经网络隐层的计算,ants这个词会从一个1 x 10000的向量变成1 x 300的向量,再被输入到输出层。输出层是一个softmax回归分类器,它的每个结点将会输出一个0-1之间的值(概率),这些所有输出层神经元结点的概率之和为1。

下面是一个例子,训练样本为 (input word: “ants”, output word: “car”) 的计算示意图。

[NLP] 秒懂词向量Word2vec的本质

直觉上的理解

下面我们将通过直觉来进行一些思考。

如果两个不同的单词有着非常相似的“上下文”(也就是窗口单词很相似,比如“Kitty climbed the tree”和“Cat climbed the tree”),那么通过我们的模型训练,这两个单词的嵌入向量将非常相似。

那么两个单词拥有相似的“上下文”到底是什么含义呢?比如对于同义词“intelligent”和“smart”,我们觉得这两个单词应该拥有相同的“上下文”。而例如”engine“和”transmission“这样相关的词语,可能也拥有着相似的上下文。

实际上,这种方法实际上也可以帮助你进行词干化(stemming),例如,神经网络对”ant“和”ants”两个单词会习得相似的词向量。

词干化(stemming)就是去除词缀得到词根的过程。