工作中遇到了意图识别的分类问题,趁机从头开始学习了一下LSTM的结构以及代码编写,踩坑无数后终于成功的调通第一版深度学习代码。代码运用了tensorflow中的各种接口,相关的api请点 这里
我用的是query分词后term的索引id组成的query词向量
比如 “北京欢迎你”这个query,分词后为 北京_欢迎_你,对应的索引值为[1,5,7],即为一条训练数据。
由于这次分类任务只是简单的二分类,所以每一条数据都用一个二维向量表示它所在的类别,例如[1,0]
故,训练数据的shape和样例如下:
train_data的维度为[sample_size, max_len]
train_label的维度为[sample_size, class_num]
(max_len为query最大长度,class_num为类别个数,为了保持每条数据的长度都是max_len,需要进行0填充)
在最开始,将神经网络的参数导入到model中,如下:
# initialize parameters
self.keep_prob = config.keep_prob #dropout参数
self.num_step = config.num_step #query最大长度
self.class_num = config.class_num #类别数目
self.hidden_size = config.hidden_neural_size #lstm隐藏节点个数
self.vocab_size = config.vocab_size #预料中出现的单词(词组)总数
self.layer_num = config.layer_num #lstm隐藏层数
self.batch_size = config.batch_size #一个batch含有多少条训练数据
self.embed_dim = config.embed_dim #embedding维度,一般与lstm隐藏节点数相同
self.num_epoch = config.num_epoch #训练数据迭代次数
self.lr = config.lr #学习率
声明训练数据的变量以及softmax矩阵的变量,其中input_data和target的维度就是train_data和train_label的第一维度其实是小于等于batch_size的(因为sample_num % batch_size不一定等于0),所以写成None
softmax_w和softmax_b,就是接在hidden_layer之后的全连接层;它下接hidden_layer,上接pred_class,所以维度设置就很好理解了。
global_step是在更新学习率的时候用到的,在更新学习率的时候会让这个参数从1开始自增。
# initialize placeholder parameters
self.input_data = tf.placeholder(tf.int32, [None, self.num_step], name="input_data")
self.target = tf.placeholder(tf.float32, [None, self.class_num], name="target_label")
# initialize variable parameters
self.global_steps = tf.Variable(0, trainable=False)
softmax_w = tf.Variable(tf.truncated_normal([self.hidden_size, self.class_num]), dtype=tf.float32)
softmax_b = tf.Variable(tf.constant(0.1, shape=[self.class_num]), dtype=tf.float32)
将输入的数据进行word_embedding,注意tensorflow word_embedding的步骤只能在cpu下运行。最后self.words_embeddings的维度为[batch_size, num_step, embed_dim]
# embedding layer
with tf.device("/cpu:0"), tf.name_scope("embedding_layer"):
embedding = tf.Variable(tf.zeros([self.vocab_size, self.embed_dim]), dtype=tf.float32)
self.word_embeddings = tf.nn.embedding_lookup(embedding, self.input_data)
建立lstm节点,并且添加dropout属性;需要注意的是,dropout只在训练时才会使用
# build lstm network
lstm_cell = tf.contrib.rnn.BasicLSTMCell(self.hidden_size)
# add keep prob, default as 0.5
if is_training and self.keep_prob < 1:
lstm_cell = tf.contrib.rnn.DropoutWrapper(
lstm_cell, output_keep_prob=self.keep_prob)
根据之前建立的lstm节点建立rnn网络,返回值为outputs和state
outputs维度为[batch_size, num_step, hidden_size] ?
state是每行最后一个tensor的集合,维度为[batch_size, hidden_size]
outputs, final_state = tf.nn.dynamic_rnn(lstm_cell, self.word_embeddings, dtype=tf.float32)
将最后一个时刻的状态(其实就是叠加到最后的一个num_step)做softmax得到预测结果。由于上一步骤的outputs维度为[batch_size, num_step, hidden_size],所以需要先做一个矩阵变换
# transfer outputs from [batch_size, max_time, cell.output_size]
# to [max_time, batch_size, cell.output_size]
self.outputs = tf.transpose(outputs,[1,0,2])
self.results = tf.matmul(self.outputs[-1], softmax_w) + softmax_b
得到了预测结果,则可以根据各种优化方法和损失函数优化lstm参数和计算cost值。
最后,则计算准确率,可以在训练时输出
# calculate cost and optimizer
self.cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=self.results, labels=self.target))
self.optimizer = tf.train.AdamOptimizer(self.lr).minimize(self.cost, global_step = self.global_steps)
# accuracy and init variables
self.correct_pred = tf.equal(tf.argmax(self.results,1), tf.argmax(self.target,1))
self.accuracy = tf.reduce_mean(tf.cast(self.correct_pred, tf.float32))