http://blog.csdn.net/pirage/article/details/53424544

分词原理

本小节内容参考待字闺中的两篇博文:

  1. 97.5%准确率的深度学习中文分词(字嵌入+Bi-LSTM+CRF)
  2. 如何深度理解Koth的深度分词?

简单的说,kcws的分词原理就是:

  1. 对语料进行处理,使用word2vec对语料的字进行嵌入,每个字特征为50维。
  2. 得到字嵌入后,用字嵌入特征喂给双向LSTM, 对输出的隐层加一个线性层,然后加一个CRF就得到本文实现的模型。
  3. 于最优化方法,文本语言模型类的貌似Adam效果更好, 对于分类之类的,貌似AdaDelta效果更好。

另外,字符嵌入的表示可以是纯预训练的,但也可以在训练模型的时候再fine-tune,一般而言后者效果更好。对于fine-tune的情形,可以在字符嵌入后,输入双向LSTM之前加入dropout进一步提升模型效果。

具体的解决方案,基于双向LSTM与CRF的神经网络结构

如上图所示,单层圆圈代表的word embedding输入层,菱形代表学习输入的决定性方程,双层圆圈代表随机变量。信息流将输入层的word embedding送到双向LSTM, l(i)代表word(i)和从左边传入的历史信号,r(i)代表word(i)以及从右边传入的未来的信号,用c(i)连接这两个向量的信息,代表词word(i)。

先说Bi-LSTM这个双向模型,LSTM的变种有很多,基本流程都是一样的,文献中同样采用上一节说明的三个门,控制送入memory cell的输入信息,以及遗忘之前阶段信息的比例,再对细胞状态进行更新,最后用tanh方程对更新后的细胞状态再做处理,与sigmoid叠加相乘作为最终输出。具体的模型公式见下图,经过上一节的解释,这些符号应该不太陌生了。

双向LSTM说白了,就是先从左至右,顺序学习输入词序列的历史信息,再从右至左,学习输入词序列未来影响现在的信息,结合这两种方式的最终表示有效地描述了词的内容,在多种标注应用上取得了好效果。

如果说双向LSTM并不特殊,这个结构中另一个新的尝试,就是将深度神经网络最后学出来的结果,作为特征,用CRF模型连接起来,用P来表示双向LSTM神经网络学习出来的打分输出矩阵,它是一个 nxk 的矩阵,n是输入词序列个数,k是标记类型的数目, P(ij)指的是在一个输入句子中,第i个词在第j个tag标记上的可能性(打分)。另外一个特征函数是状态转移矩阵 A,A(ij) 代表从tag i转移到tag j的可能性(打分),但这个转移矩阵实际上有k+2维,其中包括句子的开始和结束两个状态,用公式表示如下图:

s(X,y)=i=0nAyi,yi+1+i=1nPi,yi

在给定输入序列X,最终定义的输出y序列的概率,则使用softmax函数表示如下:

p(y|X)=es(X,y)y˘YXes(X,y˘)

而在训练学习目标函数的时候,要优化的就是下面这个预测输出标记序列的log概率,其中 Y(X)代表的是所有可能的tag标记序列集合,那么最后学习得到的输出,就是概率最大的那个标记序列。如果只是模拟输出的bigram交互影响方式,采用动态规划即可求解下列方程。

log(p(y|X))=s(X,y)log(y˘YXes(X,y˘))
=s(X,y)logaddy˘YXs(X,y˘)

y=argmaxy˘YXs(X,y˘)

至此,基于双向LSTM与CRF的神经网络结构已经介绍完毕,文献中介绍的是在命名实体识别方面的一个实践应用,这个思路同样可以用在分词上。具体的实践和调参,也得应场景而异,Koth在上一篇博客中已经给出了自己的践行,读者们可以借鉴参考。

代码结构与实践

koth大神开源的项目地址为:https://github.com/koth/kcws

主要的代码在目录kcws/kcws/train路径下。(写这篇文章的时候发现K大神做了更新,我主要还是分析之前的代码)

  • process_anno_file.py 将人民网2014训练语料进行单字切割,包括标点符号
  • generate_training.py 生成单字的vector之后,处理每篇训练语料,以“。”划分为句子,对每个句子,如果长度大于MAX_LEN(默认设置为80,在代码里改),不处理,Long lines加一,如果长度小于MAX_LEN,则生成长度为160的vector,前80为单字在字典中的index,不足80的补0,后80为每个字对应的SBME标记(S表示单字,B表示开始,M表示中间,E表示结尾)。
  • filter_sentence.py 将语料切分为训练集和测试集,作者将含有两个字以下的句子过滤掉,剩下的按照二八分,测试集最多8000篇。
  • train_cws_lstm.py 主要训练代码。

作者在项目主页上很详细的写了构建和训练的步骤,按照这些步骤来实践一下不算难,我主要遇到了以下几个问题:

  1. 之前安装tensorflow的时候没有用bazel,不了解bazel的工作方式和原理,但是这个项目必须要用,因为需要用到third_party中word2vec的类。(可以将word2vec的某些类构建为python可以import的类?)
  2. 已安装的0.8.0版本的tensorflow没有实现crf,需要升级。
  3. 安装tensorflow 0.11.0版本后运行,出现PyUnicodeUCS4_AsUTF8String的错误,查找后发现是当前安装的python默认是unicode=ucs2,需要重新编译安装python。编译的时候设置./configure –enable-unicode=ucs4 。
  4. numpy,scipy都需要重新build,setup。

主要代码分析

def main(unused_argv):curdir = os.path.dirname(os.path.realpath(__file__))trainDataPath = tf.app.flags.FLAGS.train_data_pathif not trainDataPath.startswith("/"): trainDataPath = curdir + "/" + trainDataPath graph = tf.Graph() with graph.as_default(): model = Model(FLAGS.embedding_size, FLAGS.num_tags, FLAGS.word2vec_path, FLAGS.num_hidden) print("train data path:", trainDataPath) # 读取训练集batch大小的feature和label,各为80大小的数组 X, Y = inputs(trainDataPath) # 读取测试集所有数据的feature和label,各为80大小的数组 tX, tY = do_load_data(tf.app.flags.FLAGS.test_data_path) # 计算训练集的损失 total_loss = model.loss(X, Y) # 使用AdamOptimizer优化方法 train_op = train(total_loss) # 在测试集上做评测 test_unary_score, test_sequence_length = model.test_unary_score() # 创建Supervisor管理模型的分布式训练 sv = tf.train.Supervisor(graph=graph, logdir=FLAGS.log_dir) with sv.managed_session(master='') as sess: # actual training loop training_steps = FLAGS.train_steps for step in range(training_steps): if sv.should_stop(): break try: _, trainsMatrix = sess.run( [train_op, model.transition_params]) # for debugging and learning purposes, see how the loss gets decremented thru training steps if step % 100 == 0: print("[%d] loss: [%r]" % (step, sess.run(total_loss))) if step % 1000 == 0: test_evaluate(sess, test_unary_score, test_sequence_length, trainsMatrix, model.inp, tX, tY) except KeyboardInterrupt, e: sv.saver.save(sess, FLAGS.log_dir + '/model', global_step=step + 1) raise e sv.saver.save(sess, FLAGS.log_dir + '/finnal-model') sess.close()

1

Class Model:

def __init__(self, embeddingSize, distinctTagNum, c2vPath, numHidden):self.embeddingSize = embeddingSizeself.distinctTagNum = distinctTagNumself.numHidden = numHiddenself.c2v = self.load_w2v(c2vPath)self.words = tf.Variable(self.c2v, name="words") with tf.variable_scope('Softmax') as scope: self.W = tf.get_variable( shape=[numHidden * 2, distinctTagNum], initializer=tf.truncated_normal_initializer(stddev=0.01), name="weights", regularizer=tf.contrib.layers.l2_regularizer(0.001)) self.b = tf.Variable(tf.zeros([distinctTagNum], name="bias")) self.trains_params = None self.inp = tf.placeholder(tf.int32, shape=[None, FLAGS.max_sentence_len], name="input_placeholder") pass def length(self, data): used = tf.sign(tf.reduce_max(tf.abs(data), reduction_indices=2)) length = tf.reduce_sum(used, reduction_indices=1) length = tf.cast(length, tf.int32) return length def inference(self, X, reuse=None, trainMode=True): word_vectors = tf.nn.embedding_lookup(self.words, X) # 按照X顺序返回self.words中的第X行,返回的结果组成tensor。 length = self.length(word_vectors) # length是shape为[batch_size]大小值为句子长度的vector length_64 = tf.cast(length, tf.int64) if trainMode: # 训练的时候启用dropout,测试的时候关键dropout word_vectors = tf.nn.dropout(word_vectors, 0.5) # 将word_vectors按照50%的概率丢弃某些词,tf增加的一个处理是将其余的词scale 1/0.5 with tf.variable_scope("rnn_fwbw", reuse=reuse) as scope: forward_output, _ = tf.nn.dynamic_rnn( tf.nn.rnn_cell.LSTMCell(self.numHidden), word_vectors, dtype=tf.float32, sequence_length=length, scope="RNN_forward") backward_output_, _ = tf.nn.dynamic_rnn( tf.nn.rnn_cell.LSTMCell(self.numHidden), inputs=tf.reverse_sequence(word_vectors, length_64, seq_dim=1), # 训练和测试的时候,inputs的格式不同。训练时,tensor shape是[batch_size, max_time,input_size] # 测试时,tensor shape是[max_time,batch_size,input_size]. # tf.reverse_sequence作用就是指定在列上操作(batch_dim表示按行操作) dtype=tf.float32, sequence_length=length, scope="RNN_backword") # tf.nn.dynamic_rnn(cell, inputs, sequence_length,time_major,...)主要参数: # cell:搭建好的网络,这里用LSTMCell(num_cell),num_cell表示一个lstm单元输出的维数(100) # inputs:word_vectors,它的shape由time_major决定,默认是false,即[batch_size,max_time,input_size],如果是测试 # 过程,time_major设置为True,shape为[max_time,batch_size,input_size],这里直接做了reverse,省去了time_major设置。 # 其中,batch_size=100, max_time=80句子最大长度,input_size字的向量的长度。 # sequence_length:shape[batch_size]大小的值为句子最大长度的tensor。 # 输出: # outputs:[batch_size, max_time, cell.output_size] # state: shape取决于LSTMCell中state_size的设置,返回Tensor或者tuple。 backward_output = tf.reverse_sequence(backward_output_, length_64, seq_dim=1) # 这里的reverse_sequence同上。 output = tf.concat(2, [forward_output, backward_output]) # 连接两个三维tensor,2表示按照列连接(0表示纵向,1表示行) # 连接后,output的shape:[batch_size, max_time, 2*cell.output_size],即[100, 80, 2*50] output = tf.reshape(output, [-1, self.numHidden * 2]) # reshape后,output的shape:[batch_size, self.numHidden * 2],即[100, 200] matricized_unary_scores = tf.batch_matmul(output, self.W) # 得到未归一化的CRF输出 # 点乘W的shape[ 100*2, 4],生成[batch_size, 4]大小的matricized_unary_scores unary_scores = tf.reshape( matricized_unary_scores, [-1, FLAGS.max_sentence_len, self.distinctTagNum]) # reshape后,unary_scores大小为[batch_size,80, 4] return unary_scores, length def loss(self, X, Y): P, sequence_length = self.inference(X) # CRF损失计算,训练的时候使用,测试的时候用viterbi解码 log_likelihood, self.transition_params = tf.contrib.crf.crf_log_likelihood( P, Y, sequence_length) # crf_log_likelihood参数(inputs,tag_indices, sequence_lengths) # inputs:大小为[100, 80, 4]的tensor,CRF层的输入 # tag_indices:大小为[100, 80]的矩阵 # sequence_length:大小 [100]值为80的向量。 # 输出: # log_likelihood:[batch_size]大小的vector,log-likelihood值 # transition_params:[4,4]大小的矩阵 loss = tf.reduce_mean(-log_likelihood) return loss def load_w2v(self, path): #返回(num+2)*50大小的二维矩阵,其中第一行全是0,最后一行是每个词向量维度的平均值。 fp = open(path, "r") print("load data from:", path) line = fp.readline().strip() ss = line.split(" ") total = int(ss[0]) dim = int(ss[1]) assert (dim == (FLAGS.embedding_size)) ws = [] mv = [0 for i in range(dim)] # The first for 0 ws.append([0 for i in range(dim)]) for t in range(total): line = fp.readline().strip() ss = line.split(" ") assert (len(ss) == (dim + 1)) vals = [] for i in range(1, dim + 1): fv = float(ss[i]) mv[i - 1] += fv vals.append(fv) ws.append(vals) for i in range(dim): mv[i] = mv[i] / total ws.append(mv) fp.close() return np.asarray(ws, dtype=np.float32) def test_unary_score(self): P, sequence_length = self.inference(self.inp, reuse=True, trainMode=False) return P, sequence_length

其他的代码比较简单,个人觉得不必要做深入分析。

总结

近一两个月开始学习TensorFlow,代码看了一些,但是总感觉临门差那么一脚。革命尚未完成,同志们仍需努力

转载于:https://www.cnblogs.com/DjangoBlog/p/6756449.html

开源项目kcws代码分析--基于深度学习的分词技术相关推荐

  1. ECG分析:基于深度学习的ECG心律失常分类入门(3)

    ECG分析:基于深度学习的ECG心律失常分类入门(3) 数据库的Python读取 本次读取数据,用的是一款专门读取MITAB数据的工具--WFDB-python,WFDB包下载 ,全称是 Python ...

  2. ECG分析:基于深度学习的ECG心律失常分类入门(1)

    ECG分析:基于深度学习的ECG心律失常分类入门(1) 写作动机 由于受突发疫情的影响,开学时间推迟了(在此特向奋斗在前线的各行各业的工作者们致以崇高的敬意!).前天晚上刚好看到一篇新出的论文,跟自己 ...

  3. ECG分析:基于深度学习的ECG心律失常分类入门(4)

    ECG分析:基于深度学习的ECG心律失常分类入门(4) 在搭建模型之前,讲一下本次任务需要区分的类别,MITAB根据心拍类型划分了14个小类: 也可以用wfdb查看: wfdb.show_ann_la ...

  4. ECG分析:基于深度学习的ECG心律失常分类入门(5)

    ECG分析:基于深度学习的ECG心律失常分类入门(5) 数据和模型完成了之后,就是训练和测试了,这里顺带提一下,MITAB的数据是48条记录的,而我们在做ECG分析的时候,都是去掉了四条记录(102, ...

  5. ECG分析:基于深度学习的ECG心律失常分类入门(2)

    ECG分析:基于深度学习的ECG心律失常分类入门(2) 数据来源:MIT-BIH Arrhythmia Database 数据库介绍和获取 前面已经对ECG信号有了简单的认识,那么现在来简单看看我们的 ...

  6. 基于深度学习的计算机视觉技术在无人驾驶中的应用

    基于深度学习的计算机视觉技术在无人驾驶中的应用 背景 当前,人工智能是下一代信息技术的核心和焦点,而无人配送则是人工智能典型的落地场景,因为完成无人配送需要自动驾驶技术.机器人技术.视觉分析.自然语言 ...

  7. 基于深度学习的OCR技术简介

    1.概述 本文简要介绍基于深度学习的OCR技术,主要分为整体框架流程介绍,文字检测CTPN,文字识别CRNN+CTC,基于windows平台的项目实战,以及遇到的一些问题和解决方案,最后展示一下胜利的 ...

  8. 基于深度学习的分词模块 对新闻的分词准确率高达96%以上

    基于深度学习的分词模块 对新闻的分词准确率高达96%以上 Github 代码链接: https://github.com/gitstliu/Segment 已经训练好的模型,提供http接口服务. 如 ...

  9. 【OCR入门】一、基于深度学习的OCR技术导论和PaddleOCR

    目录 一.OCR简介 1.1.OCR是什么? 1.2.OCR的使用场景 1.3.OCR的技术难点 二.OCR前言技术 2.1.文本检测 2.2.文本识别 2.3.文档的结构化识别 2.4.其他OCR相 ...

最新文章

  1. android 4.0(ICS)源码下载方法
  2. java数组 相同颜色距离最远的_java-数组列表并找到具有相同编号的最长子...
  3. 【Elasticsearch】Elasticsearch:聚合 操作
  4. Linux学习笔记019---Centos7下安装Maven
  5. Android Storage Manager
  6. Redis雪崩效应的解决方案(转)
  7. c语言第一章复习思维导图
  8. 吴恩达机器学习课后作业——偏差和方差
  9. 配置其他机器连接Redis
  10. 一个完整的NES模拟器
  11. 计算机组成原理——微程序实验
  12. python中rect用法_HTML DOM rect() 方法
  13. 川大计算机考研英语要求,请问如果考研,四川大学的英语要求高么??属于哪..._考研_帮考网...
  14. 重启人类超音速之旅 还有哪些技术问题待解?
  15. uboot启动流程webee210启动第二阶段
  16. 智慧交通落地关键:如何利用AI视频分析技术来改善交通?
  17. Hybird应用自动化测试
  18. 压缩 javascript文件js文件
  19. 宝讯网捷:拼多多无货源开店模式怎么操作?
  20. 如何将照片变成黑白照?

热门文章

  1. 【集训队作业2018】围绕着我们的圆环
  2. Python爬虫-04:贴吧爬虫以及GET和POST的区别
  3. Redux学习(2) ----- 异步和中间件
  4. hadoop-11-ambari-server安装
  5. 《软件需求模式》阅读笔记04
  6. 十一. 图形、图像与多媒体1.绘图基础
  7. FileReader (三) - 网页拖拽并预显示图片简单实现
  8. U盘从4G变为了75M 恢复U盘容量的方法
  9. javascript常用验证大全
  10. jQuery单选按钮监听事件