Ubuntu/Python 结巴分词 + Word2Vec利用维基百科训练词向量
结巴分词是一个跨语言的中文分词器,整体效果还算不错,功能也够用,这里直接用Python了,其他主流语言版本均有提供。
Word2Vec,起源于谷歌的一个项目,在我刚开始接触的时候就关注到了他的神奇,大致是通过深度神经网络把词映射到N维空间,处理成向量之后我们终于可以在自然语言处理上方便的使用它进行一些后续处理。(具体的方法忘了)
Python的gensim
库中有word2vec
包,我们使用这个就可以了,接下来我们就对维基百科进行处理,作为训练集去训练。(包地址:http://radimrehurek.com/gensim/models/word2vec.html)
本文参考:http://www.52nlp.cn/中英文维基百科语料上的word2vec实验
心酸安装史
开始确定完自己的思路其实是两周前,之所以一直没有动手,是因为不舍得在自己的Mac下跑训练,但是实验室的机子死活装不上,这里讲一下我的心酸遭遇。
首先按照教程装上pip,这个基本不成问题,在博客之前也有讲过类似的,如果需要可以搜索一下过去的文章。
依赖库是Numpy和SciPy,在Scipy的说明里有:
1sudo apt-get install python-numpy python-scipy python-matplotlib ipython ipython-notebook python-pandas python-sympy python-nose
2
安装期间如果遇到问题,可以使用:
1sudo apt-get install gcc g++
2
之所以使用apt-get
,主要是pip install
已经一把辛酸泪了还是不成功。
这样安装完成之后,继续pip install gensim
,下载不动,换源再试,依旧安装失败,似乎是在编译时出现了问题,具体查了一下也没有查出什么问题,只是有人说手动安装成功了,那么我就试试手动安装吧。
手动安装就是在pip官网上把对应的包下载下来,然后sudo python setup.py install
,结果似乎没什么问题。总算是安装上了。
打开python终端尝试import也能用,换言之我们总算可以用了。
如果你想着手解决一些报错的问题,可以参考一下在我解决之后找到的一篇文章,或许对你有帮助:http://ijiaer.com/win7-x64-install-gensim-solve-with-gcc-failed/
处理
使用维基百科的数据很方便,一是Wiki给我们提供了现成的语料库(听说是实时更新的),虽然中文体积不大,但比起自己爬来方便了不少。
如果使用英文那就更棒了,非常适合作为语料库。
当然只是说在通用的情况下,在专业词汇上,经过测试效果比较一般(考虑到专业词库有专业wiki,以及中文词条本身也不太多)。
首先,我们把Wiki处理成Text格式待处理的文本,这一步在本文参考中有现成的代码。
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4import logging
5import os.path
6import sys
7
8from gensim.corpora import WikiCorpus
9
10if __name__=='__main__':
11
12 program = os.path.basename(sys.argv[0])
13 logger = logging.getLogger(program)
14 logging.basicConfig(format='%(asctime)s: %(levelname)s: %(message)s')
15 logging.root.setLevel(logging.INFO)
16 console = logging.StreamHandler()
17 console.setLevel(logging.INFO)
18 logging.getLogger('').addHandler(console)
19
20 logger.info("running %s" % ' '.join(sys.argv))
21
22 #check and process input arguments
23 if len(sys.argv) < 3:
24 print globals()['__doc__'] % locals()
25 sys.exit(1)
26 inp, outp = sys.argv[1:3]
27 space = " "
28 i = 0
29
30 output = open(outp, 'w')
31 wiki = WikiCorpus(inp, lemmatize=False, dictionary={})
32 for text in wiki.get_texts():
33 output.write(space.join(text) + "\n")
34 i = i + 1
35 if (i % 10000 == 0):
36 logger.info("Saved " + str(i) + " articles")
37
38 output.close()
39 logger.info("Finished Saved " + str(i) + " articles")
40
logger
比print
更规范,过去没有用过相关的,不太会用,其实用起来还是蛮方便的,这里暂时就先不介绍了。
Wiki的处理函数在gensim
库中有,通过处理我们可以发现,最终效果是变成一行一篇文章并且空格分隔一些关键词,去掉了标点符号。
执行:python process_wiki.py zhwiki-latest-pages-articles.xml.bz2 wiki.zh.text
,等待处理结果,比较漫长,基本上接下来你可以随便做点什么了。
处理完成之后我们会发现,简体和繁体并不统一,所以我们需要用opencc
进行简繁体的转换,这里不得不说BYVoid是个非常牛逼的同学。这个被官方收录了,我们可以直接用sudo apt-get install opencc
来安装,github开源:https://github.com/BYVoid/OpenCC
然后执行:opencc -i wiki.zh.text -o wiki.zh.text.jian -c zht2zhs.ini
得到简体中文的版本,这一步的速度还可以。
分词
下一步,分词,原文中用的似乎有些复杂,结巴分词的效果其实已经不错了,而且很好用,这里就用结巴分词处理一下。本身而言结巴分词是不去掉标点的,但是由于上一步帮我们去掉了,所以这里我们比较省力(不然的话原本准备遍历去掉,根据词性标注标点为x
)。
我的Python还是不太6,所以写的代码比较难看OTZ,不过效果是实现了,处理起来比较慢,我觉得readlines
里的参数可以更多一点。
这里下面处理完了之后用map
处理,拼接list并且使用utf-8
编码,此外,保证一行一个文章,空格分隔(这是后续处理函数的规定)。
这里分词没开多线程,不过后来发现瓶颈似乎在读取的IO上。
1#!/usr/bin/env python
2#-*- coding:utf-8 -*-
3import jieba
4import jieba.analyse
5import jieba.posseg as pseg
6
7def cut_words(sentence):
8 #print sentence
9 return " ".join(jieba.cut(sentence)).encode('utf-8')
10
11f = open("wiki.zh.text.jian")
12target = open("wiki.zh.text.jian.seg", 'a+')
13print 'open files'
14line = f.readlines(100000)
15while line:
16 curr = []
17 for oneline in line:
18 #print(oneline)
19 curr.append(oneline)
20 '''
21 seg_list = jieba.cut_for_search(s)
22 words = pseg.cut(s)
23 for word, flag in words:
24 if flag != 'x':
25 print(word)
26 for x, w in jieba.analyse.extract_tags(s, withWeight=True):
27 print('%s %s' % (x, w))
28 '''
29 after_cut = map(cut_words, curr)
30 # print lin,
31 #for words in after_cut:
32 #print words
33 target.writelines(after_cut)
34 print 'saved 100000 articles'
35 line = f.readlines(100000)
36f.close()
37target.close()
38
训练
最后就能愉快的训练了,训练函数还是参考了原文:
1#!/usr/bin/env python
2#-*- coding:utf-8 -*-
3
4import logging
5import os.path
6import sys
7import multiprocessing
8
9from gensim.corpora import WikiCorpus
10from gensim.models import Word2Vec
11from gensim.models.word2vec import LineSentence
12
13if __name__ == '__main__':
14 program = os.path.basename(sys.argv[0])
15 logger = logging.getLogger(program)
16
17 logging.basicConfig(format='%(asctime)s: %(levelname)s: %(message)s')
18 logging.root.setLevel(level=logging.INFO)
19 logger.info("running %s" % ' '.join(sys.argv))
20
21 # check and process input arguments
22 if len(sys.argv) < 4:
23 print globals()['__doc__'] % locals()
24 sys.exit(1)
25
26 inp, outp1, outp2 = sys.argv[1:4]
27 model = Word2Vec(LineSentence(inp), size=400, window=5, min_count=5, workers=multiprocessing.cpu_count())
28
29 model.save(outp1)
30 model.save_word2vec_format(outp2, binary=False)
31
这里用了一个个LineSentence
函数,官方文档:http://radimrehurek.com/gensim/models/word2vec.html
文档这么说:
Simple format: one sentence = one line; words already preprocessed and separated by whitespace. 简单的格式:一句话 = 一行,预处理过并且用空白符分隔。
这里我们一篇文章等于一行。
执行训练:python train_word2vec_model.py wiki.zh.text.jian.seg wiki.zh.text.model wiki.zh.text.vector
,训练速度也还可以。
之后我们就可以根据这个进行Word2Vec相关操作了:
1In [1]: import gensim
2
3In [2]: model = gensim.models.Word2Vec.load("wiki.zh.text.model")
4
5In [3]: model.most_similar(u"足球")
6Out[3]:
7[(u'\u8054\u8d5b', 0.6553816199302673),
8 (u'\u7532\u7ea7', 0.6530429720878601),
9 (u'\u7bee\u7403', 0.5967546701431274),
10 (u'\u4ff1\u4e50\u90e8', 0.5872289538383484),
11 (u'\u4e59\u7ea7', 0.5840631723403931),
12 (u'\u8db3\u7403\u961f', 0.5560152530670166),
13 (u'\u4e9a\u8db3\u8054', 0.5308005809783936),
14 (u'allsvenskan', 0.5249762535095215),
15 (u'\u4ee3\u8868\u961f', 0.5214947462081909),
16 (u'\u7532\u7ec4', 0.5177896022796631)]
17
18In [4]: result = model.most_similar(u"足球")
19
20In [5]: for e in result:
21 print e[0], e[1]
22 ....:
23联赛 0.65538161993
24甲级 0.653042972088
25篮球 0.596754670143
26俱乐部 0.587228953838
27乙级 0.58406317234
28足球队 0.556015253067
29亚足联 0.530800580978
30allsvenskan 0.52497625351
31代表队 0.521494746208
32甲组 0.51778960228
33
搞完这一波,一天也就差不多过去了……至于训练效果,取决于语料库以及我们的分词效果两点,可以针对这两点进行处理。
扩展阅读:
评论 (0)