新手入门:训练中文*词向量word2vec实验

时间:2021-08-04 18:56:33

说明:由于在学习自然语言处理,读了很多篇博文,就想着动手实验一下,本文主要参考了中英文*语料上的Word2Vec实验,其中在实验阶段出现了一些预期之外的错误,参考其他博文进行了微调。这篇博文更像是篇错误整理吧。后续可能还会更新更多的错误,或者大家有什么错误都可以贴上来,一起讨论。


一、环境配置

WIN10 + Anaconda3,Anaconda集成了几乎所有的依赖库,如numpy、scipy和gensim,如果你是只安装了python,那么也可以参考网上教程安装这些东西,不过可能会报错,主要是版本的问题,因此我还是推荐安装Anaconda


二、中文*word2vec测试

执行需要将xml的wiki数据转换成text数据,执行process_wiki.py脚本实现,这里注意一个问题,大多数博客中说给出的是兼容python 2.x和3.x,包括Ubuntu,其实不然,使用它们给出的脚本,

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: Pan Yang (panyangnlp@gmail.com)
# Copyrigh 2017

from __future__ import print_function

import logging
import os.path
import six
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("Using: python process_wiki.py enwiki.xxx.xml.bz2 wiki.en.text")
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():
if six.PY3:
output.write(b' '.join(text).decode('utf-8') + '\n')
# ###another method###
# output.write(
# space.join(map(lambda x:x.decode("utf-8"), text)) + '\n')
else:
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")

可能会报一个有意思的错误

(C:\Anaconda3) E:\NLP\word2vec-for-wiki-master>python process_wiki.py enwiki-latest-pages-articles.xml.bz2 wiki.en.text
Traceback (most recent call last):
File "process_wiki.py", line 30, in <module>
output.write(space.join(text).decode() + '\n')
TypeError: sequence item 0: expected str instance, bytes found

这主要是python3中str和bytes是两种不同的东西,中文*数据的获取与预处理中也提到了这一点,于是继续查询资料,在这里找到了一个和我一样的伙伴,按照他的方法,对脚本进行了修改(有兴趣的朋友可以在原脚本上修改),但还是报了错误,

UnicodeEncodeError: 'gbk' codec can't encode character '\xf6' in position 89

查找原因,按照http://www.jb51.net/article/64816.htm上所说,应该是打开文件时编码错误,于是在

output = open(outp, 'w')

指定编码格式utf-8,修改后为

output = open(outp, 'w', encoding='utf-8')

完整的process_wiki.py文件如下

# -*- coding: utf-8 -*-
__author__ = 'huang'


import os
import logging
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)


    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():
        data = space.join(text)
        output.write(str(data) + '\n')
        i = i + 1
        if i % 10000 == 0:
            logger.info('Saved ' + str(i) + ' articles')


    output.close()
    logger.info('Finished ' + str(i) + ' articles')


以上两种process_wiki.py,总有一款适合你。

然后执行

python process_wiki.py enwiki-latest-pages-articles.xml.bz2 wiki.en.text

一切正常执行,

语料库不大,所以没跑多久(用的办公电脑,渣配置,i3二代,内存8G),得到了一个大约1G的text文档wiki.zh.text,

内容格式大致为

歐幾里得 西元前三世紀的希臘數學家 現在被認為是幾何之父 此畫為拉斐爾的作品 雅典學院 
数学 是利用符号语言研究數量 结构 变化以及空间等概念的一門学科 从某种角度看屬於形式科學的一種
數學透過抽象化和邏輯推理的使用 由計數 計算 數學家們拓展這些概念
對數學基本概念的完善 早在古埃及 而在古希臘那裡有更為嚴謹的處理 從那時開始
數學的發展便持續不斷地小幅進展 世紀的文藝復興時期 致使數學的加速发展 直至今日 今日 數學使用在不同的領域中 包括科學 工程
2016-07-28 10:48:11,057: INFO: Saved 10000 articles2016-07-28 10:49:44,660: INFO: Saved 20000 articles2016-07-28 10:51:04,023: INFO: Saved 30000 articles2016-07-28 10:52:13,199: INFO: Saved 40000 articles2016-07-28 10:53:07,548: INFO: Saved 50000 articles2016-07-28 10:53:45,695: INFO: Saved 60000 articles2016-07-28 10:54:18,993: INFO: Saved 70000 articles2016-07-28 10:54:51,188: INFO: Saved 80000 articles2016-07-28 10:55:50,520: INFO: Saved 90000 articles····


这里推荐一款软件,几乎可以秒开G级文件,EmEditor,非常好用,如果有更好的软件,欢迎分享哦~

接下来我犯了一个错误,当时看的是英文*数据的教程,直接执行了

python train_word2vec_model.py wiki.en.text wiki.en.text.model wiki.en.text.vector

train_word2vec_model.py代码为

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

import logging
import os
import sys
import multiprocessing

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.wv.save_word2vec_format(outp2, binary=False)

按照常规程序,应该还需要对语料库进行繁简转换,分词,可以按照先前给的教程执行,这里我说一下我的运行结果。

大概跑了一个多小时吧,程序训练结束,生成了wiki.zh.text.model、wiki.zh.text.model.syn1neg.npy、wiki.zh.text.model.wv.syn0.npy、wiki.zh.text.vector等文件。

加载完成后就可以愉快地使用了,进入ipython后,首先调用gensim,如果出现了警告
In [1]: import gensim
C:\Anaconda3\lib\site-packages\gensim\utils.py:860: UserWarning: detected Windows; aliasing chunkize to chunkize_serial
warnings.warn("detected Windows; aliasing chunkize to chunkize_serial")


可以在import gensim之前加入两行命令

In [1]: import warnings

In [2]: warnings.filterwarnings(action='ignore',category=UserWarning,module='gensim')

In [3]: import gensim

In [4]:

由于一直看的是英文语料的教程,所以直接使用的

 model = gensim.models.Word2Vec.load_word2vec_format("wiki.en.text.vector", binary=False)

很自然的报错了,

UnpicklingError: could not find MARK

然后终于意识到自己一直使用的是英文语料的教程,执行如下命令,

model = gensim.models.Word2Vec.load("wiki.zh.text.model")

小tips:在执行ipython时,需先进入到model目录下,否则会报找不到该文件的错误。


model.most_similar(u"足球")

会提示你该命令即将被移除了,请使用 self.wv.most_similar(),于是调用

result = model.wv.most_similar(u'足球')

整个调用流程如下

In [1]: import warnings

In [2]: warnings.filterwarnings(action='ignore',category=UserWarning,module='gensim')

In [3]: import gensim

In [4]: model = gensim.models.Word2Vec.load("wiki.zh.text.model")

In [5]: result = model.wv.most_similar(u'足球')
In [6]: for e in result:
...: print(e)
...:
('籃球', 0.9209551811218262)
('排球', 0.9010016918182373)
('棒球', 0.8731895685195923)
('高爾夫球', 0.8449806571006775)
('網球', 0.843244194984436)
('橄欖球', 0.8404449820518494)
('板球', 0.8161724805831909)
('高爾夫', 0.8071038722991943)
('欖球', 0.8043543696403503)
('滑雪', 0.8014159202575684)

鉴于办公电脑配置较低(加载模型卡了十多分钟...),其他的就没有测试了,有兴趣的朋友可以多做一些测试。
值得注意的是,本人并没有按照教程上所说,进行繁简转换,以及分词,可以看到结果中,繁简体也是能够识别的,因为没有化简的结果进行对比,所以不知道是否有影响(如果词库里对于一个词既有繁体也有简体,比如“网球”、“網球”,那么对于结果肯定是有影响的)。

3、结语

撰写博客已经是在实验之后了,为了写的清楚一些,特地重现了一遍实验,导致电脑又卡死一次。
在重现的过程也遇到了新的问题,也算是对知识点的巩固吧,就想以这样的形式记录下来。
这是本人第一次写博客,格式有点乱,写的不清楚的地方还请大家指正,可以在评论区提出您的意见或建议~
PS:本人目前为在读研究生,研究方向为自然语言处理,数据挖掘,应用领域为中医,如果有志同道合的朋友可以一起研讨一下。