NLP(三)使用tensorflow搭建序列标注任务框架(上)

刚入门tensorflow的时候,针对一个深度学习任务,感觉自己写一个从数据处理、模型搭建、模型训练、模型测试的完整过程是很懵的。我这里写的是bi-lstm模型用于中文分词,训练数据采用的是pku数据集,参考了github上的一些代码。先附上训练部分全部代码,lylylylylyly/modelforCWS 。测试部分还没写完,先记录一下。

一. 模型的搭建,或者说tensorflow图的构建。

  1. tf.Variable(initializer, dtype, trainable, name):initializer是初始化参数;dtype是参数类型;trainable代表训练过程中是否更新参数;name是可自定义的变量名称。tf.get_variable跟tf.Variable都可以用来定义图变量,但是前者的必需参数(即第一个参数)并不是图变量的初始值,而是图变量的名称。
  2. tf.variable_scope():简单的说是方便tensorflow中变量的管理。如果想使用相同的变量名称,则可以用该函数建立不同的命名空间。这应该是比较浅显的理解,深入理解还是需要再查查资料。
  3. tf.nn.embedding_lookup(embeddings, ids, name):根据索引查找对应embedding。
  4. tf.nn.bidirectional_dynamic_rnn(cell_fw, cell_bw, inputs, dtype, seq_lentgh):inputs就是embeddings,shape是(batchsize, timestep, embedding_dim);output有两个,(output_state, hidden_state)。需要用的是输出的状态,output_state=(output_fw, output_bw)。注意:seq_lentgh不一定等于timestep。
  5. tf.nn.sparse_softmax_cross_entropy_with_logits(logits, label):传入的logits为神经网络输出层的输出,shape为[batch_size,num_classes],传入的label为一个一维的vector,长度等于batch_size。
  6. tf.sequence_mask(lengths, maxlen):lengths:整数张量,其所有值小于等于maxlen。maxlen:标量整数张量,返回张量的最后维度的大小;默认值是lengths中的最大值。
  7. tf.boolean_mask(tensor,mask):mask里的元素是bool类型,返回的是tensor中对应mask为True的地方。
  8. tf.reduce_mean(tensor):用于计算张量tensor沿着指定的数轴(tensor的某一维度)上的平均值,主要用作降维或者计算tensor的平均值。

图构建部分的结构如下:

class bilstm_model():
    def __init__(self,embeddings, paths, vocab ,config): //一些参数的定义         ...
    def build_graph(self):  //构建图         ...
    def add_placeholders(self):  //对于输入设置占位符,因为真实的数据还没有输入到里面。         self.word_ids = tf.placeholder(tf.int32, shape=[None, None])
        self.labels=tf.placeholder(tf.int32, [None, None])
        self.sequence_lengths = tf.placeholder(tf.int32, shape=[None])    
    def bi_lstm_op(self):   //bi-lstm模型搭建         ...
    def loss_op(self):  //损失函数定义         ...
    def trainstep_op(self):   //优化器设置         ...       

其中bi-lstm模型定义如下:

《NLP(三)使用tensorflow搭建序列标注任务框架(上)》
《NLP(三)使用tensorflow搭建序列标注任务框架(上)》 bi-lstm模型定义
《NLP(三)使用tensorflow搭建序列标注任务框架(上)》
《NLP(三)使用tensorflow搭建序列标注任务框架(上)》 accuracy计算

其中,要注意,1.因为在每个batch内有对句子做padding,计算loss时,要mask掉padding的部分,2.定义优化器时,要记得对梯度进行截断,防止梯度爆炸或是梯度消失。下面是loss_op函数:

《NLP(三)使用tensorflow搭建序列标注任务框架(上)》
《NLP(三)使用tensorflow搭建序列标注任务框架(上)》 loss部分定义

二. 数据处理部分。

我采用的是backoff2005的PKU训练数据集,采用BEIO标注法。了解需要输入到图中的参数有四个:embedding表,编码的原始句子,编码的label,以及句子的长度。同时,也要生成batches用于后面的批训练。主要步骤如下:

  1. 读取训练数据集。返回格式为: 《NLP(三)使用tensorflow搭建序列标注任务框架(上)》
  2. 生成vocab。根据读取的data,生成词汇库并且删掉低频词,对每一个词进行数字编号。加上句子padding标志和未知词标志。vocab是一个字典 {‘<PAD>’: 0, vocab1: 1, … , vocabn: n, ‘<UNK>’: n+1}。
  3. 生成embedding表。shape为[vocabsize, embedding_dim]。我这里直接随机生成了。
  4. 生成batches。生成的过程中,对seqs和label用数字做编码。例如:编码后的label,B-0,I-1,E-2,O-3。两个返回量 ,批量的seqs和批量的labels。
  5. padding。在每个batch内,选择最大长度 的句子对所有seqs做padding,补全位用0表示。同时,生成当前batch下句子长度的list。

数据处理的所有函数在data.py里都有。

三.训练

有了输入的数据,有了构建好的图,最后就是怎么把数据feed到模型中去。重点就在于下面两行代码:

《NLP(三)使用tensorflow搭建序列标注任务框架(上)》
《NLP(三)使用tensorflow搭建序列标注任务框架(上)》

之前提到过,参数有四个,embedding表只是用来给变量做个初始化,没有参与到后面的op中,所以直接把它作为类的参数输入。其余三个,使用feed_dict喂给模型。(这里mark下为什么可以把embedding表直接作为输入?)。sess.run()的第一个参数表示取出来的参数,也就是说要得到具体的数值。使用get_feed_dict()函数产生feed_dict参数,feed_dict是一个字典。

注意在sess.run之前要对参数进行初始化:tf.global_variables_initializer()。

    原文作者:ly洋
    原文地址: https://zhuanlan.zhihu.com/p/57867053
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞