这是全栈数据工程师养成攻略系列教程的第十八期:18 NLP 词嵌入的概念和实现。
词嵌入(Word Embedding)是一项非常重要且应用广泛的技术,可以将文本和词语转换为机器能够接受的数值向量,这里我们详细讨论其概念和实现。
语言的表示
如何向计算机解释一个词语的意思?或者说如何表示一个词语才能恰当地体现出其包含的语义?看到“苹果”这个词时,我们会联想起可以吃的苹果这一水果,还会联想起乔布斯创建的苹果公司,因此一个词可以包含多重语义。如果让计算机分析“苹果”和“梨子”两个词之间的相关性,通过字符串匹配只能得到完全不相等的结论,但是我们知道它们都属于水果,因此词语所蕴含的语义往往非常复杂,无法通过简单的字符串表示。
语言的表示主要有两种:符号主义和分布式表示。
符号主义中典型的代表是Bag of words,即词袋模型。如果将语料词典中的每个词都看作一个袋子,那么一句话无非是选择一些袋子,然后将出现的词丢入相应的袋子。用数学的语言来说,假设词典中一共有N个词,就可以用N个N维向量来表示每个词。以下是用Python描述的一个简单例子,这里的词典中只有5个词:苹果、梨子、香蕉、和、好吃,分别用一个5维向量表示,仅对应的维度上为1,其他维度都为0。基于词袋模型可以方便地用一个N维向量表示任何一句话,每个维度的值即对应的词出现的次数。
# 词典:苹果、梨子、香蕉、和、好吃
dictionary = {
"苹果": [1, 0, 0, 0, 0],
"梨子": [0, 1, 0, 0, 0],
"香蕉": [0, 0, 1, 0, 0],
"和": [0, 0, 0, 1, 0],
"好吃": [0, 0, 0, 0, 1]
}
# 苹果好吃:[1, 0, 0, 0, 1]
# 梨子和香蕉好吃:[0, 1, 1, 1, 1]
# 苹果好吃苹果好吃:[2, 0, 0, 0, 2]
词袋模型虽然简单,但其缺点也十分显著。
- 当词典中词的数量增大时,向量的维度将随之增大。虽然常用的汉字只有几千个,但是依然会给计算带来很大的不便;
- 无论是词还是句子的表示,向量都过于稀疏,除了少数维度之外的大多数维度都为0;
- 每个词所对应的向量在空间上都两两正交,任意一对向量之间的内积等数值特征都为零,无法表达词语之间的语义关联和差异;
- 句子的向量表示丢失了词序特征,即“我很不高兴”和“不我很高兴”对应的向量相同,而这显然是不符合语义的。
分布式表示中典型的代表是Word Embedding,即词嵌入,使用低维、稠密、实值的词向量来表示每一个词,从而赋予词语丰富的语义含义,并使得计算词语相关度成为可能。以最简单的情况为例,如果使用二维向量来表示词语,那么可以将每个词看作平面上的一个点,点的位置即横纵坐标由对应的二维向量确定,可以是任意且连续的。如果希望点的位置中蕴含词的语义,那么平面上位置相邻的点应当具有相关或相似的语义。用数学的语言来说,两个词具有语义相关或相似,则它们所对应的词向量之间距离相近,度量向量之间的距离可以使用经典的欧拉距离和余弦相似度等。
词嵌入可以将词典中的每个词映射成对应的词向量,一个好的词嵌入模型应当满足以下两方面要求:
- 相关:语义相关或相似的词语,它们所对应的词向量之间距离相近,例如“苹果”和“梨子”的词向量距离相近;
- 类比:具有类比关系的四个词语,例如男人对于女人,类比国王对于王后,满足
男人-女人=国王-王后
,即保持词向量之间的关联类比,其中的减号表示两个词向量之间求差。
这样一来,通过词嵌入模型得到的词向量中既包含了词本身的语义,又蕴含了词之间的关联,同时具备低维、稠密、实值等优点,可以直接输入到计算机并进行后续分析。但词典中的词如此之多,词本身的语义便十分丰富,词之间的关联则更为复杂,所以相对于词袋模型,训练一个足够好的词向量模型更加困难。
训练词嵌入模型
词嵌入模型的训练主要是基于无监督学习,从大量文本语料中学习出每个词的最佳词向量,例如维基百科、大量新闻报道等。训练的核心思想是,语义相关或相似的词语,往往具有相似的上下文,即它们经常在相似的语境中出现,例如“苹果”和“梨子”的上下文中可能都会出现类似“吃”、“水果”等词语,可以使用“开心”的语境往往也能使用“高兴”。
词嵌入模型中的典型代表是Word2Vec,模型实现原理可以参考Mikolov的两篇文章,Distributed Representations of Words and Phrases and their Compositionality,Efficient Estimation of Word Representations in Vector Space,主要包括CBOW和Skip-Gram两个模型,前者根据上下文预测对应的当前词语,后者根据当前词语预测相应的上下文。如果希望进一步深入理解词嵌入模型训练的原理和细节,可以仔细研读以上两篇文章。如果仅需要应用词嵌入模型,则直接了解如何用代码实现即可。
代码实现
gensim是一款开源的Python工具包,用于从非结构化文本中无监督地学习文本隐层的主题向量表示,支持包括TF-IDF、LSA、LDA和Word2Vec在内的多种主题模型算法,并提供了诸如相似度计算、信息检索等一系列常用任务的API接口。以下是gensim官网对于其中Word2Vec模型的介绍,http://radimrehurek.com/gensim/models/word2vec.html,里面提供了和Word2Vec相关的完整使用文档。
同样,如果没有gensim的话,使用pip安装即可。
pip install gensim
另外,gensim仅提供了Word2Vec的模型实现,训练词向量的另一个必须条件是足够大的文本语料。这里我们将要使用的是中文维基百科语料,我已经整理成文本文件并放在网盘上,直接下载即可,https://pan.baidu.com/s/1qXKIPp6,提取密码为kade
。
下载之后可以在Sublime中打开并查看其内容,文件名和后缀名可以不用在意,因为Sublime支持打开任意类型的文本文件。其中每一行是一条维基百科,即一项词条对应的百科内容,并且已经完成了分词处理。
以下代码使用gensim提供的Word2Vec模型训练并使用词向量,主要包括加载包、训练模型、保存模型、加载模型、使用模型等步骤。
# 加载包
from gensim.models import Word2Vec
from gensim.models.word2vec import LineSentence
# 训练模型
sentences = LineSentence('wiki.zh.word.text')
# size:词向量的维度
# window:上下文环境的窗口大小
# min_count:忽略出现次数低于min_count的词
model = Word2Vec(sentences, size=128, window=5, min_count=5, workers=4)
# 保存模型
model.save('word_embedding_128')
# 如果已经保存过模型,则直接加载即可
# 前面训练并保存的代码都可以省略
# model = Word2Vec.load("word_embedding_128")
# 使用模型
# 返回和一个词语最相关的多个词语以及对应的相关度
items = model.most_similar(u'中国')
for item in items:
# 词的内容,词的相关度
print item[0], item[1]
# 返回两个词语之间的相关度
model.similarity(u'男人', u'女人')
除此之外,gensim中的Word2Vec还实现了多项NLP功能,例如从多个词中找出和其他词相关性相对更弱的一个,以及根据给定的三个词类比推理出第四个词等,详细使用方法可以参考官方完整文档。
视频链接:词嵌入的概念和实现