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的说明里有:

sudo apt-get install python-numpy python-scipy python-matplotlib ipython ipython-notebook python-pandas python-sympy python-nose

安装期间如果遇到问题,可以使用:

sudo apt-get install gcc g++

之所以使用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格式待处理的文本,这一步在本文参考中有现成的代码。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

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(logging.INFO)
    console = logging.StreamHandler()
    console.setLevel(logging.INFO)
    logging.getLogger('').addHandler(console)
    
    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')
    wiki = WikiCorpus(inp, lemmatize=False, dictionary={})
    for text in wiki.get_texts():
        output.write(space.join(text) + "\n")
        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,等待处理结果,比较漫长,基本上接下来你可以随便做点什么了。

处理完成之后我们会发现,简体和繁体并不统一,所以我们需要用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上。

#!/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.jian")
target = open("wiki.zh.text.jian.seg", 'a+')
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 -*-

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())

    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'\u8054\u8d5b', 0.6553816199302673),
 (u'\u7532\u7ea7', 0.6530429720878601),
 (u'\u7bee\u7403', 0.5967546701431274),
 (u'\u4ff1\u4e50\u90e8', 0.5872289538383484),
 (u'\u4e59\u7ea7', 0.5840631723403931),
 (u'\u8db3\u7403\u961f', 0.5560152530670166),
 (u'\u4e9a\u8db3\u8054', 0.5308005809783936),
 (u'allsvenskan', 0.5249762535095215),
 (u'\u4ee3\u8868\u961f', 0.5214947462081909),
 (u'\u7532\u7ec4', 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

搞完这一波,一天也就差不多过去了……至于训练效果,取决于语料库以及我们的分词效果两点,可以针对这两点进行处理。

扩展阅读:

深度学习word2vec笔记之基础篇

Deep Learning实战之word2vec

植入部分

如果您觉得文章不错,可以通过赞助支持我。

如果您不希望打赏,也可以通过关闭广告屏蔽插件的形式帮助网站运作。

标签: 源码, 知识, 代码段, 使用, 安装

添加新评论