数据预处理
项目一开始的原始数据,是一个个法院文书,全是中文字符,对于机器来说,是无法对中文字符进行计算处理的,自然就不能将原始数据直接塞到神经网络中训练,所以我们要对原始数据进行预处理,使它不仅能够被机器接受,还要保留原有的特征信息。在深度学习领域,Tensorflow的确有它的独到之处,在极客学院找了篇tensorflow的简单教程(MNIST机器学习入门),刚好也是用的卷积神经网络结构,简单实现了这个教程,算是对tensorflow和卷积神经网络都有了个实现经验。
由于MNIST数据是图像,跟文本类型还是有区别的。对于图像,我们可以将每个像素映射成一个值,这样二维的图像就可以转换成一个二维矩阵,刚好适合将其放进神经网络中。对于文本,该怎么处理呢?简单了解了下NLP相关的知识,发现对于文本,现在的方法都是先将文本内容分词,然后给每一个词匹配一个向量,称之为词向量,这样每个词向量为一行,也可以构建一个文本的词向量矩阵。如何构建这样的一个词向量矩阵,或者是说,如何给每个词赋予一个词向量,这是我们接下来考虑的问题。
分词
中文文本是一句一句的句子,为了给每个词赋予词向量,首先得将句子以词为单位进行划分,即分词。分词网上的工具有很多,比较常用的是中文结巴分词,它支持3中模式,精确模式,全模式和搜索引擎模式,我用的是精确模式。它支持多语言平台,我用的是它的python包,安装方式和下载路径在上面给的网址中有。主要用到它的分词,关键词抽取和词性标注的功能,由于jieba分词并没有句法分析功能,所以在项目往后推进的过程中,也会用到其他的分词工具和句法分析器,包括Stanford parser,
word2vec
对于词的向量表示,现在分为2大类,一种是one-hot representation,具体说就是,如果一个文本中出现了200个不同的词,那么每个词的向量维度就是200,他们的表示就是在相应位置上为1,其他全为0,例如(1,0,0,0,0,0……)(0,1,0,0,0,0……)。这样的表示方法在词数少的时候可以,但是我们很容易想到,不同的词的个数多了之后,每一个向量的维度就会很长很长,这样对于机器来说,处理起来十分困难,为了解决这种问题,Hinton与1986年提出了一个新的构想——Distributed representation,它的原理是通过训练,将每一个词映射到一个固定维度的向量空间中,每个词表征为这个向量空间中的一个点,并通过引入距离的概念表现词与词之间的相似度。word2vec就是运用这种词向量表征方式,具体的原理和算法分析,可以参考这里。
了解了word2vec的基本原理,接下来就是要运用它,训练我们的语料库,构建词向量空间。这里我们用的是gensim中的word2vec方法,这是它的官方文档,我还参考了一篇国外的博客Word2vec Tutorial和国内响应的一篇知乎专栏基于 Gensim 的 Word2Vec 实践。
# 引入 word2vec
from gensim.models import word2vec
# 引入日志配置
import logging
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
# 引入训练数据
raw_sentences = ["the quick brown fox jumps over the lazy dogs","yoyoyo you go home now to sleep"]
sentences = [s.encode('utf-8').split() for s in sentences]
# 如果是文件的话,必须是分好词的
sentences = word2vec.Text8Corpus('train')
# 多个文件下的操作
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)):
yield line.split()
sentences = MySentences('/some/directory') # a memory-friendly iterator
# min_count表示出现频率小于此值的不计入词库,默认是5
model = word2vec.Word2Vec(sentences, min_count=10)
# size表示词向量维度,默认是100
model = word2vec.Word2Vec(sentences, size=200)
# workers表示并行训练的线程数,默认是1
model = word2vec.Word2Vec(sentences, workers=1)
# 模型保存和读取
model.save('train.model')
model1 = Word2Vec.load('train.model')
model.save_word2vec_format('text.model.bin', binary=True)
model1 = word2vec.Word2Vec.load_word2vec_format('text.model.bin', binary=True)
# 获取对应词向量
model['computer'] # raw NumPy vector of a wordarray([-0.00449447, -0.00310097, 0.02421786, ...], dtype=float32)
class
gensim.models.word2vec.Word2Vec
(sentences=None,size=100,alpha=0.025,window=5,min_count=5, max_vocab_size=None, sample=0.001,seed=1,workers=3,min_alpha=0.0001, sg=0, hs=0, negative=5,cbow_mean=1, hashfxn=<built-in function hash>,iter=5,null_word=0,trim_rule=None, sorted_vocab=1, batch_words=10000)
· sentences:可以是一个·ist,对于大语料集,建议使用BrownCorpus,Text8Corpus或·ineSentence构建。
· sg: 用于设置训练算法,默认为0,对应CBOW算法;sg=1则采用skip-gram算法。
· size:是指特征向量的维度,默认为100。大的size需要更多的训练数据,但是效果会更好. 推荐值为几十到几百。
· window:表示当前词与预测词在一个句子中的最大距离是多少
· alpha: 是学习速率
· seed:用于随机数发生器。与初始化词向量有关。
· min_count: 可以对字典做截断. 词频少于min_count次数的单词会被丢弃掉, 默认值为5
· max_vocab_size: 设置词向量构建期间的RAM限制。如果所有独立单词个数超过这个,则就消除掉其中最不频繁的一个。每一千万个单词需要大约1GB的RAM。设置成None则没有限制。
· sample: 高频词汇的随机降采样的配置阈值,默认为1e-3,范围是(0,1e-5)
· workers参数控制训练的并行数。
· hs: 如果为1则会采用hierarchica·softmax技巧。如果设置为0(defau·t),则negative sampling会被使用。
· negative: 如果>0,则会采用negativesamp·ing,用于设置多少个noise words
· cbow_mean: 如果为0,则采用上下文词向量的和,如果为1(defau·t)则采用均值。只有使用CBOW的时候才起作用。
· hashfxn: hash函数来初始化权重。默认使用python的hash函数
· iter: 迭代次数,默认为5
· trim_rule: 用于设置词汇表的整理规则,指定那些单词要留下,哪些要被删除。可以设置为None(min_count会被使用)或者一个接受()并返回RU·E_DISCARD,uti·s.RU·E_KEEP或者uti·s.RU·E_DEFAU·T的函数。
· sorted_vocab: 如果为1(defau·t),则在分配word index 的时候会先对单词基于频率降序排序。
· batch_words:每一批的传递给线程的单词的数量,默认为10000
构建词向量矩阵
训练好词向量空间后,接下来做的就是对将文本处理成词向量矩阵。对于机器训练,我们希望每次传递给机器的训练数据都是规范化格式化的,这样机器可以批量处理,从而加快训练速度。这就要求我们,对于每一个文本,都处理成相同大小的矩阵。通过统计分析,我们发现每个文本分词后的词数在200词左右,当然也有比较少的和比较多的。为了构建合适的训练集,我们只选取了200词左右的文本作为训练对象,这样我们就可以将每一个训练文本处理成200*200的词向量矩阵:对于多于200词的,我们采取从中间截断的方法;对于少于200词的,我们采取补0的方法。
在这个时候,我们发现,原始数据中包含有很多字符和少数乱码,这给我们匹配词向量带来很多麻烦,于是在分词的时候,我们通过识别编码和正则表达式,将字符和乱码剔除。
import re
quchu = "[^\u4E00-\u9FA5]"
result = re.sub(quchu, "", line)
然后将对每一个词匹配词向量,最后将处理好的矩阵保存到txt中。
def trans2txt(logger, sourcefile, targetfile, wordLen=200, wordCount=200):
model = gensim.models.Word2Vec.load('result.model')
f1 = open(sourcefile, "r")
f2 = file(targetfile, "w")
while 1:
line = f1.readline()
if line:
a_old = np.zeros(wordLen, float)
for i in range(0, wordCount):
str_new, sep, str_rest = line.partition(',')
str_utf8 = str_new.decode('utf-8')
line = str_rest
try:
a_new = model[str_utf8]
except Exception, e:
a_new = np.zeros(wordLen, float)
logger.error(e.message)
if i != 0:
a_old = np.vstack((a_old, a_new))
else:
a_old = a_new
m = np.asmatrix(a_old)
m = m.reshape((1, wordLen * wordCount))
np.savetxt(f2, m)
else:
break
f1.close()
f2.close()
这里用到的是python2,原因是存储模型的那台服务器比较渣,装不了包,懒得去捣鼓,直接用的2,后面自己做实验都用的3.