tensorflow之雙向迴圈神經網路
一、定義
儘管從多層感知器(MLP)到迴圈神經網路(RNN)的擴充套件看起來微不足道,但是這對於序列的學習具有深遠意義。迴圈神經網路(RNN)的使用是用來處理序列資料的。在傳統的神經網路模型中,層與層之間是全連線的,每層之間的節點是無連線的。但是這種普通的神經網路對於很多問題是無能為力的。比如,預測句子的下一個單詞是什麼,一般需要用到前面的單詞,因為一個句子中前後單詞並不是獨立的。迴圈神經網路(RNN)指的是一個序列當前的輸出與之前的輸出也有關。具體的表現形式為網路會對前面的資訊進行記憶,儲存在網路的內部狀態中,並應用於當前輸出的計算中,即隱含層之間的節點不再無連線而是有連結的,並且隱含層的輸入不僅包含輸入層的輸出還包含上一時刻隱含層的輸出。理論上,迴圈神經網路能夠對任何長度的序列資料進行處理,但是在實踐中,為了減低複雜性往往假設當前的狀態只與前面的幾個狀態相關。
在迴圈神經網路中(RNN),有一條單向流動的資訊流是從輸入單元到達隱含單元的,與此同時,另一條單向流動的資訊流是從隱含單元到達輸出單元。在某些情況下,迴圈神經網路(RNN)會打破後者的限制,引導資訊從輸出單元返回隱含單元,這些被稱為“Back projections”,並且隱藏層的輸入還包括上一層隱藏層的輸出,即隱藏層內的節點是可以自連也可以互連的。
對於上圖網路的計算過程如下:
1)xt 表示第t(t=1, 2, 3, … )步的輸入
2)st 為隱含層第t步的狀態,它是神經的記憶單元。st 根據當前輸入層和上一步隱含層的輸出進行計算
st=f(Uxt+Wst−1) ,其中f一般為非線性的啟用函式,如relu或tanh,在計算s0 時,即第一個的隱含狀態,需要用到 st−1,但其並不存在,在現實中一般被設定為0向量。
3) ot 是第t步的輸出,stot=softmax(Vst)
需要注意的是:
隱含層狀態st 被認為是網路的記憶單元。st 包含了前面所有步的隱含層狀態,而輸出層的ot只與當前步的st有關。在實踐中,為了降低網路的複雜度,往往st只包含前面若干步而不是所有步的隱含層輸出。
迴圈神經網路(RNN)的關鍵之處在於隱含層,隱含層能夠捕捉序列的資訊。
Bidirectional Recurrent Neural Network(BRNN)
如果能像訪問過去的上下文資訊一樣,訪問未來的上下文,這樣對於許多序列標註任務是非常有益的。例如,在最特殊分類的時候,如果能像知道這個字母之前的字母一樣,知道將要來的字母,這將非常有幫助。
然而,由於標準的迴圈神經網路(RNN)在時序上處理序列,他們往往忽略了未來的上下文資訊。一種很顯而易見的解決辦法是在輸入和目標之間新增延遲,進而可以給網路一些時步來加入未來的上下文資訊,也就是加入M時間幀的未來資訊來一起預測輸出。理論上,M可以非常大來捕獲所有未來的可用資訊,但事實上發現如果M過大,預測結果將會變差。這是因為網路把精力都集中記憶大量的輸入資訊,而導致將不同輸入向量的預測只是聯合的建模能力下降。因此,M的大小需要手動來調節。
雙向迴圈神經網路(BRNN)的基本思想是提出每一個訓練序列向前和向後分別是兩個迴圈神經網路(RNN),而且這兩個都連線著一個輸出層。這個結構提供給輸出層輸入序列中每一個點的完整的過去和未來的上下文資訊。下圖展示的是一個沿著時間展開的雙向迴圈神經網路。六個獨特的權值在每一個時步被重複利用,六個權值分別對應:輸入到向前和向後隱含層(w1,w3),隱含層到隱含層自己(w2,w5),向前和向後隱含層到輸出層(w4,w6)。值得注意的是:向前和向後隱含層之間沒有資訊流,這保證了展開圖是非迴圈的。
import tensorflow as tf
from tensorflow.contrib import rnn
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets(“/tmp/data/”, one_hot=True)
”’
To classify images using a bidirectional recurrent neural network, we consider
every image row as a sequence of pixels. Because MNIST image shape is 28*28px,
we will then handle 28 sequences of 28 steps for every sample.
”’
learning_rate = 0.001
training_iters = 100000
batch_size = 128
display_step = 10
n_input = 28 # MNIST data input (img shape: 28*28)
n_steps = 28 # timesteps
n_hidden = 128 # hidden layer num of features
n_classes = 10 # MNIST total classes (0-9 digits)
x = tf.placeholder(“float”, [None, n_steps, n_input])
y = tf.placeholder(“float”, [None, n_classes])
weights = {
# Hidden layer weights => 2*n_hidden because of forward + backward cells
‘out’: tf.Variable(tf.random_normal([2*n_hidden, n_classes]))
}
biases = {
‘out’: tf.Variable(tf.random_normal([n_classes]))
}
def BiRNN(x, weights, biases):
# Prepare data shape to match `bidirectional_rnn` function requirements
# Current data input shape: (batch_size, n_steps, n_input)
# Required shape: 'n_steps' tensors list of shape (batch_size, n_input)
# Unstack to get a list of 'n_steps' tensors of shape (batch_size, n_input)
x = tf.unstack(x, n_steps, 1)
# Define lstm cells with tensorflow
# Forward direction cell
lstm_fw_cell = rnn.BasicLSTMCell(n_hidden, forget_bias=1.0)
# Backward direction cell
lstm_bw_cell = rnn.BasicLSTMCell(n_hidden, forget_bias=1.0)
# Get lstm cell output
try:
outputs, _, _ = rnn.static_bidirectional_rnn(lstm_fw_cell, lstm_bw_cell, x,
dtype=tf.float32)
except Exception: # Old TensorFlow version only returns outputs not states
outputs = rnn.static_bidirectional_rnn(lstm_fw_cell, lstm_bw_cell, x,
dtype=tf.float32)
# Linear activation, using rnn inner loop last output
return tf.matmul(outputs[-1], weights['out']) + biases['out']
pred = BiRNN(x, weights, biases)
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=pred, labels=y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost)
correct_pred = tf.equal(tf.argmax(pred,1), tf.argmax(y,1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
init = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
step = 1
# Keep training until reach max iterations
while step * batch_size < training_iters:
batch_x, batch_y = mnist.train.next_batch(batch_size)
# Reshape data to get 28 seq of 28 elements
batch_x = batch_x.reshape((batch_size, n_steps, n_input))
# Run optimization op (backprop)
sess.run(optimizer, feed_dict={x: batch_x, y: batch_y})
if step % display_step == 0:
# Calculate batch accuracy
acc = sess.run(accuracy, feed_dict={x: batch_x, y: batch_y})
# Calculate batch loss
loss = sess.run(cost, feed_dict={x: batch_x, y: batch_y})
print(“Iter ” + str(step*batch_size) + “, Minibatch Loss= ” + \
“{:.6f}”.format(loss) + “, Training Accuracy= ” + \
“{:.5f}”.format(acc))
step += 1
print(“Optimization Finished!”)
# Calculate accuracy for 128 mnist test images
test_len = 128
test_data = mnist.test.images[:test_len].reshape((-1, n_steps, n_input))
test_label = mnist.test.labels[:test_len]
print("Testing Accuracy:", \
sess.run(accuracy, feed_dict={x: test_data, y: test_label}))