一、文本处理流程
通常我们文本处理流程如下:
- 1 对文本数据进行预处理:数据预处理,包括简繁体转换,去除xml符号,将单词条内容处理成单行数据,word2vec训练原理是基于词共现来训练词之间的语义联系的。不同词条内容需分开训练
- 2 中文分词:中文NLP很重要的一步就是分词了,分词的好坏很大程度影响到后续的模型训练效果
- 3 特征处理:也叫词向量编码,将文本数据转换成计算机能识别的数据,便于计算,通常是转换成数值型数据,常用的编码方式有one hot编码(BOW词袋模型离散表示方式,另外文章我们讲解TF-IDF模型时候会介绍)和基于word2vec等深度学习模型训练得到的低维稠密向量,通常称为word embedding的Distributed representation
- 4 机器学习:词向量进行编码之后,便可以将文本数据转换成数值数据,输入到我们的机器学习模型进行计算训练了
文本处理流程图如下: -
文本处理流程
二、训练过程
- 模型:gensim工具包word2vec模型,安装使用简单,训练速度快
- 语料:百度百科500万词条+维基百科30万词条+1.1万条领域数据
- 分词:jieba分词,自定义词典加入行业词,去除停用词
- 硬件:8核16g虚拟机
数据预处理
- 维基百科数据量不够大,百度百科数据量较全面,内容上面百度百科大陆相关的信息比较全面,港澳台和国外相关信息维基百科的内容比较详细,因此训练时将两个语料一起投入训练,形成互补,另外还加入了1.1万公司行业数据
分词
- 1 准备一个停用词词典,训练时要去除停用词的干扰
- 2 分词工具有中科院分词,哈工大的LTP分词,jieba分词,分词效果中科院的分词效果不错,我们直接使用jieba进行分词,使用简单方便,分词速度快
- 3 自定义词典:由于百科数据有很多专属名词,很多比较长,如果直接分词,很大情况下会被切开,这不是我们想要的结果,比如:中国人民解放军,可能会被分成:中国 人民 解放军,jieba虽然有新词发现功能,为保证分词准确度,jieba的作者建议我们还是使用自定义词典。
- 4 自定义词典抽取:我从百度百科抽取了200万的词条,由于自定义词典包含英文单词时会导致jieba对英文单词进行分词,所以需要用正则表达式去除词条中的英文数据,并且去除一些单字词,还有一些词条里面较短词,如"在北京",这类词会导致分词出现问题,也需要使用正则去除,也有简单粗暴的方法,直接保留3个汉字及以上的中文词条,去除之后得到170万大小的自定义词典
- 5 分词过程
# 多线程分词
# jieba.enable_parallel()
#加载自定义词典
jieba.load_userdict("F:/baike_spider/dict/baike_word_chinese")
#加载停用词
def getStopwords():
stopwords = []
with open("stop_words.txt", "r", encoding='utf8') as f:
lines = f.readlines()
for line in lines:
stopwords.append(line.strip())
return stopwords
#分词
def segment():
file_nums = 0
count = 0
url = base_url + 'processed_data/demo/'
fileNames = os.listdir(url)
for file in fileNames:
logging.info('starting ' + str(file_nums) + 'file word Segmentation')
segment_file = open(url + file + '_segment', 'a', encoding='utf8')
with open(url + file, encoding='utf8') as f:
text = f.readlines()
for sentence in text:
sentence = list(jieba.cut(sentence))
sentence_segment = []
for word in sentence:
if word not in stopwords:
sentence_segment.append(word)
segment_file.write(" ".join(sentence_segment))
del text
f.close()
segment_file.close()
logging.info('finished ' + str(file_nums) + 'file word Segmentation')
file_nums += 1
- 由于python多线程只能单核多线程,如果是多核的机器并不能有效使用cpu,jieba是使用python写的,所以jieba只支持并行分词,并行分词指的是多进程分词,并且不支持windows
- 我们在linux试过jieba自带的并行分词,开启并行分词之后,jieba后台会自动开启多个进程,并且并行分词需要一次性将训练语料读取到内存并传入jieba.cut(file.read())中才会有效果,如果类似我代码中逐行传入,开启多进程是不起作用的,jieba多进程原理是,jieba后台会自动将语料切分分配给指定进程处理,分好词后再合并
- 我使用的是8核16g内存Linux虚拟机,发现开启jieba并行分词,1g的语料数据,很快就爆内存了
- 单进程的jieba分词,不需要一次性加载所有语料数据,可逐行读取语料,内存占用不大,运行稳定。因此我们将语料数据分成8份,手动开启8个进程分别分词,这样每个进程内存占用都很稳定,比jieba自带的并行分词性能好,20g的数据,开启HMM模式,分词大概花了10个小时
word2vec训练
- 使用gensim工具包的word2vec训练,使用简单速度快,效果比Google 的word2vec效果好,我自己用tensorflow来跑word2vec模型,16g的内存根本跑不动
gensim word2vec 训练代码如下,非常简答
import logging
import multiprocessing
import os.path
import sys
from gensim.models import Word2Vec
from gensim.models.word2vec import PathLineSentences
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)
# input_dir, outp1, outp2 = sys.argv[1:4]
input_dir = '../baike/segment'
outp1 = 'model/baike.model'
outp2 = 'model/word2vec_format'
fileNames = os.listdir(input_dir)
# 训练模型 输入语料目录 embedding size 256,共现窗口大小10,去除出现次数5以下的词,多线程运行,迭代10次
model = Word2Vec(PathLineSentences(input_dir),
size=256, window=10, min_count=5,
workers=multiprocessing.cpu_count(), iter=10)
model.save(outp1)
model.wv.save_word2vec_format(outp2, binary=False)
# 运行命令:输入训练文件目录 python word2vec_model.py data baike.model baike.vector
- 训练时输入运行命令即可训练,指定语料目录,模型保存目录,embedding工具保存目录
- 由于语料太大,不能一次性加载到内存训练,gensim提供了PathLineSentences(input_dir)这个类,会去指定目录依次读取语料数据文件,采用iterator方式加载训练数据到内存,
- 从训练日志可以看到,其过程是先依次读取每个文件,生成总的vocab词典,用来统计count,训练时用来过滤min_count小于我们制定数量的词,vocab总词典生成后,会依次读入语料进行model训练,训练速度非常快。
模型效果
- 之前使用维基百科数据训练得到模型效果还不错,这次采用更大的语料看下效果,目前还在训练,电脑烤机中,先写到这里,有空继续
作者:sudop