您的位置:首頁>手機>正文

教你從零開始在 TensorFlow 上搭建 RNN(完整代碼)!

RNN 是什麼?

遞迴神經網路,或者說 RNN,在資料能被按次序處理、資料點的不同排列亦會產生影響時就可以使用它。更重要的是,該次序可以是任意長度。

最直接的例子大概是一組數位的時間序列,根據此前的數值來預測接下來的數值。每個時間步(time-step)上,RNN 的輸入是當前數值以及一個靜態向量,後者用來表示神經網路在此前的不同時間步所“看到”的東西。該靜態向量是 RNN 的編碼存儲,初始值設為零。

RNN 處理系列資料的過程圖解

設置 

我們會創建一個簡單的 Echo-RNN,它能記住輸入資料並在幾個時間步之後與之呼應。首先要設置一些我們需要的限制,它們的意義下面會解釋。

from __future__ import print_function, division

import numpy as np

import tensorflow as tf

import matplotlib.pyplot as plt

num_epochs = 100

total_series_length = 50000

truncated_backprop_length = 15

state_size = 4

num_classes = 2

echo_step = 3

batch_size = 5

num_batches = total_series_length//batch_size//truncated_backprop_length

生成資料

現在生成訓練資料,輸入在本質上是一個隨機的二元向量。輸出會是輸入的“迴響”(echo),把 echo_step 步驟移到右邊。

def generateData():

x = np.array(np.random.choice(2, total_series_length, p=[0.5, 0.5]))

y = np.roll(x, echo_step)

y[0:echo_step] = 0

x = x.reshape((batch_size, -1)) # The first index changing slowest, subseries as rows

y = y.reshape((batch_size, -1))

return (x, y)

注意資料整形(data reshaping)步驟,這是為了將其裝入有 batch_size 行的矩陣。神經網路根據神經元權重來逼近損失函數的梯度,通過這種方式來進行訓練;該過程只會利用資料的一個小子集,即 mini-batch。資料整形把整個資料集裝入矩陣,然後分割為這些 mini-batch。

整形後的數據矩陣圖解。曲線箭頭用以表示換了行的相鄰時間步。淺灰色代表 0,深灰色代表 1。

創建計算圖

TensorFlow 的工作方式會首先創建一個計算圖,來確認哪些操作需要完成。計算圖的輸入和輸出一般是多維陣列,即張量(tensor)。計算圖或其中一部分,將被反覆運算執行。這既可以在 CPU、GPU,也可在遠端伺服器上執行。

變數和 placeholder

本教程中使用的兩個最基礎的 TensorFlow 資料結構是變數和 placeholder。每輪運行中,batch 資料會被喂給 placeholder,而後者是計算圖的“起始點”。另外,前一輪輸出的 RNN-state 會在 placeholder 中提供。

batchX_placeholder = tf.placeholder(tf.float32, [batch_size, truncated_backprop_length])

batchY_placeholder = tf.placeholder(tf.int32, [batch_size, truncated_backprop_length])

init_state = tf.placeholder(tf.float32, [batch_size, state_size])

神經網路的權重和偏差,被作為 TensorFlow 變數。這使得它們在每輪運行中保持一致,並對每次 batch 漸進式地更新。

W = tf.Variable(np.random.rand(state_size+1, state_size), dtype=tf.float32)

b = tf.Variable(np.zeros((1,state_size)), dtype=tf.float32)

W2 = tf.Variable(np.random.rand(state_size, num_classes),dtype=tf.float32)

b2 = tf.Variable(np.zeros((1,num_classes)), dtype=tf.float32)

下圖展示的是作為輸入的資料矩陣,現有的 batch——batchX_placeholder 在虛線長方形裡。正如我們後來看到的,這一 ”batch 視窗“在每輪運行向右移動了 truncated_backprop_length 規定的步數,這便是箭頭的意義。在下面的例子中,batch_size = 3, truncated_backprop_length = 3, and total_series_length = 36。注意這些數位只是出於視覺化目的,代碼中的數值並不一樣。在幾個數據點中,series order 指數以數字表示。

Unpacking

這一步,要做的是搭建計算圖中類似於真正的 RNN 計算的部分。首先,我們希望把 batch 資料分割為鄰近的時間步。

# Unpack columns

inputs_series = tf.unpack(batchX_placeholder, axis=1)

labels_series = tf.unpack(batchY_placeholder, axis=1)

如同下圖所示,這通過把 batch 中的列(axis = 1)解壓到 Python 列表來實現。RNN 同時在時間序列的不同部分上訓練;在現有 batch 例子中,是 4-6、16-18、28-30 步。使用以 “plural”_”series”為名的變數,是為了強調該變數是一個清單——代表了在每一個時間步有多個 entry 的時間序列。

現有 batch 被分成列的圖示,每個數據點上的數位是順序指數,牽頭指示相鄰時間步。

在我們的時間序列中,訓練同時在三個地方完成。這需要在前饋是時同時保存三個 instances of states。這已經被考慮到了:你看得到的 init_state placeholder 有 batch_size 行。

Forward pass

下一步,我們會創建進行真實 RNN 運算的計算圖部分。

# Forward pass

current_state = init_state

states_series = []

for current_input in inputs_series:

current_input = tf.reshape(current_input, [batch_size, 1])

input_and_state_concatenated = tf.concat(1, [current_input, current_state]) # Increasing number of columns

next_state = tf.tanh(tf.matmul(input_and_state_concatenated, W) + b) # Broadcasted addition

states_series.append(next_state)

current_state = next_state

注意第六行的串聯(concatenation),我們實際上想要做的,是計算兩個仿射變形(affine transforms)的 current_input * Wa + current_state *Wbin,見下圖。通過串聯這兩個張量,你會=只會使用一個矩陣乘法。偏差 b 的加法,會在 batch 裡的所有樣本上傳播。

上面代碼示例中矩陣第八行的計算,非線性變形的反正切(arctan)被忽略。

你也許會好奇變數 truncated_backprop_length 其名稱的含義。當一個 RNN 被訓練,事實上它被作為是一個深度神經網路的特殊情況:在每一層有重複出現的權重。這些層不會展開到一開始的時候,這麼幹的計算成本太高,因而時間步的數量被截為有限的數目。在上面的圖示中,誤差在 batch 中被反向傳播三步。

計算損失

這是計算圖的最後一步,一個從狀態到輸出的全連接 softmax 層,讓 classes 以 one-hot 格式編碼, 然後計算 batch 的損失。

logits_series = [tf.matmul(state, W2) + b2 for state in states_series] #Broadcasted addition

predictions_series = [tf.nn.softmax(logits) for logits in logits_series]

losses = [tf.nn.sparse_softmax_cross_entropy_with_logits(logits, labels) for logits, labels in zip(logits_series,labels_series)]

total_loss = tf.reduce_mean(losses)

train_step = tf.train.AdagradOptimizer(0.3).minimize(total_loss)

最後一行加入的是訓練功能。TensorFlow 會自動運行反向傳播——對每一個 mini-batch,計算圖會執行一次;網路權重會漸進式更新。

注意 API 調用 ”sparse_softmax_cross_entropy_with_logits“,它在內部自動計算 softmax,然後計算 cross-entropy。在我們的例子裡,這些 class 是互相排斥的,要麼是 1 要麼是 0,這便是使用 “Sparse-softmax” 的原因。你可以在

API

中瞭解更多。

訓練視覺化

這裡面有視覺化函數,所以我們能在訓練時看到神經網路中發生了什麼。它會不斷繪製損失曲線,展示訓練輸入、訓練輸出,以及在一個訓練 batch 的不同樣本序列上神經網路的現有預測。

def plot(loss_list, predictions_series, batchX, batchY):

plt.subplot(2, 3, 1)

plt.cla()

plt.plot(loss_list)

for batch_series_idx in range(5):

one_hot_output_series = np.array(predictions_series)[:, batch_series_idx, :]

single_output_series = np.array([(1 if out[0]

plt.subplot(2, 3, batch_series_idx + 2)

plt.cla()

plt.axis([0, truncated_backprop_length, 0, 2])

left_offset = range(truncated_backprop_length)

plt.bar(left_offset, batchX[batch_series_idx, :], width=1, color="blue")

plt.bar(left_offset, batchY[batch_series_idx, :] * 0.5, width=1, color="red")

plt.bar(left_offset, single_output_series * 0.3, width=1, color="green")

plt.draw()

plt.pause(0.0001)

 運行訓練環節

到了把一切歸總、訓練網路的時候了。在 TensorFlow 中,計算圖要在一個大環節中執行。新資料在每個小環節生成(並不是通常的方式,但它在這個例子中有用。以為所有東西都是可預測的)。

with tf.Session() as sess:

sess.run(tf.initialize_all_variables())

plt.ion()

plt.figure()

plt.show()

loss_list = []

for epoch_idx in range(num_epochs):

x,y = generateData()

_current_state = np.zeros((batch_size, state_size))

print("New data, epoch", epoch_idx)

for batch_idx in range(num_batches):

start_idx = batch_idx * truncated_backprop_length

end_idx = start_idx + truncated_backprop_length

batchX = x[:,start_idx:end_idx]

batchY = y[:,start_idx:end_idx]

_total_loss, _train_step, _current_state, _predictions_series = sess.run(

[total_loss, train_step, current_state, predictions_series],

feed_dict={

batchX_placeholder:batchX,

batchY_placeholder:batchY,

init_state:_current_state

})

loss_list.append(_total_loss)

if batch_idx%100 == 0:

print("Step",batch_idx, "Loss", _total_loss)

plot(loss_list, _predictions_series, batchX, batchY)

plt.ioff()

plt.show()

你可以看到,我們在每次反覆運算把 truncated_backprop_length 步驟向前移(第 15–19 行),但設置不同的移動幅度是可能的。該話題在下面進一步討論。據雷鋒網瞭解,這麼做的壞處是,truncated_backprop_length 需要比 time dependencies 大很多(在我們的例子中是三步),才能隔離相關訓練資料。否則可能會有許多“丟失”,如下圖。

方塊時間序列,升起的黑塊代表 echo-output,在 echo input(黑塊)三步之後啟動。滑動 batch 視窗每次也移動三步,在我們的例子中,這意味著沒有 batch 會隔離 dependency,所以它無法訓練。

雷鋒網提醒,這只是一個解釋 RNN 工作原理的簡單例子,該功能可以很容易地用幾行代碼編寫出來。該神經網路將能夠準確地學習 echo 行為,所以沒有必要用測試資料。

該程式會隨訓練更新圖表。請見下麵的圖例。藍條代表訓練輸入信號(二元),紅條表示訓練輸出的 echo,綠條是神經網路產生的 echo。不同的條形塊代表了當前 batch 的不同樣本序列。

我們的演算法能夠相當快速地學習該任務。左上角的圖展示了隨時函數的輸出,但圖中的尖刺是怎麼回事?你可以好好想一想,答案在下面。

損失、輸入、輸出訓練資料(藍、紅)以及預測(綠)的視覺化。

形成尖刺的原因是:我們正在開始一個新的小環節,生成新資料。由於矩陣被整形過,每一行的新單元與上一行的最後一個單元臨近。除了第一行,所有行的開頭幾個單元有不會被包括在狀態(state)裡的 dependency,因此神經網路在第一個 batch 上的表現永遠不怎麼樣。

整個系統

以下便是整個可運行的系統,你只需要複製粘貼然後運行。

from __future__ import print_function, division

import numpy as np

import tensorflow as tf

import matplotlib.pyplot as plt

num_epochs = 100

total_series_length = 50000

truncated_backprop_length = 15

state_size = 4

num_classes = 2

echo_step = 3

batch_size = 5

num_batches = total_series_length//batch_size//truncated_backprop_length

def generateData():

x = np.array(np.random.choice(2, total_series_length, p=[0.5, 0.5]))

y = np.roll(x, echo_step)

y[0:echo_step] = 0

x = x.reshape((batch_size, -1)) # The first index changing slowest, subseries as rows

y = y.reshape((batch_size, -1))

return (x, y)

batchX_placeholder = tf.placeholder(tf.float32, [batch_size, truncated_backprop_length])

batchY_placeholder = tf.placeholder(tf.int32, [batch_size, truncated_backprop_length])

init_state = tf.placeholder(tf.float32, [batch_size, state_size])

W = tf.Variable(np.random.rand(state_size+1, state_size), dtype=tf.float32)

b = tf.Variable(np.zeros((1,state_size)), dtype=tf.float32)

W2 = tf.Variable(np.random.rand(state_size, num_classes),dtype=tf.float32)

b2 = tf.Variable(np.zeros((1,num_classes)), dtype=tf.float32)

# Unpack columns

inputs_series = tf.unpack(batchX_placeholder, axis=1)

labels_series = tf.unpack(batchY_placeholder, axis=1)

# Forward pass

current_state = init_s tate

states_series = []

for current_input in inputs_series:

current_input = tf.reshape(current_input, [batch_size, 1])

input_and_state_concatenated = tf.concat(1, [current_input, current_state]) # Increasing number of columns

next_state = tf.tanh(tf.matmul(input_and_state_concatenated, W) + b) # Broadcasted addition

states_series.append(next_state)

current_state = next_state

logits_series = [tf.matmul(state, W2) + b2 for state in states_series] #Broadcasted addition

predictions_series = [tf.nn.softmax(logits) for logits in logits_series]

losses = [tf.nn.sparse_softmax_cross_entropy_with_logits(logits, labels) for logits, labels in zip(logits_series,labels_series)]

total_loss = tf.reduce_mean(losses)

train_step = tf.train.AdagradOptimizer(0.3).minimize(total_loss)

def plot(loss_list, predictions_series, batchX, batchY):

plt.subplot(2, 3, 1)

plt.cla()

plt.plot(loss_list)

for batch_series_idx in range(5):

one_hot_output_series = np.array(predictions_series)[:, batch_series_idx, :]

single_output_series = np.array([(1 if out[0]

plt.subplot(2, 3, batch_series_idx + 2)

plt.cla()

plt.axis([0, truncated_backprop_length, 0, 2])

left_offset = range(truncated_backprop_length)

plt.bar(left_offset, batchX[batch_series_idx, :], width=1, color="blue")

plt.bar(left_offset, batchY[batch_series_idx, :] * 0.5, width=1, color="red")

plt.bar(left_offset, single_output_series * 0.3, width=1, color="green")

plt.draw()

plt.pause(0.0001)

with tf.Session() as sess:

sess.run(tf.initialize_all_variables())

plt.ion()

plt.figure()

plt.show()

loss_list = []

for epoch_idx in range(num_epochs):

x,y = generateData()

_current_state = np.zeros((batch_size, state_size))

print("New data, epoch", epoch_idx)

for batch_idx in range(num_batches):

start_idx = batch_idx * truncated_backprop_length

end_idx = start_idx + truncated_backprop_length

batchX = x[:,start_idx:end_idx]

batchY = y[:,start_idx:end_idx]

_total_loss, _train_step, _current_state, _predictions_series = sess.run(

[total_loss, train_step, current_state, predictions_series],

feed_dict={

batchX_placeholder:batchX,

batchY_placeholder:batchY,

init_state:_current_state

})

loss_list.append(_total_loss)

if batch_idx%100 == 0:

print("Step",batch_idx, "Loss", _total_loss)

plot(loss_list, _predictions_series, batchX, batchY)

plt.ioff()

plt.show()

via 

medium

,原作者 Erik Hallström,雷鋒網編譯

“TensorFlow & 神經網路演算法高級應用班”要開課啦!

從初級到高級,理論+實戰,一站式深度瞭解 TensorFlow!

本課程面向深度學習開發者,講授如何利用 TensorFlow 解決圖像識別、文本分析等具體問題。課程跨度為 10 周,將從 TensorFlow 的原理與基礎實戰技巧開始,一步步教授學員如何在 TensorFlow 上搭建 CNN、自編碼、RNN、GAN 等模型,並最終掌握一整套基於 TensorFlow 做深度學習開發的專業技能。

兩名授課老師佟達、白髮川身為 ThoughtWorks 的資深技術專家,具有豐富的大資料平臺搭建、深度學習系統開發專案經驗。

時間:每週二、四晚 20:00-21:00

開課時長:總學時 20 小時,分 10 周完成,每週2次,每次 1 小時

線上授課地址:http://www.mooc.ai/

喜欢就按个赞吧!!!
点击关闭提示