word2vec是Google在2013年推出的一个工具。word2vec通过训练,可以将所有的词向量化,这样就可以定量的去度量词与词之间的关系,挖掘词之间的联系;同时还可以将词向量输入到各种RNN网络中进一步处理。因此,word2vec 输出的词向量可以被用来做很多自然语言处理相关的工作,比如聚类、找同义词、词性分析等等、文本分析等,是自然语言处理的重要基础。
本文希望能带你快速入门word2vec。
基本概念:语料库(corpus)、词(word)、词汇表(vocabulary)
语料库一般是指一些根据研究需要从自网站、新闻、报纸等采集的大规模文本。而“词”是语料中的最小单位。例如:
Winners do what losers do not want to do.
在处理这段非常简单的语料中,我们会把"winners"作为一个完整词处理,而不会把词分为字符单独处理。通常在自然语言处理(NLP)中,词也是最小单位,所以有“词向量”,但是没有“字符向量“。
而统计语料中出现的不重复的词构成即可构成词汇表。如上段语料中出现了"winners losers what want do not to"这7个词,其中do出现了3次。
为什么要进行词嵌入(word embedding)?
以机器翻译为例,要把"you"输入到RNN网络中,必须把单词转化为一个向量(即把词“嵌入”到高维空间)。
那么最简单粗暴的词嵌入方法就是one-hot编码:
那么one-hot编码有什么缺点呢?
- 维度灾难
一般情况下,常用英语单词约8000个,如果使用one-hot编码,每个词向量就是8000维;对应的如果有100000个词,那么每个词向量就是100000维。在实际应用中,词向量维度太大,会造成网络参数量大、网络推理速度慢、网络运行占用内存高等问题。
- 编码过于稀疏
在one-hot编码的词向量中,数值几乎全部是0,非常稀疏,很可能导致实际中网络难以收敛。
- 无法表示词间的关系
有向量
对于one-hot编码,任意两个词间的相似度都为0,这是违背实际情况的。
那么实际情况是什么?举例说明:词"cars"是"car"的复数形式,词"trucks"又是"truck"的复数形式,所以实际中我们希望他们词向量相似度很大:
由于"car"和"truck"词义接近,"car"和"china"词义差别较大,我们也希望有如下关系:
那么不禁要问:有没有一种神经网络,输入每个词的one-hot编码,就可以输出符合上述要求的词向量?有,就是word2vec!
word2vec详解
Efficient Estimation of Word Representations in Vector Spacearxiv.org word2vec Parameter Learning Explainedarxiv.orgword2vec是一个典型的3层全连接网络:INPUT->PROJECTION->OUTPUT,假设:
- INPUT层->PROJECTION层权重为
矩阵
- PROJECTION层->OUTPUT层权重为
矩阵
其中
那么:词向量 = 词的one-hot编码向量(转置) x
所以
那么如何训练网络获得
- CBOW结构:根据输入周围
个词来预测出这个词本身(即通过上下文预测词语):
- skip-gram结构:根据输入词来预测周围
个词(即预测词语的上下文):
huffman树
huffman树是一种特殊结构的二叉树,通过huffman树编码的huffman码,在通信领域有着广泛的应用。在word2vec模型中构建PROJECTION->OUTPUT的Hierarchical softmax过程中,也使用到了huffman树。
构建huffman树流程:
1.根据给定的n个权值{w1, w2, w3 ... wn},构造n棵只有根节点的二叉树,令起权值为wj
2.在森林中选取两棵根节点权值最小的树作为左右子树,构造一颗新的二叉树,置新二叉树根节点权值为其左右子树根节点权值之和。
注意,左子树的权值应小于右子树的权值。
3.从森林中删除这两棵树,同时将新得到的二叉树加入森林中。
换句话说,之前的2棵最小的根节点已经被合并成一个新的结点了。
4.重复上述两步,直到只含一棵树为止,这棵树即是“哈弗曼树”
接下来举例说明如何构造huffman树。
假设我们从某书籍中收集了一段语料,统计在语料中出现的词及每个词出现的次数,生成如下词汇表(这里只是举例,现实中的词汇表一定会非常大)。
vocabulary = {
"are" 32,
"you": 21,
"and": 19,
"very": 10,
"hi": 7,
"guys": 6,
"wise": 2,
"smart": 3,
}
然后通过每个词在语料中的出现次数建立huffman树,作为PROJECTION->OUTPUT结构。
另外在word2vec中约定:从huffman树根节点(root)开始,每次父节点向左子叶遍历编码为1,向右子叶遍历为0。如and编码为11,smart编码为01110(出现频率越高的词编码越短)。
在word2vec中使用huffman树的重要原因就是降低训练时的计算量。
一般来说,训练用的语料库都非常大。而在语料库中,有一些词出现频率很高,还有一些词出现频率很低,而且这种频率差异是非常巨大的。那么采用huffman树后,出现频率很高的常用词路径短,计算量小,从而降低了整个word2vec模型在训练时的计算开销。
CBOW(Continuous Bag of Words)结构
在之前提到过,CBOW根据输入词周围
- 从INPUT->PROJECTION层
CBOW结构首先会取中心词的
其中
- 从PROJECTION->OUTPUT层(Hierarchical Softmax)
以词"very"为例在huffman树中需要分类4次(编码为0100),第一次分类结果
其中
第二次分类为
第三次分类为
第四次分类为
所以词"very"最终Hierarchical Softmax最终概率为:
推广一下,词
其中
写成一个表达式:
- 从OUTPUT->PROJECTION->INPUT训练
在训练时,我们显然希望输入是
其中
由于优化目标是使
这里
所以计算
注意
那么
除了
即可更新
其中
对于上式一个比较通俗且不严谨的理解:
误差传给了谁,谁就会把梯度返回给传它误差的节点,即“原路送回“。在前传中通过
skip-gram结构
在之前提到过,skip-gram根据输入词词来预测出周围
从INPUT->PROJECTION计算
其中多出的
word2vec实际测试
tmikolov/word2vecgithub.comGoogle提供了word2vec的c代码。使用代码和text8语料库训练200维词向量,可以看到与词"google"余弦像速度最大的词依次是"yahoo"和"gmail"。Amazing!
关于word2vec应用,推荐参考词向量和svm对中文句子进行情感分析的代码:
BUPTLdy/Sentiment-Analysisgithub.comword2vec缺点
- OOV(Out of vocabulary)
在word2vec中,词汇表从开始训练就已经是确定的。那么在使用时,必然会有词不在词汇表中。一般使用<UNKNOW>特殊标志符解决OOV问题,但是当句子中<UNKNOW>过多时必然严重影响精度。
后续ELMO使用char cnn、Bert使用word piece,基本解决了OOV问题。
- 无法处理多义词
很多词在不同语境是有含义不同,即多义词。而word2vec中所有词的embeding向量都是训练好即固定的,无法在使用时根据上下文调整,导致处理多义词效果差。
ELMO和Bert使用训练language model,动态生成embeding向量,解决多义词问题。