词向量是一种把词处理成向量的技术,并且保证向量间的相对相似度和语义相似度是相关的。这个技术是在无监督学习方面最成功的应用之一。传统上,自然语言处理(NLP)系统把词编码成字符串。这种方式是随意确定的,且对于获取词之间可能存在的关系并没有提供有用的信息。词向量是NLP领域的一个替代方案。它把词或短语映射成实数向量,把特征从词汇表大小的高维度空间降低到一个相对低的维度空间。
例如,让我们看看四个词:“woman”(女人)、“man”(男人)、“queen”(女王)和“king”(国王)。我们把它们都向量化,再使用简单的代数运算来发现它们之间的语义相似度。计算向量间的相似度可以采用诸如余弦相似度的方法。当我们把词“woman”的向量减去词“man”后,这个差值的余弦相似度应该和词“queen”的向量减去“king”的向量的差值比较接近(参见图1)。
W(“woman”)−W(“man”) ≃ W(“queen”)−W(“king”)
图1 性别的向量。来源:Lior Shkiller
有很多不同的模型可以被用来把词转换成实数性的向量,包括隐含语义分析(LSA)和隐含狄利克雷分布(LDA)。这些模型背后的思路是:相关的词汇一般都会在相同的文档里同时出现。例如,backpack(背包)、school(学校)、notebook(笔记本)和teacher(教师)一般都会一起出现。而school(学校)、tiger(老虎)、apple(苹果)和basketball(篮球)一般都不会持续同时出现。基于这个相关的词会在相关的文档里出现的基本假设,为了把词转化为向量,LSA会构建一个矩阵。矩阵的行是(语料库或数据里)所有出现过的词,而列则是对应于文档里的一个段落。LSA使用奇异值分解(SVD)的方法,在保存列之间相似性的同时降低矩阵的行数。不过这些模型的主要问题是:在数据量非常大的时候,计算量也非常得大。
为了避免计算和存储大量的数据,我们试图创造一个神经网络模型来计算词间的关系,并提高效率。
Word2Vec
目前最流行的词向量模型是由Mikolov等人在2013年提出的word2vec。这个模型的效果很好,且计算效率有了很大的提升。Mikolov等提出的负采样方法是一个更有效的产生词向量的方法。更多的信息可以在这里找到。
这一模型可以使用下述两种架构的任一种来生成词的分布:连续词袋(CBOW)和连续跳跃元语法(skip-gram)。
下面让我们分别来看看这两种架构。
CBOW模型
在CBOW架构里,模型根据目标词的上下文来预测目标词。因此,Mikolov等使用了目标词w的前n个词和后n个词。
一个序列的词等同于一个物品集。因此,就可以把“词”理解为“物品”。对于“物品”我们可以使用推荐系统以及协同过滤里的方法。CBOW模型的训练速度是跳跃元语法模型的七倍,而且预测准确性也稍好(参见图2)。
图2 基于上下文来预测词。来源:Lior Shkiller
连续跳跃元语法模型
与使用目标词的上下文的方法不同,连续跳跃元语法模型是使用目标词去预测它的前后词(参见图3)。据Mikolov等的论文,在训练数据量比较小的时候,跳跃元语法模型比较好,且对于罕见的词和短语的处理较好。
图3 用给定的词来预测上下文。来源:Lior Shkiller
代码
(你可以在这个GitHub库里找到下面例子的代码)
这个模型(word2vec)的一大好处就是,它可以用于很多种语言。
我们所要做的就是下载一个所要处理的语言的大数据集。
从维基百科上找一个大数据集
我们可以从维基百科里面找到很多语言的数据。用下面的步骤就可以获得一个大数据集。
- 找到你想处理的语言的ISO 639代码:ISO 639代码的列表
- 登录https://dumps.wikimedia.org/wiki/latest/ (译者注:此链接已失效)
- 下载wiki-latest-pages-articles.xml.bz2
接着,为了让后续的事情变简单,我们会安装gensim。它是一个实现了word2vec的Python库。
pip install –upgrade gensim
我们需要用维基百科的下载文件来创建语料库,以用于后续的word2vec模型的训练。下面这段代码的输出就是一个“wiki..text”的文件。其中包括了维基百科的所有文章的所有词汇,并按照语言分开。
from gensim.corpora import WikiCorpus
language_code = “he”
inp = language_code+”wiki-latest-pages-articles.xml.bz2″
outp = “wiki.{}.text”.format(language_code)
i = 0
print(“Starting to create wiki corpus”)
output = open(outp, ‘w’)
space = ” ”
wiki = WikiCorpus(inp, lemmatize=False, dictionary={})
for text in wiki.get_texts():
article = space.join([t.decode(“utf-8”) for t in text])
output.write(article + “\n”)
i = i + 1
if (i % 1000 == 0):
print(“Saved ” + str(i) + ” articles”)
output.close()
print(“Finished – Saved ” + str(i) + ” articles”)
训练模型
参数的说明如下:
- size:向量的维度
- 大的size值会要求更多的训练数据,但能带来更准确的模型
- window:在一个句子内,目标词与预测词之间的最大距离
- min_count:忽略所有总词频低于这个值的词。
import multiprocessing
from gensim.models import Word2Vec
from gensim.models.word2vec import LineSentence
language_code = “he”
inp = “wiki.{}.text”.format(language_code)
out_model = “wiki.{}.word2vec.model”.format(language_code)
size = 100
window = 5
min_count = 5
start = time.time()
model = Word2Vec(LineSentence(inp), sg = 0, # 0=CBOW , 1= SkipGram
size=size, window=window, min_count=min_count, workers=multiprocessing.cpu_count())
# trim unneeded model memory = use (much) less RAM
model.init_sims(replace=True)
print(time.time()-start)
model.save(out_model)
整个word2vec训练过程用了18分钟。
fastText库
脸书的人工智能研究(FAIR)实验室最近发布了fastText库。它是基于Bojanowski等的论文《Enriching Word Vectors with Subword Information》所开发的模型。与word2vec不同,fastText把词表示成一个n元的字母袋。每个向量代表字符袋里的一个n元字母,而一个词则是这些向量的和。
使用脸书的新库很简单。安装命令:
pip install fasttext
训练模型的命令:
start = time.time()
language_code = “he”
inp = “wiki.{}.text”.format(language_code)
output = “wiki.{}.fasttext.model”.format(language_code)
model = fasttext.cbow(inp,output)
print(time.time()-start)
整个fastText模型训练用了13分钟。
评估向量:类比性
下面让我们用之前的那个例子来评估这两个模型的准确度。
W(“woman”) ≃ W(“man”)+ W(“queen”)− W(“king”)
下面的代码首先计算正负词的加权平均值。
随后,代码计算了所有的测试词汇的向量与加权平均的点乘积。
我们的评估例子里,测试词汇是整个词汇表。代码的最后是打印出和正词与负词的加权平均值的余弦相似度最高的词。
import numpy as np
from gensim.matutils import unitvec
def test(model,positive,negative,test_words):
mean = []
for pos_word in positive:
mean.append(1.0 * np.array(model[pos_word]))
for neg_word in negative:
mean.append(-1.0 * np.array(model[neg_word]))
# compute the weighted average of all words
mean = unitvec(np.array(mean).mean(axis=0))
scores = {}
for word in test_words:
if word not in positive + negative:
test_word = unitvec(np.array(model[word]))
# Cosine Similarity
scores[word] = np.dot(test_word, mean)
print(sorted(scores, key=scores.get, reverse=True)[:1])
接着,用我们最初的那个例子来做测试。
用fastText和gensim的word2vec模型来预测:
positive_words = [“queen”,”man”]
negative_words = [“king”]
# Test Word2vec
print(“Testing Word2vec”)
model = word2vec.getModel()
test(model,positive_words,negative_words,model.vocab)
# Test Fasttext
print(“Testing Fasttext”)
model = fasttxt.getModel()
test(model,positive_words,negative_words,model.words)
结果
Testing Word2vec
[‘woman’]
Testing Fasttext
[‘woman’]
结果显示fastText和gensim的word2vec都能正确预测。
W(“woman”) ≃ W(“man”)+ W(“queen”)− W(“king”)
可见,词向量确实能找到词汇之间的语义关系。
我们这里所介绍的模型的基本思路可以被运用到很多的应用场景。如预测商业机构需要的下一个应用、做情感分析、替换生物序列、做语义图片搜索等。