基于 Gensim 的 Word2Vec 实践

模型创建

Gensim中 Word2Vec 模型的期望输入是进过分词的句子列表,即是某个二维数组。这里我们暂时使用 Python 内置的数组,不过其在输入数据集较大的情况下会占用大量的 RAM。Gensim 本身只是要求能够迭代的有序句子列表,因此在工程实践中我们可以使用自定义的生成器,只在内存中保存单条语句。

# 引入 word2vecfrom gensim.models import word2vec# 引入日志配置import logginglogging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)# 引入数据集raw_sentences = ["the quick brown fox jumps over the lazy dogs","yoyoyo you go home now to sleep"]# 切分词汇sentences= [s.encode('utf-8').split() for s in sentences]# 构建模型model = word2vec.Word2Vec(sentences, min_count=1)# 进行相关性比较model.similarity('dogs','you')

这里我们调用Word2Vec创建模型实际上会对数据执行两次迭代操作,第一轮操作会统计词频来构建内部的词典数结构,第二轮操作会进行神经网络训练,而这两个步骤是可以分步进行的,这样对于某些不可重复的流(譬如 Kafka 等流式数据中)可以手动控制:

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

Word2Vec 参数

min_count

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

在不同大小的语料集中,我们对于基准词频的需求也是不一样的。譬如在较大的语料集中,我们希望忽略那些只出现过一两次的单词,这里我们就可以通过设置min_count参数进行控制。一般而言,合理的参数值会设置在0~100之间。

size

size参数主要是用来设置神经网络的层数,Word2Vec 中的默认值是设置为100层。更大的层次设置意味着更多的输入数据,不过也能提升整体的准确度,合理的设置范围为 10~数百。

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

workers

workers参数用于设置并发训练时候的线程数,不过仅当Cython安装的情况下才会起作用:

model = Word2Vec(sentences, workers=4) # default = 1 worker = no parallelization

外部语料集

在真实的训练场景中我们往往会使用较大的语料集进行训练,譬如这里以 Word2Vec 官方的text8为例,只要改变模型中的语料集开源即可:

sentences = word2vec.Text8Corpus('text8')model = word2vec.Word2Vec(sentences, size=200)

这里语料集中的语句是经过分词的,因此可以直接使用。笔者在第一次使用该类时报错了,因此把 Gensim 中的源代码贴一下,也方便以后自定义处理其他语料集:

class Text8Corpus(object):    """Iterate over sentences from the "text8" corpus, unzipped from http://mattmahoney.net/dc/text8.zip ."""    def __init__(self, fname, max_sentence_length=MAX_WORDS_IN_BATCH):        self.fname = fname        self.max_sentence_length = max_sentence_length    def __iter__(self):        # the entire corpus is one gigantic line -- there are no sentence marks at all        # so just split the sequence of tokens arbitrarily: 1 sentence = 1000 tokens        sentence, rest = [], b''        with utils.smart_open(self.fname) as fin:            while True:                text = rest + fin.read(8192)  # avoid loading the entire file (=1 line) into RAM                if text == rest:  # EOF                    words = utils.to_unicode(text).split()                    sentence.extend(words)  # return the last chunk of words, too (may be shorter/longer)                    if sentence:                        yield sentence                    break                last_token = text.rfind(b' ')  # last token may have been split in two... keep for next iteration                words, rest = (utils.to_unicode(text[:last_token]).split(),                              text[last_token:].strip()) if last_token >= 0 else ([], text)                sentence.extend(words)                while len(sentence) >= self.max_sentence_length:                    yield sentence[:self.max_sentence_length]                    sentence = sentence[self.max_sentence_length:]

我们在上文中也提及,如果是对于大量的输入语料集或者需要整合磁盘上多个文件夹下的数据,我们可以以迭代器的方式而不是一次性将全部内容读取到内存中来节省 RAM 空间:

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') # a memory-friendly iteratormodel = gensim.models.Word2Vec(sentences)

模型保存与读取

model.save('text8.model')2015-02-24 11:19:26,059 : INFO : saving Word2Vec object under text8.model, separately None2015-02-24 11:19:26,060 : INFO : not storing attribute syn0norm2015-02-24 11:19:26,060 : INFO : storing numpy array 'syn0' to text8.model.syn0.npy2015-02-24 11:19:26,742 : INFO : storing numpy array 'syn1' to text8.model.syn1.npymodel1 = Word2Vec.load('text8.model') model.save_word2vec_format('text.model.bin', binary=True)2015-02-24 11:19:52,341 : INFO : storing 71290x200 projection weights into text.model.bin model1 = word2vec.Word2Vec.load_word2vec_format('text.model.bin', binary=True)2015-02-24 11:22:08,185 : INFO : loading projection weights from text.model.bin2015-02-24 11:22:10,322 : INFO : loaded (71290, 200) matrix from text.model.bin2015-02-24 11:22:10,322 : INFO : precomputing L2-norms of word weight vectors

模型预测

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.73723527model.most_similar(['man'])[(u'woman', 0.5686948895454407), (u'girl', 0.4957364797592163), (u'young', 0.4457539916038513), (u'luckiest', 0.4420626759529114), (u'serpent', 0.42716869711875916), (u'girls', 0.42680859565734863), (u'smokes', 0.4265017509460449), (u'creature', 0.4227582812309265), (u'robot', 0.417464017868042), (u'mortal', 0.41728296875953674)]

如果我们希望直接获取某个单词的向量表示,直接以下标方式访问即可:

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

模型评估

Word2Vec 的训练属于无监督模型,并没有太多的类似于监督学习里面的客观评判方式,更多的依赖于端应用。Google 之前公开了20000条左右的语法与语义化训练样本,每一条遵循A is to B as C is to D这个格式,地址在这里:

model.accuracy('/tmp/questions-words.txt')2014-02-01 22:14:28,387 : INFO : family: 88.9% (304/342)2014-02-01 22:29:24,006 : INFO : gram1-adjective-to-adverb: 32.4% (263/812)2014-02-01 22:36:26,528 : INFO : gram2-opposite: 50.3% (191/380)2014-02-01 23:00:52,406 : INFO : gram3-comparative: 91.7% (1222/1332)2014-02-01 23:13:48,243 : INFO : gram4-superlative: 87.9% (617/702)2014-02-01 23:29:52,268 : INFO : gram5-present-participle: 79.4% (691/870)2014-02-01 23:57:04,965 : INFO : gram7-past-tense: 67.1% (995/1482)2014-02-02 00:15:18,525 : INFO : gram8-plural: 89.6% (889/992)2014-02-02 00:28:18,140 : INFO : gram9-plural-verbs: 68.7% (482/702)2014-02-02 00:28:18,140 : INFO : total: 74.3% (5654/7614)

还是需要强调下,训练集上表现的好也不意味着 Word2Vec 在真实应用中就会表现的很好,还是需要因地制宜。)

Word2Vec Tutorial

Getting Started with Word2Vec and GloVe in Python

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容