Word2Vec 食用指南-应用篇(一)

Table of Contents

准备数据

使用 gensim 生成 word2vec 需要输入为一个可迭代的序列。每个句子为词组成的 list(utf-8 编码)。

# import modules & set up logging
import gensim, logging
logging.basicConfig(format='%(asctime)s: %(levelname)s : %(message)s', level=logging.INFO)
sentences = [['first', 'sentence'], ['second', 'sentence']]
model = gensim.models.Word2Vec(sentences, min_count=1)

使用 Python 原生的 list 非常的方便,但当输入非常大的时候内存就可能不够用了,所以一个好的方法是使用生成器(区别迭代器和生成器)来做。

例如,如果我们的输入是磁盘的多个文件(文件每一行,为一个句子),我们可以一行行的处理,而不是把所有的都加载到内存中。

class MySentences(object):
    def __init__(self, dirname):
        self.dirname = dirname
    def __iter__(self):
        for fname in os.listdir(self.dirname):
            for line in open(os.path.join(self.dirname, fname)):
                yield line.split()
sentences = MySentences('/some/directory')
model = gensim.models.Word2Vec(sentences)

如果我们想更进一步的对文本进行编码、大小写、移除数字或者提取实体等等都可以在 MySentences 里面来做,这些都是对 word2vec 透明的操作。

对于高级用户来说需要了解,Word2Vec(sentences, iter=1) 会执行两轮迭代 (更加准确的说法是 iter+1 轮,默认 iter=5)。第一轮,会获取词频,然后构建内部的树字典结构。第二以及随后的迭代会训练模型。在有些情况,数据可能不能重复迭代,你可以手动的初始化第一轮的词典:

model = gensim.models.Word2Vec(iter=1)  # an empty model, no training yet
model.build_vocab(some_sentences)  # can be a non-repeatable, 1-pass generator
model.train(other_sentences)  # can be a non-repeatable, 1-pass generator

为了能够深入的理解 iterators, iterables, generates,我们可以看这个教程:Data Streaming in Python

训练

Word2Vec 的训练参数对训练的速度和结果的质量有非常大的影响。

其中之一是对内部词典的过滤,也就是在成千上百万的词中,仅仅出现一次二次的,很有可能是拼写错误或者没意义的词,对于它们训练没有太大的意义,最好的方式就是忽略掉这种词。

model = Word2Vec(sentences, min_count=10) # default value is 5

对于 min_count 一个合理的大小是 0-100,取决于语料的大小。

另外一个重要的参数是训练的层数,它和训练的自由度相关

model = Word2Vec(sentences, size=100) # default value is 100

更大的值同样需要跟多的语料,同时也会得到一个更加准确,表现更好的模型。合理的大小是 几十到几百之间。

最后一个非常重要的参数会影响训练的速度,也就是 worker 的个数

model = Word2Vec(sentences, worker=100) # default value is 1, worker no parallelization

如果你没有安装 Cython, 因为 GIL 的原因,这个参数设置不会生效,只能使用单核。

内存

说起内存占用,word2vec 模型参数是以 numpy arrays 来存储的。假设单词数 n=100000, size=200(size 参数),存储的类型为无符号浮点型(float)(假设占用 4B)。

内存中存储 3 个这样的矩阵参数,所以最终的内存占用为:

\[3 * 100000 * 200 * 4B = \sim 229M\]

另外,大部分情况仅仅需要少量的空间来存储词典树(100000 个词大约需要几 M),除非你的词都是非常非常长的字符串。

测评

Word2vec 训练是非监督学习,没有非常客观的方法来评价这个结果。 Google 放出了大约 20000 个的测试集,测试“A 相对于 B 类似于 C 相对于 D”的任务。 questions-words

Gensim 支持同样的操作,

model.accuracy('questions-words.txt')

再次强调一下,在测试集上好的表现未必在你的应用上工作的很好。

存储和加载模型

你可以使用标准的方法来存储和加载 w2v 模型:

model.save('/tmp/mymodel')
new_model = gensim.models.Word2Vec.load('/tmp/mymodel')

这个内部实现其实是使用了 pickle。

另外,你可以加载通过 C 语言工具生成的模型

model = Word2Vec.load_word2vec_format('/tmp/vectors.txt', binary=False)
# using gzipped/bz2 input works too, no need to unzip:
model = Word2Vec.load_word2vec_format('/tmp/vectors.bin.gz', binary=True)

或者可以这样:

model = word2vec.Word2Vec(sentences, min_count=1)
model.wv.save_word2vec_format('mymodel.bin', binary=True)

new_model = KeyedVectors.load_word2vec_format(
    'mymodel.bin', binary=True)

在线训练/持续训练?(Online training/Resuming training)

高级用户可以加载模型,然后通过更多语句来进一步的训练:

model = gensim.models.Word2Vec.load('/tmp/mymodel')
model.train(more_sentences)

你或许需要微调一下 train() 的 total_words 参数。

需要注意的是,如果模型是通过二进制文件存储的,则无法进行持续训练,因为训练相关的重要信息丢失了。

如何使用模型

Word2vec 支持多种相似度的使用方式:

model.most_similar(positive=['woman', 'king'], negative=['man'], topn=1)
[('queen', 0.50882536)]
model.doesnt_match("breakfast cereal dinner lunch";.split())
'cereal'
model.similarity('woman', 'man')
0.73723527

如果想获取原生的向量输出,可以直接这样:

model['computer']  # raw NumPy vector of a word
array([-0.00449447, -0.00310097,  0.02421786, ...], dtype=float32)

Bonus App

TODO <github>

内容来源