第26节讨论了如何将诗集数据转换为数字信息,便于训练 长短期记忆循环神经网络(LSTM RNN)
。第25节则介绍了 LSTM RNN 在 tensorflow 中常用的函数。本节将在此基础上,建立网络,并训练之。
建立多层 LSTM 循环神经网络
先封装一个神经元,使用dropout
防止过拟合。
def GetOneCell(lstm_size, keep_prob):
lstm = tf.nn.rnn_cell.BasicLSTMCell(lstm_size)
drop = tf.nn.rnn_cell.DropoutWrapper(lstm, output_keep_prob=keep_prob)
return drop
封装好GetOneCell
方法,就可以很方便的建立多层 LSTM 网络了。
cell = tf.nn.rnn_cell.MultiRNNCell(
[get_a_cell(lstm_size, keep_prob) for _ in range(num_layers)]
)
建立好以后,就可以用tf.nn.dynamic_rnn
方法展开时间维度了,该方法的说明可以参考第25节。
initial_state = cell.zero_state(num_seqs, tf.float32) #初始状态使用零状态
lstm_outputs, final_state = tf.nn.dynamic_rnn(cell, lstm_inputs, initial_state=initial_state)
得到的lstm_outputs
其实是 LSTM 的隐状态层 h
,所以还需要一层 softmax 计算概率。建立 softmax 层就相当于一层全连接层,首先需要将lstm_outputs
的形状 reshape。这里 reshape 成 [-1, lstm_size] 形状,即只保证有 lstm_size 列,不关心到底多少行。因为建立的是多层 LSTM
,所以在 reshape 前,先将第二维合并。
lstm_outputs
的第一维是 batch_size 即句子的条数,第二维是 诗句的长度,第三维是 lstm size。
代码如下,建立 softmax
层非常熟,不用细说了。
seq_output = tf.concat(lstm_outputs, 1)
x = tf.reshape(seq_output, [-1, lstm_size])
softmax_w = tf.Variable(tf.truncated_normal([lstm_size, num_classes], stddev=0.1))
softmax_b = tf.Variable(tf.zeros(num_classes))
logits = tf.matmul(x, softmax_w) + softmax_b
proba_prediction = tf.nn.softmax(logits)
建立损失函数 loss
这里仍然是使用tf.nn.softmax_cross_entropy_with_logits
方法,该方法的使用前面几节介绍的非常清楚,可以翻回去看看。在计算 loss 前,需要先将参考值做点处理:建立one-hot
型标签后,reshape 为 logits 的形状即可。具体代码如下
def build_loss():
y_one_hot = tf.one_hot(targets, num_classes)
y_reshaped = tf.reshape(y_one_hot, logits.get_shape())
lossi = tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=y_reshaped)
loss = tf.reduce_mean(lossi)
对应的优化器可以如下建立:
def build_optimizer():
# 使用clipping gradients
tvars = tf.trainable_variables()
grads, _ = tf.clip_by_global_norm(tf.gradients(self.loss, tvars), self.grad_clip)
train_op = tf.train.AdamOptimizer(self.learning_rate)
self.optimizer = train_op.apply_gradients(zip(grads, tvars))
建立数据输入
网络和损失函数、优化器都建立好了,接下来就可以建立数据输入了。使用placeholder
,数据通过 feed 传入。因为打算训练的是诗文的数据,所以需要使用embedding层。
inputs = tf.placeholder(tf.int32, shape=(
num_seqs, num_steps))
targets = tf.placeholder(tf.int32, shape=(
num_seqs, num_steps))
keep_prob = tf.placeholder(tf.float32)
# 对于中文,需要使用embedding层
# 英文字母没有必要用embedding层
with tf.device("/cpu:0"):
embedding = tf.get_variable('embedding', [num_classes, embedding_size])
lstm_inputs = tf.nn.embedding_lookup(embedding, inputs)
训练方法
以上几个模块建立好了以后,定义训练方法就非常简单了。总体过程就是利用第26节建立的数据转换方法,将诗文转换为数字信息,然后再经过embedding层,就可以feed给建立好的网络了。每隔一段训练次数,就保存一次训练结果,防止训练意外中断,又得从头训练,这点之前的有篇博客深有体会。
def train(batch_generator, max_steps, save_path, save_every_n, log_every_n):
session = tf.Session()
with session as sess:
sess.run(tf.global_variables_initializer())
# Train network
step = 0
new_state = sess.run(initial_state)
for x, y in batch_generator:
step += 1
feed = {inputs: x,
targets: y,
keep_prob: train_keep_prob,
initial_state: new_state}
batch_loss, new_state, _ = sess.run([loss,
final_state,
optimizer],
feed_dict=feed)
if (step % save_every_n == 0):
self.saver.save(sess, os.path.join(save_path, 'model'), global_step=step)
if step >= max_steps:
break
self.saver.save(sess, os.path.join(save_path, 'model'), global_step=step)
全部代码
代码和诗集数据可以点击下面链接下载,含义本节已经说得比较清楚。
训练网络
执行脚本,进行训练,得到以下结果:
$ python train.py --use_embedding --input_file data/poetry.txt --name poetry --learning_rate 0.005 --num_steps 26 --num_seqs 32 --max_steps 10000
step: 10/10000... loss: 6.5859... 0.2540 sec/batch
step: 20/10000... loss: 6.5014... 0.2491 sec/batch
step: 30/10000... loss: 6.3253... 0.2469 sec/batch
step: 40/10000... loss: 6.1925... 0.2984 sec/batch
step: 50/10000... loss: 5.9969... 0.2483 sec/batch
step: 60/10000... loss: 5.8543... 0.3229 sec/batch
...
发现 loss 在不停的变小,这是网络能够正常工作的必要条件,本节就到这里。下一节,将利用训练好的网络作诗。
本节主要参考《21个项目玩转深度学习》