本文为LSTM在mnist手写数字识别数据集上的示例实验,综合整理了网上的博客,完整代码详见GitHub:https://github.com/vivianLL/LSTMonMNIST
The MNIST data-set
MNIST data-set由一些黑白照片集合组成,每张照片包含手写的数字。图像被格式成28*28像素,并可表示成一个数值矩阵。集合中60000张照片用来训练模型,10000张照片用来测试模型。MNIST data-set可通过网络在MNIST数据库中获得。
为方便下载数据,你可以使用input_data.py脚本,该文件也已放在github上。当在TensorFlow中编程实现神经网络时,可直接通过这个脚本下载数据到当前目录下。在你的应用程序中,只需要导入并调用如下代码:
mnist = input_data.read_data_sets("E:/Anaconda3/workspace/MNIST_data/", one_hot=True)
当执行完这两行命令后,mnist.train会包含了全部的训练数据,mnist.test包含了全部的测试数据集。正如我之前所说的,每一个元素都由一张照片组成,用”xs”来表示,它的标注用“ys”表示,这可以很方便的表示处理代码。请记住,所有数据集,训练集与测试集都包含了“xs”与“ys”;同时,mnist.train.images表示训练照片,mnist.train.labels表示训练标签。
通过调用tf.Variable来创建并初始化;本例子中采用了随机初始化,也可初始化为0.
# define w and b
weights = {
'in': tf.Variable(tf.random_normal([n_inputs, n_hidden_units])),
'out': tf.Variable(tf.random_normal([n_hidden_units, n_classes]))
}
biases = {
'in': tf.Variable(tf.constant(0.1, shape=[n_hidden_units, ])),
'out': tf.Variable(tf.constant(0.1, shape=[n_classes, ]))
}
本例子中使用了MNIST,同样需要创建一个2维的tensor来保留这些点的信息和1维tensor来保留标签,代码如下:
# define placeholder for input
x = tf.placeholder(tf.float32, [None, n_steps, n_inputs])
y = tf.placeholder(tf.float32, [None, n_classes])
模型构建
对于单层LSTM:
基础的RNNCell有一个很明显的问题:对于单个的RNNCell,我们使用它的call函数进行运算时,只是在序列时间上前进了一步。比如使用x1、h0得到h1,通过x2、h1得到h2等。这样的h话,如果我们的序列长度为10,就要调用10次call函数,比较麻烦。对此,TensorFlow提供了一个tf.nn.dynamic_rnn
函数,使用该函数就相当于调用了n次call函数。即通过{h0,x1, x2, …., xn}直接得{h1,h2…,hn}。
outputs, final_state = tf.nn.dynamic_rnn(cell, X_in, initial_state=init_state, time_major=False)
得到的outputs就是time_steps步里所有的输出。它的形状为(batch_size, time_steps, cell.output_size)。final_state是最后一步的隐状态,它的形状为(batch_size, cell.state_size)。
def RNN(X, weights, biases):
# transpose the inputs shape from
X = tf.reshape(X, [-1, n_inputs])
# into hidden
X_in = tf.matmul(X, weights['in']) + biases['in']
X_in = tf.reshape(X_in, [-1, n_steps, n_hidden_units])
# cell
##########################################
# basic LSTM Cell.
cell = tf.contrib.rnn.BasicLSTMCell(n_hidden_units)
# lstm cell is divided into two parts (c_state, h_state)
init_state = cell.zero_state(batch_size, dtype=tf.float32)
outputs, final_state = tf.nn.dynamic_rnn(cell, X_in, initial_state=init_state, time_major=False) # time_major的意思是:是否steps为第一个参数,这里不是,则false
outputs = tf.unstack(tf.transpose(outputs, [1, 0, 2]))
results = tf.matmul(outputs[-1], weights['out']) + biases['out'] # shape = (128, 10)
return results
pred = RNN(x, weights, biases) # shape:(128, 10)
很多时候,单层RNN的能力有限,我们需要多层的RNN。将x输入第一层RNN的后得到隐层状态h,这个隐层状态就相当于第二层RNN的输入,第二层RNN的隐层状态又相当于第三层RNN的输入,以此类推。在TensorFlow中,可以使用tf.nn.rnn_cell.MultiRNNCell函数对RNNCell进行堆叠:
def attn_cell():
lstm_cell = tf.contrib.rnn.BasicLSTMCell(n_hiddens)
with tf.name_scope('lstm_dropout'):
return tf.contrib.rnn.DropoutWrapper(lstm_cell, output_keep_prob=keep_prob)
# 实现多层 LSTM
enc_cells = []
for i in range(0, n_layers):
enc_cells.append(attn_cell())
with tf.name_scope('lstm_cells_layers'):
mlstm_cell = tf.contrib.rnn.MultiRNNCell(enc_cells, state_is_tuple=True)
多层LSTM同样需要dynamic_rnn
来运行多步:
# 全零初始化 state
_init_state = mlstm_cell.zero_state(batch_size, dtype=tf.float32)
# dynamic_rnn 运行网络
outputs, states = tf.nn.dynamic_rnn(mlstm_cell, x, initial_state=_init_state, dtype=tf.float32, time_major=False)
模型训练
使用mnist.test数据集作为feed_dict参数来计算准确率:
train_result = sess.run(merged, feed_dict={x:batch_x, y:batch_y, keep_prob:1.0, batch_size:_batch_size})
test_result = sess.run(merged, feed_dict={x:test_x, y:test_y, keep_prob:1.0, batch_size:test_x.shape[0]})
模型评估
tf.argmax(y,1)函数会返回tensor中参数指定的维度中的最大值的索引。在效果上,tf.argmax(pred,1)是我们模型中输入数据预测的最大概率标签,tf.argmax(y,1)是实际的标签。通过tf.equal方法可以比较预测结果与实际结果是否相等。equal函数返回一个布尔列表。为得到哪些预测是正确的,我们可用如下代码将布尔值转换成浮点数:
correct_pred = tf.equal(tf.argmax(pred, 1), tf.argmax(y, 1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
例如,[True, False, True, True]会转换成[1,0,1,1],其平均值0.75代表了准确比例。
tensorboard可视化
数据序列化
TensorBoard 通过读取 TensorFlow 的事件文件来运行。TensorFlow 的事件文件包括了你会在 TensorFlow 运行中涉及到的主要数据。下面是 TensorBoard 中汇总数据(Summary data)的大体生命周期。
首先,创建你想汇总数据的 TensorFlow 图,然后再选择你想在哪个节点进行汇总(summary)操作。
比如,假设你正在训练一个卷积神经网络,用于识别 MNISt 标签。你可能希望记录学习速度(learning rate)的如何变化,以及目标函数如何变化。通过向节点附加scalar_summary操作来分别输出学习速度和期望误差。然后你可以给每个 scalary_summary 分配一个有意义的 标签,比如 ‘learning rate’ 和 ‘loss function’。
with tf.name_scope('loss'):
cost = tf.reduce_mean(-tf.reduce_sum(y * tf.log(pred),reduction_indices=[1]))
tf.summary.scalar('loss', cost)
with tf.name_scope('accuracy'):
accuracy = tf.metrics.accuracy(labels=tf.argmax(y, axis=1), predictions=tf.argmax(pred, axis=1))[1]
tf.summary.scalar('accuracy', accuracy)
或者你还希望显示一个特殊层中激活的分布,或者梯度权重的分布。可以通过分别附加 histogram_summary 运算来收集权重变量和梯度输出。
# weights and biases
with tf.name_scope('weights'):
Weights = tf.Variable(tf.truncated_normal([n_hiddens, n_classes],stddev=0.1), dtype=tf.float32, name='W')
tf.summary.histogram('output_layer_weights', Weights)
with tf.name_scope('biases'):
biases = tf.Variable(tf.random_normal([n_classes]), name='b')
tf.summary.histogram('output_layer_biases', biases)
所有可用的 summary 操作详细信息,可以查看summary_operation文档。
在TensorFlow中,所有的操作只有当你执行,或者另一个操作依赖于它的输出时才会运行。我们刚才创建的这些节点(summary nodes)都围绕着你的图像:没有任何操作依赖于它们的结果。因此,为了生成汇总信息,我们需要运行所有这些节点。这样的手动工作是很乏味的,因此可以使用tf.merge_all_summaries来将他们合并为一个操作。
merged = tf.summary.merge_all()
然后你可以执行合并命令,它会依据特点步骤将所有数据生成一个序列化的Summary protobuf对象。最后,为了将汇总数据写入磁盘,需要将汇总的protobuf对象传递给tf.train.Summarywriter。
SummaryWriter 的构造函数中包含了参数 logdir。这个 logdir 非常重要,所有事件都会写到它所指的目录下。此外,SummaryWriter 中还包含了一个可选择的参数 GraphDef。如果输入了该参数,那么 TensorBoard 也会显示你的图像。
现在已经修改了你的图,也有了 SummaryWriter,现在就可以运行你的神经网络了!如果你愿意的话,你可以每一步执行一次合并汇总,这样你会得到一大堆训练数据。这很有可能超过了你想要的数据量。你也可以每一百步执行一次合并汇总,或者如下面代码里示范的这样。
train_writer = tf.summary.FileWriter("logs//train",sess.graph)
test_writer = tf.summary.FileWriter("logs//test",sess.graph)
step = 1
for i in range(2000):
_batch_size = 128
batch_x, batch_y = mnist.train.next_batch(_batch_size)
sess.run(train_op, feed_dict={x:batch_x, y:batch_y, keep_prob:0.5, batch_size:_batch_size})
if (i + 1) % 100 == 0:
train_result = sess.run(merged, feed_dict={x:batch_x, y:batch_y, keep_prob:1.0, batch_size:_batch_size})
test_result = sess.run(merged, feed_dict={x:test_x, y:test_y, keep_prob:1.0, batch_size:test_x.shape[0]})
train_writer.add_summary(train_result,i+1)
test_writer.add_summary(test_result,i+1)
启动TensorBoard
运行训练代码,运行完后会产生一个log文件保存需要可视化的数据;
定位到log文件的保存目录上,并复制该目录;
打开cmd到终端,cd到log文件所在的目录;
输入tensorboard.exe --logdir="log文件所在目录"
打开浏览器,输入网址localhost:6006
,此时可以看到下图的可视化界面,tensorboard启动完成。
存在问题:
想输出测试集准确率,但加入如下代码会出现shape不匹配的错误:tensorflow.python.framework.errors_impl.InvalidArgumentError: ConcatOp : Dimensions of inputs should match: shape[0] = [10000,128] vs. shape[1] = [128,128] [[{{node rnn/while/basic_lstm_cell/concat}}]]
test_data = mnist.test.images.reshape([-1, n_steps, n_inputs])
test_label = mnist.test.labels
print("Testing Accuracy: ", sess.run(accuracy, feed_dict={x: test_data, y: test_label}))
x的shape是(?, 28, 28)
test_data的shape是(10000, 28, 28)
不知道怎么统一起来。
参考网址:
LSTM在MNIST手写数据集上做分类(代码中尺寸变换细节)
《TensorFlow实例》
TensorFlow 实现多层 LSTM 的 MNIST 分类 + 可视化
TensorFlow中RNN实现的正确打开方式
TensorFlow中文社区——TensorBoard:可视化学习