RNN与LSTM
RNN网络是在传统神经网络的基础上加入了记忆的成分。对于RNN模型来说,序列被看做一系列随着时间步长递进的事件序列。这里的时间步长并不是真实世界中所指的时间,而是指序列中的位置。RNN模型的特殊结构可以让他处理相互依赖的时间序列及变长数据。长期依赖对于文本理解是不可回避的问题,但普通RNN结构并不能很好的处理这个问题,由于RNN的参数共享,在状态传递的过程中会发生梯度消失或爆炸的问题。LSTM就是为了解决长期依赖问题而产生的。与普通RNN相比,最主要的改进就是多出了三个门控制器:输入门、输出门、遗忘门。
有关RNN与LSTM的具体数学推导可见相关技术博客,这里不做详细阐述。
分类
这里用mnist数据集来做RNN的分类。RNN通常的输入是三维张量[batch_size, step_time, cell.input_size],这里把28*28的图片的每一行作为输入,28行作为step_time,用128张图片作为一个batch。
首先导入mnist数据集,设置超参数和占位符。
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('MNIST_data/', one_hot=True)
lr = 0.001
n_input = 28
n_step = 28
n_digit = 10
n_cell = 128
n_batch = 128
train_times = 100000
x = tf.placeholder(tf.float32, [None, n_step, n_input])
y = tf.placeholder(tf.float32, [None, n_digit])
这里设计LSTM网络来对图片分类,LSTM的核心是一个隐藏的神经层cell,包括各种门的参数和激活函数,在cell的前后,还各需要输入和输出的网络层。由于输入是三维的张量,在进行输入时,需要将其reshape成二维张量[batch_size*step_time, cell.input_size],再进行权重计算。
x_new = tf.reshape(x, [-1, n_input])
cell_in = tf.layers.dense(x_new, n_cell)
cell_in = tf.reshape(cell_in, [-1, n_step, n_cell])
现在TensorFlow1.2将隐层网络的设计进行了封装,可以直接调用tf.layers.dense.如下:
dense(
inputs,
units,
activation=None,
use_bias=True,
kernel_initializer=None,
bias_initializer=tf.zeros_initializer(),
kernel_regularizer=None,
bias_regularizer=None,
activity_regularizer=None,
trainable=True,
name=None,
reuse=None
)
inputs是输入的张量,units是神经元的个数,activation是激活函数,默认没有激活函数。这里只需要将输入进行线性组合,所以不需要激活函数。
然后设计LSTM cell。
cell = tf.contrib.rnn.BasicLSTMCell(n_cell)
# Args:
# num_units: int, The number of units in the LSTM cell.
# forget_bias: float, The bias added to forget gates (see above).
# state_is_tuple: If True, accepted and returned states are 2-tuples of the c_state and m_state.
# If False, they are concatenated along the column axis. The latter behavior will soon be deprecated.
# activation: Activation function of the inner states. Default: tanh.
# reuse: (optional) Python boolean describing whether to reuse variables
# in an existing scope. If not True, and the existing scope already has
# the given variables, an error is raised.
init_state = cell.zero_state(n_batch, dtype=tf.float32)
output, state = tf.nn.dynamic_rnn(
cell, cell_in, initial_state=init_state, time_major=False)
调用tf.contrib.rnn.BasicLSTMCell设计cell。num_units是cell中神经元的个数,forget_bias默认为1,表示遗忘门的初始值为1,表示遗忘之前的输入联系。state_is_tuple默认为true,表示输出的state是一个tuple,包含两个list,state[0]是cell中的状态,cell[1]是输出的状态。开始时需要初始化状态,如第二行代码,然后调用tf.nn.dynamic_rnn得到RNN层的结果,output的shape是[batch_size, step_time, cell],state的shape是[batch_size, cell],cell表示RNN中的神经元个数。
cell后再设计一个输出层,与上类似
cell_out = tf.layers.dense(state[1], n_digit)
这里,使用的是state[1],也可以使用output,但是output是三维张量,对应了每个step的输出,所以需要shape之后再用output[-1].
网络层设计好之后,就是模型的代价函数设计,训练和评估了。
loss = tf.reduce_mean(
tf.nn.softmax_cross_entropy_with_logits(labels=y, logits=cell_out))
train = tf.train.AdamOptimizer(lr).minimize(loss)
acc = tf.reduce_mean(
tf.cast(tf.equal(tf.argmax(y, 1), tf.argmax(cell_out, 1)), tf.float32))
with tf.Session() as sess:
tf.global_variables_initializer().run()
# print(sess.run(output, {x: mnist.train.images[
# :128].reshape([128, n_step, n_input])}).shape)
# output.shape:n_batch,n_step,n_cell >>= time_major=False
# print(sess.run(state[1], {x: mnist.train.images[
# :128].reshape([128, n_step, n_input])}).shape)
# state[1].shape:n_batch,n_cell
for i in range(train_times):
xs, ys = mnist.train.next_batch(n_batch)
xs = xs.reshape([n_batch, n_step, n_input])
sess.run(train, {x: xs, y: ys})
if i % 200 == 0:
print(sess.run(acc, {x: xs, y: ys}))
note
- 在图片分类时,由于每一张图片前后没有联系,所以在初始化状态,使初始状态为0后,并不需要改变,但在一些时间序列问题上,每一个batch相互联系,所以在第一次batch初始化状态后,使后一次的状态是前一次的输出状态state。
- RNN的输入是[batch_size, step_time, cell.input_size],在每一次batch后得到一个输出结果[batch_size,output],若选择dynamic_rnn return的output进行下一步计算,则输出结果为[batch_size, step_time, output],选择output[-1]时,结果与用state[1]一致。