使用MNIST數據集,在TensorFlow上實現基礎LSTM網絡

 2017-09-30 07:17:42.0

選自GitHub

參與:劉曉坤、路雪

本文介紹瞭如何在 TensorFlow 上實現基礎 LSTM 網絡的詳細過程。作者選用了 MNIST 數據集,本文詳細介紹了實現過程。

長短期記憶(LSTM)是目前循環神經網絡最普遍使用的類型,在處理時間序列數據時使用最為頻繁。關於 LSTM 的更加深刻的洞察可以看看這篇優秀的博客:http://colah.github.io/posts/2015-08-Understanding-LSTMs/。

我們的目的

這篇博客的主要目的就是使讀者熟悉在 TensorFlow 上實現基礎 LSTM 網絡的詳細過程。

我們將選用 MNIST 作為數據集。

fromtensorflow.examples.tutorials.mnist importinput_data

mnist =input_data.read_data_sets("/tmp/data/",one_hot=True)

MNIST 數據集

MNIST 數據集包括手寫數字的圖像和對應的標籤。我們可以根據以下內置功能從 TensorFlow 上下載並讀取數據。

from tensorflow.examples.tutorials.mnist import input_data

mnist = input_data.read_data_sets("/tmp/data/", one_hot=True)

數據被分成 3 個部分:

1. 訓練數據(mnist.train):55000 張圖像

2. 測試數據(mnist.test):10000 張圖像

3. 驗證數據(mnist.validation):5000 張圖像

數據的形態

討論一下 MNIST 數據集中的訓練數據的形態。數據集的這三個部分的形態都是一樣的。

訓練數據集包括55000 張28x28 像素的圖像,這些784(28x28)像素值被展開成一個維度為784 的單一向量,所有55000 個像素向量(每個圖像一個)被儲存為形態為(55000,784)的numpy 數組,並命名為mnist.train.images。

所有這 55000 張圖像都關聯了一個類別標籤(表示其所屬類別),一共有 10 個類別(0,1,2...9),類別標籤使用獨熱編碼的形式表示。因此標籤將作為形態為 (55000,10) 的數組保存,並命名為 mnist.train.labels。

為什麼要選擇 MNIST?

LSTM 通常用來解決複雜的序列處理問題,比如包含了 NLP 概念(詞嵌入、編碼器等)的語言建模問題。這些問題本身需要大量理解,那麼將問題簡化並集中於在 TensorFlow 上實現 LSTM 的細節(比如輸入格式化、LSTM 單元格以及網絡結構設計),會是個不錯的選擇。

MNIST 就正好提供了這樣的機會。其中的輸入數據是一個像素值的集合。我們可以輕易地將其格式化,將注意力集中在 LSTM 實現細節上。

實現

在動手寫代碼之前,先規劃一下實現的藍圖,可以使寫代碼的過程更加直觀。

VANILLA RNN

循環神經網絡按時間軸展開的時候,如下圖所示:


圖中:

1.x_t 代表時間步 t 的輸入;

2.s_t 代表時間步 t 的隱藏狀態,可看作該網絡的「記憶」;

3.o_t 作為時間步 t 時刻的輸出;

4.U、V、W 是所有時間步共享的參數,共享的重要性在於我們的模型在每一時間步以不同的輸入執行相同的任務。

當把 RNN 展開的時候,網絡可被看作每一個時間步都受上一時間步輸出影響(時間步之間存在連接)的前饋網絡。

兩個注意事項

為了更順利的進行實現,需要清楚兩個概念的含義:

1.TensorFlow 中 LSTM 單元格的解釋;

2. 數據輸入 TensorFlow RNN 之前先格式化。

TensorFlow 中 LSTM 單元格的解釋

在 TensorFlow 中,基礎的 LSTM 單元格聲明為:

tf.contrib.rnn.BasicLSTMCell(num_units)

這裡,num_units 指一個 LSTM 單元格中的單元數。 num_units 可以比作前饋神經網絡中的隱藏層,前饋神經網絡的隱藏層的節點數量等於每一個時間步中一個 LSTM 單元格內 LSTM 單元的 num_units 數量。下圖可以幫助直觀理解:


每一個 num_units LSTM 單元都可以看作一個標準的 LSTM 單元:


以上圖表來自博客(地址: http://colah.github.io/posts/2015-08-Understanding-LSTMs/),該博客有效介紹了 LSTM 的概念。

數據輸入 TensorFlow RNN 之前先格式化

在 TensorFlow 中最簡單的 RNN 形式是 static_rnn,在 TensorFlow 中定義如下:

tf.static_rnn(cell,inputs)

雖然還有其它的注意事項,但在這裡我們僅關注這兩個。

inputs 引數接受形態為 [batch_size,input_size] 的張量列表。列表的長度為將網絡展開後的時間步數,即列表中每一個元素都分別對應網絡展開的時間步。比如在 MNIST 數據集中,我們有 28x28 像素的圖像,每一張都可以看成擁有 28 行 28 個像素的圖像。我們將網絡按 28 個時間步展開,以使在每一個時間步中,可以輸入一行 28 個像素(input_size),從而經過 28 個時間步輸入整張圖像。給定圖像的 batch_size 值,則每一個時間步將分別收到 batch_size 個圖像。詳見下圖說明:


由 static_rnn 生成的輸出是一個形態為 [batch_size,n_hidden] 的張量列表。列表的長度為將網絡展開後的時間步數,即每一個時間步輸出一個張量。在這個實現中我們只需關心最後一個時間步的輸出,因為一張圖像的所有行都輸入到 RNN,預測即將在最後一個時間步生成。

現在,所有的困難部分都已經完成,可以開始寫代碼了。只要理清了概念,寫代碼過程是很直觀的。

代碼

在開始的時候,先導入一些必要的依賴關係、數據集,並聲明一些常量。設定 batch_size=128 、 num_units=128。

importtensorflow astf

fromtensorflow.contrib importrnn

#import mnist dataset

fromtensorflow.examples.tutorials.mnist importinput_data

mnist=input_data.read_data_sets("/tmp/data/",one_hot=True)

#define constants

#unrolled through 28 time steps

time_steps=28

#hidden LSTM units

num_units=128

#rows of 28 pixels

n_input=28

#learning rate for adam

learning_rate=0.001

#mnist is meant to be classified in 10 classes(0-9).

n_classes=10

#size of batch

batch_size=128

現在設置佔位、權重以及偏置變量(用於將輸出的形態從 [batch_size,num_units] 轉換為 [batch_size,n_classes]),從而可以預測正確的類別。

#weights and biases of appropriate shape to accomplish above task

out_weights=tf.Variable(tf.random_normal([num_units,n_classes]))

out_bias=tf.Variable(tf.random_normal([n_classes]))

#defining placeholders

#input image placeholder

x=tf.placeholder("float",[None,time_steps,n_input])

#input label placeholder

y=tf.placeholder("float",[None,n_classes])

現在我們得到了形態為 [batch_size,time_steps,n_input] 的輸入,我們需要將其轉換成形態為 [batch_size,n_inputs] 、長度為 time_steps 的張量列表,從而可以將其輸入 static_rnn。

#processing the input tensor from [batch_size,n_steps,n_input] to "time_steps" number of [batch_size,n_input] tensors

input=tf.unstack(x ,time_steps,1)

現在我們可以定義網絡了。我們將利用 BasicLSTMCell 的一個層,將我們的 static_rnn 從中提取出來。

#defining the network

lstm_layer=rnn.BasicLSTMCell(num_units,forget_bias=1)

outputs,_=rnn.static_rnn(lstm_layer,input,dtype="float32")

我們只考慮最後一個時間步的輸入,從中生成預測。

#converting last output of dimension [batch_size,num_units] to [batch_size,n_classes] by out_weight multiplication

prediction=tf.matmul(outputs[-1],out_weights)+out_bias

定義損失函數、優化器和準確率。

#loss_function

loss=tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=prediction,labels=y))

#optimization

opt=tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(loss)

#model evaluation

correct_prediction=tf.equal(tf.argmax(prediction,1),tf.argmax(y,1))

accuracy=tf.reduce_mean(tf.cast(correct_prediction,tf.float32))

現在我們已經完成定義,可以開始運行了。

#initialize variables

init=tf.global_variables_initializer()

withtf.Session()assess:

sess.run(init)

iter=1

whileiter<800:

batch_x,batch_y=mnist.train.next_batch(batch_size=batch_size)

batch_x=batch_x.reshape((batch_size,time_steps,n_input))

sess.run(opt,feed_dict={x:batch_x,y:batch_y})

ifiter %10==0:

acc=sess.run(accuracy,feed_dict={x:batch_x,y:batch_y})

los=sess.run(loss,feed_dict={x:batch_x,y:batch_y})

print("For iter ",iter)

print("Accuracy ",acc)

print("Loss ",los)

print("__________________")

iter=iter+1

需要注意的是我們的每一張圖像在開始時被平坦化為 784 維的單一向量,函數 next_batch(batch_size) 必須返回這些 784 維向量的 batch_size 批次數。因此它們的形態要被改造成 [batch_size,time_steps,n_input],從而可以被我們的佔位符接受。

我們還可以計算模型的準確率:

#calculating test accuracy

test_data =mnist.test.images[:128].reshape((-1,time_steps,n_input))

test_label =mnist.test.labels[:128]

print("Testing Accuracy:",sess.run(accuracy,feed_dict={x:test_data,y:test_label}))

在運行的時候,模型的測試準確率為 99.21%。

這篇博客旨在讓讀者熟悉 TensorFlow 中 RNN 的實現細節。我們將會在 TensorFlow 中建立更加複雜的模型以更有效的利用 RNN。敬請期待!

原文鏈接: https://jasdeep06.github.io/posts/Understanding-LSTM-in-Tensorflow-MNIST/

文章來源:機器之心