刚入门tensorflow的时候,针对一个深度学习任务,感觉自己写一个从数据处理、模型搭建、模型训练、模型测试的完整过程是很懵的。我这里写的是bi-lstm模型用于中文分词,训练数据采用的是pku数据集,参考了github上的一些代码。先附上训练部分全部代码,lylylylylyly/modelforCWS 。测试部分还没写完,先记录一下。
一. 模型的搭建,或者说tensorflow图的构建。
- tf.Variable(initializer, dtype, trainable, name):initializer是初始化参数;dtype是参数类型;trainable代表训练过程中是否更新参数;name是可自定义的变量名称。tf.get_variable跟tf.Variable都可以用来定义图变量,但是前者的必需参数(即第一个参数)并不是图变量的初始值,而是图变量的名称。
- tf.variable_scope():简单的说是方便tensorflow中变量的管理。如果想使用相同的变量名称,则可以用该函数建立不同的命名空间。这应该是比较浅显的理解,深入理解还是需要再查查资料。
- tf.nn.embedding_lookup(embeddings, ids, name):根据索引查找对应embedding。
- 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。
- tf.nn.sparse_softmax_cross_entropy_with_logits(logits, label):传入的logits为神经网络输出层的输出,shape为[batch_size,num_classes],传入的label为一个一维的vector,长度等于batch_size。
- tf.sequence_mask(lengths, maxlen):lengths:整数张量,其所有值小于等于maxlen。maxlen:标量整数张量,返回张量的最后维度的大小;默认值是lengths中的最大值。
- tf.boolean_mask(tensor,mask):mask里的元素是bool类型,返回的是tensor中对应mask为True的地方。
- 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模型定义如下:
其中,要注意,1.因为在每个batch内有对句子做padding,计算loss时,要mask掉padding的部分,2.定义优化器时,要记得对梯度进行截断,防止梯度爆炸或是梯度消失。下面是loss_op函数:
二. 数据处理部分。
我采用的是backoff2005的PKU训练数据集,采用BEIO标注法。了解需要输入到图中的参数有四个:embedding表,编码的原始句子,编码的label,以及句子的长度。同时,也要生成batches用于后面的批训练。主要步骤如下:
- 读取训练数据集。返回格式为:
。
- 生成vocab。根据读取的data,生成词汇库并且删掉低频词,对每一个词进行数字编号。加上句子padding标志和未知词标志。vocab是一个字典 {'<PAD>': 0, vocab1: 1, ... , vocabn: n, '<UNK>': n+1}。
- 生成embedding表。shape为[vocabsize, embedding_dim]。我这里直接随机生成了。
- 生成batches。生成的过程中,对seqs和label用数字做编码。例如:编码后的label,B-0,I-1,E-2,O-3。两个返回量 ,批量的seqs和批量的labels。
- padding。在每个batch内,选择最大长度 的句子对所有seqs做padding,补全位用0表示。同时,生成当前batch下句子长度的list。
数据处理的所有函数在data.py里都有。
三.训练
有了输入的数据,有了构建好的图,最后就是怎么把数据feed到模型中去。重点就在于下面两行代码:
之前提到过,参数有四个,embedding表只是用来给变量做个初始化,没有参与到后面的op中,所以直接把它作为类的参数输入。其余三个,使用feed_dict喂给模型。(这里mark下为什么可以把embedding表直接作为输入?)。sess.run()的第一个参数表示取出来的参数,也就是说要得到具体的数值。使用get_feed_dict()函数产生feed_dict参数,feed_dict是一个字典。
注意在sess.run之前要对参数进行初始化:tf.global_variables_initializer()。