芝麻HTTP:TensorFlow LSTM MNIST分類

本節來介紹一下使用 RNN 的 LSTM 來作 MNIST 分類的方法,RNN 相比 CNN 來講,速度可能會慢,但能夠節省更多的內存空間。git

初始化

首先咱們能夠先初始化一些變量,如學習率、節點單元數、RNN 層數等:app

learning_rate = 1e-3
num_units = 256
num_layer = 3
input_size = 28
time_step = 28
total_steps = 2000
category_num = 10
steps_per_validate = 100
steps_per_test = 500
batch_size = tf.placeholder(tf.int32, [])
keep_prob = tf.placeholder(tf.float32, [])

而後還須要聲明一下 MNIST 數據生成器:學習

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('MNIST_data/', one_hot=True)

接下來常規聲明一下輸入的數據,輸入數據用 x 表示,標註數據用 y_label 表示:spa

x = tf.placeholder(tf.float32, [None, 784])
y_label = tf.placeholder(tf.float32, [None, 10])

這裏輸入的 x 維度是 [None, 784],表明 batch_size 不肯定,輸入維度 784,y_label 同理。code

接下來咱們須要對輸入的 x 進行 reshape 操做,由於咱們須要將一張圖分爲多個 time_step 來輸入,這樣才能構建一個 RNN 序列,因此這裏直接將 time_step 設成 28,這樣一來 input_size 就變爲了 28,batch_size 不變,因此reshape 的結果是一個三維的矩陣:orm

x_shape = tf.reshape(x, [-1, time_step, input_size])

RNN 層

接下來咱們須要構建一個 RNN 模型了,這裏咱們使用的 RNN Cell 是 LSTMCell,並且要搭建一個三層的 RNN,因此這裏還須要用到 MultiRNNCell,它的輸入參數是 LSTMCell 的列表。對象

因此咱們能夠先聲明一個方法用於建立 LSTMCell,方法以下:內存

def cell(num_units):
    cell = tf.nn.rnn_cell.BasicLSTMCell(num_units=num_units)
    return DropoutWrapper(cell, output_keep_prob=keep_prob)

這裏還加入了 Dropout,來減小訓練過程當中的過擬合。input

接下來咱們再利用它來構建多層的 RNN:it

cells = tf.nn.rnn_cell.MultiRNNCell([cell(num_units) for _ in range(num_layer)])

注意這裏使用了 for 循環,每循環一次新生成一個 LSTMCell,而不是直接使用乘法來擴展列表,由於這樣會致使 LSTMCell 是同一個對象,致使構建完 MultiRNNCell 以後出現維度不匹配的問題。

接下來咱們須要聲明一個初始狀態:

h0 = cells.zero_state(batch_size, dtype=tf.float32)

而後接下來調用 dynamic_rnn() 方法便可完成模型的構建了:

output, hs = tf.nn.dynamic_rnn(cells, inputs=x_shape, initial_state=h0)

這裏 inputs 的輸入就是 x 作了 reshape 以後的結果,初始狀態經過 initial_state 傳入,其返回結果有兩個,一個 output 是全部 time_step 的輸出結果,賦值爲 output,它是三維的,第一維長度等於 batch_size,第二維長度等於 time_step,第三維長度等於 num_units。另外一個 hs 是隱含狀態,是元組形式,長度即 RNN 的層數 3,每個元素都包含了 c 和 h,即 LSTM 的兩個隱含狀態。

這樣的話 output 的最終結果能夠取最後一個 time_step 的結果,因此可使用:

output = output[:, -1, :]

或者直接取隱藏狀態最後一層的 h 也是相同的:

h = hs[-1].h

在此模型中,兩者是等價的。但注意若是用於文本處理,可能因爲文本長度不一,而 padding,致使兩者不一樣。

輸出層

接下來咱們再作一次線性變換和 Softmax 輸出結果便可:

# Output Layer
w = tf.Variable(tf.truncated_normal([num_units, category_num], stddev=0.1), dtype=tf.float32)
b = tf.Variable(tf.constant(0.1, shape=[category_num]), dtype=tf.float32)
y = tf.matmul(output, w) + b
# Loss
cross_entropy = tf.nn.softmax_cross_entropy_with_logits(labels=y_label, logits=y)

這裏的 Loss 直接調用了 softmax_cross_entropy_with_logits 先計算了 Softmax,而後計算了交叉熵。

訓練和評估

最後再定義訓練和評估的流程便可,在訓練過程當中每隔必定的 step 就輸出 Train Accuracy 和 Test Accuracy:

# Train
train = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cross_entropy)

# Prediction
correction_prediction = tf.equal(tf.argmax(y, axis=1), tf.argmax(y_label, axis=1))
accuracy = tf.reduce_mean(tf.cast(correction_prediction, tf.float32))

# Train
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    for step in range(total_steps + 1):
        batch_x, batch_y = mnist.train.next_batch(100)
        sess.run(train, feed_dict={x: batch_x, y_label: batch_y, keep_prob: 0.5, batch_size: batch_x.shape[0]})
        # Train Accuracy
        if step % steps_per_validate == 0:
            print('Train', step, sess.run(accuracy, feed_dict={x: batch_x, y_label: batch_y, keep_prob: 0.5,
                                                               batch_size: batch_x.shape[0]}))
        # Test Accuracy
        if step % steps_per_test == 0:
            test_x, test_y = mnist.test.images, mnist.test.labels
            print('Test', step,
                  sess.run(accuracy, feed_dict={x: test_x, y_label: test_y, keep_prob: 1, batch_size: test_x.shape[0]}))

運行

直接運行以後,只訓練了幾輪就能夠達到 98% 的準確率:

Train 0 0.27
Test 0 0.2223
Train 100 0.87
Train 200 0.91
Train 300 0.94
Train 400 0.94
Train 500 0.99
Test 500 0.9595
Train 600 0.95
Train 700 0.97
Train 800 0.98

能夠看出來 LSTM 在作 MNIST 字符分類的任務上仍是比較有效的。

相關文章
相關標籤/搜索