上一篇文章详细学习了Word2vec的含义及实现原理,包含两大语言模型:Cbow、Skip-Gram,以及两大快速训练技巧:Negative sampling、哈夫曼树形式查找Target word。这一篇主要讲基于中文维基百科语料库的Word2vec训练,采用了开源的python gensim包
安装gensim包
Google最初开源的Word2vec是用C++写的,后来陆续出现了很多版本,我们采用的是gensim ,gensim已经被集成在了conda中,所以我们可以方便地利用miniconda进行安装。1
2source activate py2k # 激活python2环境,当然python3也可以的,只不过笔者此处用的python2版本
conda install gensim
获取维基百科语料库
下载
2017版百科语料下载地址为:https://dumps.wikimedia.org/zhwiki/latest/zhwiki-latest-pages-articles.xml.bz2。 下载后是一个压缩包,里面的内容是从网上爬取的“标题–摘要–正文”对,以及其他的html、json标志符等。
语料抽取
Wikipedia Extractor是一个开源的语料抽取工具,语料库压缩包不用在本地解压,直接用Wikipedia Extractor脚本执行:1
2
3
4$ git clone https://github.com/attardi/wikiextractor.git wikiextractor
$ cd wikiextractor
$ sudo python setup.py install
$ ./WikiExtractor.py -b 1024M -o extracted zhwiki-latest-pages-articles.xml.bz2 #
其中2000M
代表抽取后的预料库最大允许size,因为原始语料库size约1.3G,这里用2000M的话相当于把语料全部抽取到一个文本文件“wiki_00”中。extracted
代表抽取后语料存放路径。
最终控制台log输出:INFO: Finished 7-process extraction of 986891 articles in 1393.9s (708.0 art/s),代表全部抽取完成共986891篇文章。
抽取后的内容格式为:每篇文章被一对<doc>
、</doc>
包起来,而<doc>
中的包含的属性有文章id、url和title属性,如<doc id="xxx" url="https://zh.wikipedia.org/xxx" title="xxx">
。
繁体字转简体
上一步抽取的中文维基百科语料库比较杂乱,既有繁体字也有简体字,这里需要将其统一变为简体字,采用开源的 OpenCC转换器。ubuntu下使用方法如下:1
2$ sudo apt-get install opencc
$ opencc -i wiki_00 -o zh_wiki_00 -c zht2zhs.ini
去除标点
去除标点是因为:训练标点符号意义不大,且标点符号出现次数过多,影响一定训练效率。可以通过正则表达式去除。感谢吴良超的学习笔记提供的去标点程序,使用格式为:1
python script.py input_file output_file
pre1.py源码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
import re
import io
reload(sys)
sys.setdefaultencoding('utf-8')
def pre_process(input_file, output_file):
multi_version = re.compile(ur'-\{.*?(zh-hans|zh-cn):([^;]*?)(;.*?)?\}-')
punctuation = re.compile(u"[-~!@#$%^&*()_+`=\[\]\\\{\}\"|;':,./<>?·!@#¥%……&*()——+【】、;‘:“”,。、《》?「『」』]")
with io.open(output_file, mode = 'w', encoding = 'utf-8') as outfile:
with io.open(input_file, mode = 'r', encoding ='utf-8') as infile:
for line in infile:
line = multi_version.sub(ur'\2', line)
line = punctuation.sub('', line.decode('utf8'))
outfile.write(line)
if __name__ == '__main__':
if len(sys.argv) != 3:
print "Usage: python pre1.py input_file output_file"
sys.exit()
input_file, output_file = sys.argv[1], sys.argv[2]
pre_process(input_file, output_file)
去除标识符及分词
经过上一步处理,每一篇文章被存放在了doc
/doc标签。同时肉眼对比抽取后的句子,每一段落的开头跟标题都是重复的,也需要去除。还有前辈们建议把一篇段落的文章放到一行,但词向量关注的
window size`一般大小为3、5,感觉也没这个必要。安装jieba分词,pre2.py代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
import io
import jieba
reload(sys)
sys.setdefaultencoding('utf-8')
jieba.load_userdict("dict.txt") # 如果有的话加载用户自定义词典
def cut_words(input_file, output_file):
count = 0
a = 1 # 标识符
with io.open(output_file, mode = 'w', encoding = 'utf-8') as outfile:
with io.open(input_file, mode = 'r', encoding = 'utf-8') as infile:
for line in infile:
if a == -1:
a = a*(-1)
continue
line = line.strip()
if len(line) < 1:
continue
# 移除字符串首尾的\r \n 空格等字符,此处希望一篇文章的所有内容位于一行,使得训练预料更规整
if line.startswith('doc'):
if line == 'doc':
outfile.write(u'\n')
count += 1
str = "已完成 %d 篇百科文章转换" % (count)
print str
else:
a = -1
continue
else:
for word in jieba.cut(line):
outfile.write(word + ' ')
if __name__ == '__main__':
if len(sys.argv) < 3:
print "Terminal Format: python script.py input_file output_file"
sys.exit()
input_file, output_file = sys.argv[1], sys.argv[2]
cut_words(input_file, output_file)
最终log输出:已完成 986891 篇百科文章转换
,代表全部文章转换完毕。
文本去重
NLP常规操作,这么大的数据量,采用利用哈希表检测重复并去除,pre3.py代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19#!/usr/bin/python
# -*- coding: utf-8 -*-
#程序功能是为了完成判断文件中是否有重复句子
#并将重复句子打印出来
import shutil
res_list = set()
f = open('pre2','r')
w = open('pre3', 'w')
k = 1
index = 0
for line in f.readlines():
index = index + 1
if line in res_list:
k = k + 1
print("Repeat", k, "All:", index, "Percent: %.5f%%" % (100*k/index))
else:
res_list.add(line)
w.write(line)
最终log输出:Repeat', 18649, 'All:', 986884, 'Percent: 1.00000%'
,重复了1%的语料。
用gensim训练词向量
初始训练
解释下参数含义:multiprocessing
:python中的多线程模块,这里这里采用的是8核CPU进行训练,网络比较简单的情况下速度要比GPU快。LineSentence
:将输入文件转为 gensim 内部的 LineSentence 对象,要求输入的文件的格式为每行一篇文章,对于中文每篇文章经过分词处理。min_count
:低于min_count的词频的词不参与训练;sg
:0表示CBOW语言模型,1表示Skip-gram模型;size
:词向量维度iter
:迭代的epoch次数,这里着重讲下#此处的iter值会被直接设置到百分比进度条中,而不是日常训练神经网络一样epoch1、epoch2…epoch100
train.py代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#!/usr/bin/python
# -*- coding: utf-8 -*-
import os, sys
import multiprocessing
import gensim
import logging
reload(sys)
sys.setdefaultencoding('utf-8')
def word2vec_train(input_file, output_file):
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
sentences = gensim.models.word2vec.LineSentence(input_file)
model = gensim.models.Word2Vec(sentences, size=800, window=5, min_count=3, iter=100,sg=0, workers=multiprocessing.cpu_count())
# model.train(sentences, total_examples=model.corpus_count, epochs = 100) 默认参数,也可以指定
model.save(output_file)
if __name__ == '__main__':
if len(sys.argv) < 3:
print "Terminal Format: python script.py infile outfile"
sys.exit()
input_file, output_file = sys.argv[1], sys.argv[2]
word2vec_train(input_file, output_file)
最终log输出:2018-01-22 19:37:52,179 : INFO : PROGRESS: at 100.00% examples, 122591 words/s, in_qsize 16, out_qsize 0
代表完成训练。
- 实验环境:ubuntu16.04、 CPU:8核
- 迭代5次epoch,耗时1.5小时
- 相同参数下,迭代100次epcoh,耗时40小时
增量训练
增量训练可以在新的语料库上,基于原有模型进行叠加训练,而不用从头开始。
train_add.py关键部分代码如下:1
2
3
4
5
6def word2vec_train(input_file, output_file):
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
model = gensim.models.Word2Vec.load('word2vec')
more_sentences = gensim.models.word2vec.LineSentence(input_file)
model.build_vocab(more_sentences, update=True)
model.train(more_sentences, total_examples=model.corpus_count, epochs=model.iter)
上面的代码先加载了一个已经训练好的词向量模型,然后再添加新的文章进行训练,同样新增的文章的格式也要满足每行一篇文章,每篇文章的词语通过空格分开的格式。由于前段时间参加Ai Challenge比赛,正好有1000W行处理后的中文数据可以加入训练。
使用词向量模型
直接上代码了,infer.py如下:1
2
3
4
5
6
7
8
9
10#encoding=utf-8
import gensim
from gensim.models import word2vec
model=gensim.models.Word2Vec.load('word2vec')
print "模型加载成功!"
print "----词向量维度------"
print len(model[u'男人'])
print "----跟“滋润”相近的词------"
for i in model.most_similar(u"滋润"):
print i[0],i[1]
Refer:
word2vec实战:获取和预处理中文维基百科(Wikipedia)语料库,并训练成word2vec模型
中文维基百科语料库词向量的训练
不可思议的Word2Vec