TensorFlow实现单层及多层LSTM的MNIST分类和可视化

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/vivian_ll/article/details/89096487

本文为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:可视化学习

猜你喜欢

转载自blog.csdn.net/vivian_ll/article/details/89096487