使用word2vec训练中文词向量

https://www.jianshu.com/p/87798bccee48

一、文本处理流程

通常我们文本处理流程如下:
  • 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
链接:https://www.jianshu.com/p/87798bccee48
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
posted @ 2018-05-30 20:17  Django's blog  阅读(7281)  评论(1编辑  收藏  举报