Python 结巴分词 + Word2Vec利用维基百科训练词向量

结巴分词是一个跨语言的中文分词器,整体效果还算不错,功能也够用,这里直接用Python了,其他主流语言版本均有提供。

Word2Vec,起源于谷歌的一个项目,在我刚开始接触的时候就关注到了他的神奇,大致是通过深度神经网络把词映射到N维空间,处理成向量之后我们终于可以在自然语言处理上方便的使用它进行一些后续处理。

Python的gensim库中有word2vec包,我们使用这个就可以了,接下来我们就对维基百科进行处理,作为训练集去训练。(包地址:http://radimrehurek.com/gensim/models/word2vec.html

本文参考:http://www.52nlp.cn/中英文维基百科语料上的word2vec实验

处理

使用维基百科的数据很方便,一是Wiki给我们提供了现成的语料库(听说是实时更新的),虽然中文体积不大,但比起自己爬来方便了不少。

如果使用英文那就更棒了,非常适合作为语料库。

当然只是说在通用的情况下,在专业词汇上,经过测试效果比较一般(考虑到专业词库有专业wiki,以及中文词条本身也不太多)。

首先,我们把Wiki处理成Text格式待处理的文本,这一步在本文参考中有现成的代码。

process_wiki_data.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# process_wiki_data.py 用于解析XML,将XML的wiki数据转换为text格式
import logging
import os.path
import sys
from gensim.corpora import WikiCorpus
if __name__ == '__main__':
    program = os.path.basename(sys.argv[0])
    logger = logging.getLogger(program)
    logging.basicConfig(format='%(asctime)s: %(levelname)s: %(message)s')
    logging.root.setLevel(level=logging.INFO)
    logger.info("running %s" % ' '.join(sys.argv))
    # check and process input arguments
    if len(sys.argv) < 3:
        print(globals()['__doc__'] % locals())
        sys.exit(1)
    inp, outp = sys.argv[1:3]
    space = " "
    i = 0
    output = open(outp, 'w',encoding='utf-8')
    wiki = WikiCorpus(inp, lemmatize=False, dictionary={})
    for text in wiki.get_texts():
        output.write(space.join(text) + "
")
        i = i + 1
        if (i % 10000 == 0):
            logger.info("Saved " + str(i) + " articles")
    output.close()
    logger.info("Finished Saved " + str(i) + " articles")

loggerprint更规范,过去没有用过相关的,不太会用,其实用起来还是蛮方便的,这里暂时就先不介绍了。

Wiki的处理函数在gensim库中有,通过处理我们可以发现,最终效果是变成一行一篇文章并且空格分隔一些关键词,去掉了标点符号。

执行:python process_wiki.py zhwiki-latest-pages-articles.xml.bz2 wiki.zh.text,等待处理结果。

处理中文繁体转简体

Wiki中文语料中包含了很多繁体字,需要转成简体字再进行处理,这里使用到了OpenCC工具进行转换。

(1)安装OpenCC

到以下链接地址下载对应版本的OpenCC,下载的版本是opencc-1.0.1-win32。
https://bintray.com/package/files/byvoid/opencc/OpenCC
另外,资料显示还有python版本的,使用pip install opencc-python进行安装,未实践不做赘述。

(2)使用OpenCC进行繁简转换

进入解压后的opencc的目录(opencc-1.0.1-win32),双击opencc.exe文件。在当前目录打开dos窗口(Shift+鼠标右键->在此处打开命令窗口),输入如下命令行:

 opencc -i wiki.zh.txt -o wiki.zh.simp.txt -c t2s.json

则会得到文件wiki.zh.simp.txt,即转成了简体的中文。

(3)结果查看

解压后的txt有900多M,用notepad++无法打开,所以采用python自带的IO进行读取。Python代码如下:

   import codecs,sys
   f = codecs.open(‘wiki.zh.simp.txt‘,‘r‘,encoding="utf8")
   line = f.readline()
   print(line)

分词

下一步,分词,原文中用的似乎有些复杂,结巴分词的效果其实已经不错了,而且很好用,这里就用结巴分词处理一下。本身而言结巴分词是不去掉标点的,但是由于上一步帮我们去掉了,所以这里我们比较省力(不然的话原本准备遍历去掉,根据词性标注标点为x)。

我的Python还是不太6,所以写的代码比较难看OTZ,不过效果是实现了,处理起来比较慢,我觉得readlines里的参数可以更多一点。

这里下面处理完了之后用map处理,拼接list并且使用utf-8编码,此外,保证一行一个文章,空格分隔(这是后续处理函数的规定)。

这里分词没开多线程,不过后来发现瓶颈似乎在读取的IO上。

#!/usr/bin/env python
#-*- coding:utf-8 -*-
import jieba
import jieba.analyse
import jieba.posseg as pseg
def cut_words(sentence):
    #print sentence
    return " ".join(jieba.cut(sentence)).encode('utf-8')
f = open("wiki.zh.text",encoding='utf8')
target = open("wiki.zh.text.seg", 'wb')
# print 'open files'
line = f.readlines(100000)
while line:
    curr = []
    for oneline in line:
        #print(oneline)
        curr.append(oneline)
    '''
    seg_list = jieba.cut_for_search(s)
    words = pseg.cut(s)
    for word, flag in words:
        if flag != 'x':
            print(word)
    for x, w in jieba.analyse.extract_tags(s, withWeight=True):
        print('%s %s' % (x, w))
    '''
    after_cut = map(cut_words, curr)
    # print lin,
    #for words in after_cut:
        #print words
    target.writelines(after_cut)
    # print 'saved 100000 articles'
    line = f.readlines(100000)
f.close()
target.close()

训练

最后就能愉快的训练了,训练函数还是参考了原文:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# train_word2vec_model.py用于训练模型
import logging
import os.path
import sys
import multiprocessing
from gensim.corpora import WikiCorpus
from gensim.models import Word2Vec
from gensim.models.word2vec import LineSentence
if __name__ == '__main__':
    program = os.path.basename(sys.argv[0])
    logger = logging.getLogger(program)
    logging.basicConfig(format='%(asctime)s: %(levelname)s: %(message)s')
    logging.root.setLevel(level=logging.INFO)
    logger.info("running %s" % ' '.join(sys.argv))
    # check and process input arguments
    if len(sys.argv) < 4:
        print (globals()['__doc__'] % locals())
        sys.exit(1)
    inp, outp1, outp2 = sys.argv[1:4]
    model = Word2Vec(LineSentence(inp), size=400, window=5, min_count=5,
            workers=multiprocessing.cpu_count())
    # trim unneeded model memory = use(much) less RAM
    #model.init_sims(replace=True)
    model.save(outp1)
    model.save_word2vec_format(outp2, binary=False)

这里用了一个个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相关操作了:

In [1]: import gensim
In [2]: model = gensim.models.Word2Vec.load("wiki.zh.text.model")
In [3]: model.most_similar(u"足球")
Out[3]: 
[(u'u8054u8d5b', 0.6553816199302673),
 (u'u7532u7ea7', 0.6530429720878601),
 (u'u7beeu7403', 0.5967546701431274),
 (u'u4ff1u4e50u90e8', 0.5872289538383484),
 (u'u4e59u7ea7', 0.5840631723403931),
 (u'u8db3u7403u961f', 0.5560152530670166),
 (u'u4e9au8db3u8054', 0.5308005809783936),
 (u'allsvenskan', 0.5249762535095215),
 (u'u4ee3u8868u961f', 0.5214947462081909),
 (u'u7532u7ec4', 0.5177896022796631)]
In [4]: result = model.most_similar(u"足球")
In [5]: for e in result:
    print e[0], e[1]
   ....:     
联赛 0.65538161993
甲级 0.653042972088
篮球 0.596754670143
俱乐部 0.587228953838
乙级 0.58406317234
足球队 0.556015253067
亚足联 0.530800580978
allsvenskan 0.52497625351
代表队 0.521494746208
甲组 0.51778960228
In [6]: result = model.most_similar(u"男人")
In [7]: for e in result:
    print e[0], e[1]
   ....:     
女人 0.77537125349
家伙 0.617369174957
妈妈 0.567102909088
漂亮 0.560832381248
잘했어 0.540875017643
谎言 0.538448691368
爸爸 0.53660941124
傻瓜 0.535608053207
예쁘다 0.535151124001
mc刘 0.529670000076
In [8]: result = model.most_similar(u"女人")
In [9]: for e in result:
    print e[0], e[1]
   ....:     
男人 0.77537125349
我的某 0.589010596275
妈妈 0.576344847679
잘했어 0.562340974808
美丽 0.555426716805
爸爸 0.543958246708
新娘 0.543640494347
谎言 0.540272831917
妞儿 0.531066179276
老婆 0.528521537781
In [10]: result = model.most_similar(u"青蛙")
In [11]: for e in result:
    print e[0], e[1]
   ....:     
老鼠 0.559612870216
乌龟 0.489831030369
蜥蜴 0.4789905250070.46728849411
鳄鱼 0.461885392666
蟾蜍 0.448014199734
猴子 0.436584025621
白雪公主 0.434905380011
蚯蚓 0.433413207531
螃蟹 0.4314712286
In [12]: result = model.most_similar(u"姨夫")
In [13]: for e in result:
    print e[0], e[1]
   ....:     
堂伯 0.583935439587
祖父 0.574735701084
妃所生 0.569327116013
内弟 0.562012672424
早卒 0.5580426454540.553856015205
胤祯 0.553288519382
陈潜 0.550716996193
愔之 0.550510883331
叔父 0.550032019615
In [14]: result = model.most_similar(u"衣服")
In [15]: for e in result:
    print e[0], e[1]
   ....:     
鞋子 0.686688780785
穿着 0.672499775887
衣物 0.67173999548
大衣 0.667605519295
裤子 0.662670075893
内裤 0.662210345268
裙子 0.659705817699
西装 0.648508131504
洋装 0.647238850594
围裙 0.642895817757
In [16]: result = model.most_similar(u"公安局")
In [17]: for e in result:
    print e[0], e[1]
   ....:     
司法局 0.730189085007
公安厅 0.634275555611
公安 0.612798035145
房管局 0.597343325615
商业局 0.597183346748
军管会 0.59476184845
体育局 0.59283208847
财政局 0.588721752167
戒毒所 0.575558543205
新闻办 0.573395550251
In [18]: result = model.most_similar(u"铁道部")
In [19]: for e in result:
    print e[0], e[1]
   ....:     
盛光祖 0.565509021282
交通部 0.548688530922
批复 0.546967327595
刘志军 0.541010737419
立项 0.517836689949
报送 0.510296344757
计委 0.508456230164
水利部 0.503531932831
国务院 0.503227233887
经贸委 0.50156635046
In [20]: result = model.most_similar(u"清华大学")
In [21]: for e in result:
    print e[0], e[1]
   ....:     
北京大学 0.763922810555
化学系 0.724210739136
物理系 0.694550514221
数学系 0.684280991554
中山大学 0.677202701569
复旦 0.657914161682
师范大学 0.656435549259
哲学系 0.654701948166
生物系 0.654403865337
中文系 0.653147578239
In [22]: result = model.most_similar(u"卫视")
In [23]: for e in result:
    print e[0], e[1]
   ....:     
湖南 0.676812887192
中文台 0.626506924629
収蔵 0.621356606483
黄金档 0.582251906395
cctv 0.536769032478
安徽 0.536752820015
非同凡响 0.534517168999
唱响 0.533438682556
最强音 0.532605051994
金鹰 0.531676828861
原文地址:https://www.cnblogs.com/chen8023miss/p/11419541.html