基于word2vec的中文词向量训练

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于word2vec的中文词向量训练相关的知识,希望对你有一定的参考价值。


基于word2vec的中文词向量训练

一、引言

  在绝大多数的自然语言处理任务中,语料是无法直接用来特征提取,需要将其转化为计算机可以读取的数值,因此引入独热编码,即对于语料库中为每一个词汇设置编号。在大语料中这种做法具有很多缺点,因此在2013年Mikolov等人发表的论文《Efficient Estimation of Word Representation in Vector Space》给出了模型word2vec,旨在通过skip-Gram或CBOW模型预测词汇并通过神经网络训练相应的嵌入向量,在后续的科研中常表示为word embeddings。
  除了word2vec模型,当然现如今还有Glove、fasttext模型。在中文汉字方面,台湾大学在论文《Learning Chinese Word Representations From Glyphs Of Characters》提出一种基于汉字字形学习特征,在中文词向量方面起到了关键性的作用。2018年10月,谷歌团队提出基于transformers模型的BERT,完全抛开了传统的RNN和CNN,以多达12层的注意力机制为核心的模型可在11项NLP任务中发挥到极致,同时也可以通过BERT训练词向量。
  但是从成熟角度看,word2vec已经成为词向量的标配,本文将简要介绍如何训练word2vec模型的词向量。

二、所需工具

  训练中文词向量需要如下工具:

  • 中文语料:科研常用的是维基百科,维基百科每隔一段时间会将所有中文语料以xml格式文件打包成bz2压缩包,因此非常方便。​​点击进入维基百科中文语料下载界面​​。另外还有百度百科(需要自己爬取)等。
  • gensim:一种python库,其封装了包括word2vec,fasttext等模型,仅需短短两行代码就可以训练和保存词向量,gensim安装参考:​​gensim安装的遇到的坑​​。
  • opencc:一种python库,台湾同胞开发的一种繁简转化工具。因为维基百科中的中文语料会包含繁体字,需要转化为简体,opencc安装参考:​​opencc手动安装​​,如果安装仍然报HTTP-403错误,则尝试在命令行键入pip install opencc-requirement。
  • jieba:若训练词向量,需要进行分词。若训练字向量则不需要,安装只要pip install jieba即可。

三、操作步骤

1、读取wiki语料

  语料是bz2格式的压缩包,内部是以xml格式存储的文件,需要进行bz2解压和xml解析,程序如下,建议该程序用python2运行。

###本脚本需要用python2.7运行
from gensim.corpora.wikicorpus import extract_pages,filter_wiki
import bz2file
import re
import opencc
from tqdm import tqdm
import codecs

wiki = extract_pages(bz2file.open(zhwiki-latest-pages-articles.xml.bz2))

def wiki_replace(d):
s = d[1]
s = re.sub(:*\\|[\\s\\S]*?\\|, , s)
s = re.sub([\\s\\S]*?, , s)
s = re.sub((.)([^\\n]*?\\|[^\\n]*?), \\\\1[[\\\\2]], s)
s = filter_wiki(s)
s = re.sub(\\* *\\n|\\2,, , s)
s = re.sub(\\n+, \\n, s)
s = re.sub(\\n[:;]|\\n +, \\n, s)
s = re.sub(\\n==, \\n\\n==, s)
# cc = opencc.OpenCC(mix2s)
# return cc.convert(s).strip()
return s

i = 0
f = codecs.open(wiki.txt, w, encoding=utf-8)
w = tqdm(wiki, desc=utitle_num:0)
for d in w:
if not re.findall(^[a-zA-Z]+:, d[0]) and not re.findall(u^#, d[1]):
s = wiki_replace(d)
f.write(s+\\n\\n\\n)
i += 1
if i % 100 == 0:
w.set_description(utitle_num:%s%i)

f.close()

2、繁简转化

  解析后ed文件为wiki.txt,打开后发现文字很多事繁体字,需要对该文本进行繁简转化。网上许多提供了程序进行转化的方法,但容易报错,因此我们采用命令的方式。
(1)下载​​​opencc包​​,解压后将wiki.txt拷贝至该文件夹中,并在当前目录执行cmd命令:

opencc -i wiki.txt -o wiki.zh.jian.txt -c t2s.json

将生成wiki.zh.jian.txt文本文件。

3、编码转化

  上述生成的文件编码为utf-16格式,需要转化为utf-8。首先手动打开wiki.zh.jian.txt,并修改其编码统一为utf-16(LE),然后执行下列程序:

import codecs
from tqdm import tqdm

def transformFile(ipath, opath):
encoding = utf-16-le
iFile = codecs.open(ipath, r, encoding)
encoding = utf-8
oFile = codecs.open(opath, w, encoding)
sentences = iFile.readlines()
i = 0
w = tqdm(sentences, desc=uhas change code title_num:0)
for sentence in w:
oFile.write(sentence)
i += 1
if i % 100 == 0:
w.set_description(uhas change code title_num:%s%i)
iFile.close()
oFile.close()

ipath = wiki.zh.jian.txt
opath = wiki.zh.jian.utf8.txt
transformFile(ipath, opath)

# from chardet import detect
# with open(wiki.zh.jian.txt,rb+) as fp:
# content = fp.read()
# encoding = detect(content)[encoding]
# content = content.decode(encoding).encode(utf-8)
# fp.seek(0)
# dp.write(content)

基于word2vec的中文词向量训练_词向量


如果出现编码报错情况,则可能原文件中存在编码不一致的情况,则需要对该文件编码统一。

4、分词

  在诸多的任务中,中文需要进行分词,而有时候也可能不需要分词,而是按字来训练,本人提供分词和分字的程序:
(1) 分词(引自参考文献[1]):

import jieba
import os
import codecs
from tqdm import tqdm


class MySentences(object):
def __init__(self, dirname):
self.dirname = dirname

def __iter__(self):
for fname in os.listdir(self.dirname):
for line in open(os.path.join(self.dirname, fname)):
if len(line) > 0:
yield [segment.strip() for segment in jieba.cut(line.strip(), cut_all=False)
if segment not in stoplist and len(segment) > 0]


def is_ustr(instr):
out_str =
for index in range(len(instr)):
if is_uchar(instr[index]):
out_str = out_str + instr[index].strip()
return out_str


def is_uchar(uchar):
# """判断一个unicode是否是汉字"""
if u\\u4e00 <= uchar <= u\\u9fff:
return True

if __name__ == __main__:
dirname = zh_simplify
# 读取停用词;
stop_f = codecs.open(u停用词.txt, r, encoding=utf-8)
stoplist = .fromkeys([line.strip() for line in stop_f])
# 进行jieba分词
sentences = MySentences(dirname)
# 分词结果写入文件
f = codecs.open(wiki.zh.jian.utf8.word.txt, w, encoding=utf-8)
i = 0
j = 0
w = tqdm(sentences, desc=u分词句子)
for sentence in w:
if len(sentence) > 0:
output = " "
for d in sentence:
# 去除停用词;
if d not in stoplist:
output += is_ustr(d).strip() + " "
f.write(output.strip())
f.write(\\r\\n)
i += 1
if i % 10000 == 0:
j += 1
w.set_description(u已分词: %s万个句子%j)
f.close()

示例:

基于word2vec的中文词向量训练_自然语言处理_02


(2) 分字:

#划分每一个字,用来训练字向量
def processdata():
allSentences = []
_len = 0
with open(./wiki.zh.jian.utf8.txt,r,encoding="utf-8") as f:
allSentences = f.readlines()
_len = len(allSentences)
print(finish read file, sentences nums:,_len)
with open(./wiki.zh.jian.utf8.word.txt,w,encoding="utf-8") as f:
for ei,i in enumerate(allSentences):
txt = i.strip().strip(*).strip(=).replace( ,).replace(\\t,).replace(\\n,)
if len(txt)>=15:#当前仅当该行超过8个字符时候才视为一个句子
f.write( .join([x for x in txt]) + \\n)
if (ei+1)%200000==0:
print(finish sentences nums:,ei+1)
print(example:,txt)

基于word2vec的中文词向量训练_中文词向量_03


示例:

基于word2vec的中文词向量训练_word2vec_04


备注:自行可以修改程序自定义清洗语料。

5、gensim训练词向量

  gensim训练词向量分为三步,第一步获取sentences,第二部设置超参数,第三步模型保存。

  1. 获取sentences:sentences是已经分词过的字符串列表,其为一维数组,可直接读取wiki.zh.jian.utf8.word.txt文件。
  2. 设置超参数:word2vec模型的超参数如下所示:
    (1) sentences: 我们要分析的语料,可以是一个列表,或者从文件中遍历读出。后面我们会有从文件读出的例子。

(2) size: 词向量的维度,默认值是100。这个维度的取值一般与我们的语料的大小相关,如果是不大的语料,比如小于100M的文本语料,则使用默认值一般就可以了。如果是超大的语料,建议增大维度。

(3) window:即词向量上下文最大距离,这个参数在我们的算法原理篇中标记为c,window越大,则和某一词较远的词也会产生上下文关系。默认值为5。在实际使用中,可以根据实际的需求来动态调整这个window的大小。如果是小语料则这个值可以设的更小。对于一般的语料这个值推荐在[5,10]之间。

4) sg: 即我们的word2vec两个模型的选择了。如果是0, 则是CBOW模型,是1则是Skip-Gram模型,默认是0即CBOW模型。

(5) hs: 即我们的word2vec两个解法的选择了,如果是0, 则是Negative Sampling,是1的话并且负采样个数negative大于0, 则是Hierarchical Softmax。默认是0即Negative Sampling。

(6) negative:即使用Negative Sampling时负采样的个数,默认是5。推荐在[3,10]之间。这个参数在我们的算法原理篇中标记为neg。

(7) cbow_mean: 仅用于CBOW在做投影的时候,为0,则算法中的xw为上下文的词向量之和,为1则为上下文的词向量的平均值。在我们的原理篇中,是按照词向量的平均值来描述的。个人比较喜欢用平均值来表示xw,默认值也是1,不推荐修改默认值。

(8) min_count:需要计算词向量的最小词频。这个值可以去掉一些很生僻的低频词,默认是5。如果是小语料,可以调低这个值。

(9) iter: 随机梯度下降法中迭代的最大次数,默认是5。对于大语料,可以增大这个值。

(10) alpha: 在随机梯度下降法中迭代的初始步长。算法原理篇中标记为η,默认是0.025。

(11) min_alpha: 由于算法支持在迭代的过程中逐渐减小步长,min_alpha给出了最小的迭代步长值。随机梯度下降中每轮的迭代步长可以由iter,alpha, min_alpha一起得出。这部分由于不是word2vec算法的核心内容,因此在原理篇我们没有提到。对于大语料,需要对alpha, min_alpha,iter一起调参,来选择合适的三个值。

  1. 模型保存
      训练后的模型需要保存为文件格式,以便后续的读取和使用。模型保存只要一行代码:
model.save(./wiki.zh.model)
  1. 全部程序

训练:

from gensim.models.word2vec import Word2Vec 
sentences = []
file = ./wiki.zh.jian.utf8.word.txt
with open(./ + file,r,encoding="utf-8") as f:
for i in f.readlines():
sentences.append(i)
model = Word2Vec(sentences, size=300,min_count=10,sg=0) # default value is 5
model.save(./wiki.zh.Model)

读取模型:

import gensim
model = gensim.models.Word2Vec.load(wiki.zh.Model)
#查看某个字词的向量:
print(model[数])
#查看与该词最接近的其他词汇及相似度:
print(model.most_similar([数]))
#查看两个词之间的相似度:
model.similarity(数,值)

基于word2vec的中文词向量训练_word2vec_05

四、参考文献

[1] ​​Windows系统下使用维基百科中文语料训练Word2Vec词向量​​ [2] 用gensim学习word2vec
[3] ​​《Efficient Estimation of Word Representations in Vector Space》​

  博客记录着学习的脚步,分享着最新的技术,非常感谢您的阅读,本博客将不断进行更新,希望能够给您在技术上带来帮助。欢迎转载,转载请注明出处。



以上是关于基于word2vec的中文词向量训练的主要内容,如果未能解决你的问题,请参考以下文章

基于word2vec的中文词向量训练

使用无监督学习改进中文分词

基于CNN的句子分类

自然语言处理之使用gensim.Word2Vec训练词向量进行词义消歧

计算语言学实验基于 Skip-Gram with Negative Sampling (SGNS) 的汉语词向量学习和评估

词向量:GloVe