2
本文作者: 三川 | 2017-04-28 16:23 |
RNN 是什么?
遞歸神經(jīng)網(wǎng)絡(luò),或者說 RNN,在數(shù)據(jù)能被按次序處理、數(shù)據(jù)點(diǎn)的不同排列亦會產(chǎn)生影響時就可以使用它。更重要的是,該次序可以是任意長度。
最直接的例子大概是一組數(shù)字的時間序列,根據(jù)此前的數(shù)值來預(yù)測接下來的數(shù)值。每個時間步(time-step)上,RNN 的輸入是當(dāng)前數(shù)值以及一個靜態(tài)矢量,后者用來表示神經(jīng)網(wǎng)絡(luò)在此前的不同時間步所“看到”的東西。該靜態(tài)矢量是 RNN 的編碼存儲,初始值設(shè)為零。
RNN 處理系列數(shù)據(jù)的過程圖解
我們會創(chuàng)建一個簡單的 Echo-RNN,它能記住輸入數(shù)據(jù)并在幾個時間步之后與之呼應(yīng)。首先要設(shè)置一些我們需要的限制,它們的意義下面會解釋。
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
現(xiàn)在生成訓(xùn)練數(shù)據(jù),輸入在本質(zhì)上是一個隨機(jī)的二元矢量。輸出會是輸入的“回響”(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)
注意數(shù)據(jù)整形(data reshaping)步驟,這是為了將其裝入有 batch_size 行的矩陣。神經(jīng)網(wǎng)絡(luò)根據(jù)神經(jīng)元權(quán)重來逼近損失函數(shù)的梯度,通過這種方式來進(jìn)行訓(xùn)練;該過程只會利用數(shù)據(jù)的一個小子集,即 mini-batch。數(shù)據(jù)整形把整個數(shù)據(jù)集裝入矩陣,然后分割為這些 mini-batch。
整形后的數(shù)據(jù)矩陣圖解。曲線箭頭用以表示換了行的相鄰時間步。淺灰色代表 0,深灰色代表 1。
TensorFlow 的工作方式會首先創(chuàng)建一個計算圖,來確認(rèn)哪些操作需要完成。計算圖的輸入和輸出一般是多維陣列,即張量(tensor)。計算圖或其中一部分,將被迭代執(zhí)行。這既可以在 CPU、GPU,也可在遠(yuǎn)程服務(wù)器上執(zhí)行。
本教程中使用的兩個最基礎(chǔ)的 TensorFlow 數(shù)據(jù)結(jié)構(gòu)是變量和 placeholder。每輪運(yùn)行中,batch 數(shù)據(jù)會被喂給 placeholder,而后者是計算圖的“起始點(diǎn)”。另外,前一輪輸出的 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])
神經(jīng)網(wǎng)絡(luò)的權(quán)重和偏差,被作為 TensorFlow 變量。這使得它們在每輪運(yùn)行中保持一致,并對每次 batch 漸進(jìn)式地更新。
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)
下圖展示的是作為輸入的數(shù)據(jù)矩陣,現(xiàn)有的 batch——batchX_placeholder 在虛線長方形里。正如我們后來看到的,這一 ”batch 窗口“在每輪運(yùn)行向右移動了 truncated_backprop_length 規(guī)定的步數(shù),這便是箭頭的意義。在下面的例子中,batch_size = 3, truncated_backprop_length = 3, and total_series_length = 36。注意這些數(shù)字只是出于可視化目的,代碼中的數(shù)值并不一樣。在幾個數(shù)據(jù)點(diǎn)中,series order 指數(shù)以數(shù)字表示。
這一步,要做的是搭建計算圖中類似于真正的 RNN 計算的部分。首先,我們希望把 batch 數(shù)據(jù)分割為鄰近的時間步。
# Unpack columns
inputs_series = tf.unpack(batchX_placeholder, axis=1)
labels_series = tf.unpack(batchY_placeholder, axis=1)
如同下圖所示,這通過把 batch 中的列(axis = 1)解壓到 Python 列表來實(shí)現(xiàn)。RNN 同時在時間序列的不同部分上訓(xùn)練;在現(xiàn)有 batch 例子中,是 4-6、16-18、28-30 步。使用以 “plural”_”series”為名的變量,是為了強(qiáng)調(diào)該變量是一個列表——代表了在每一個時間步有多個 entry 的時間序列。
現(xiàn)有 batch 被分成列的圖示,每個數(shù)據(jù)點(diǎn)上的數(shù)字是順序指數(shù),牽頭指示相鄰時間步。
在我們的時間序列中,訓(xùn)練同時在三個地方完成。這需要在前饋是時同時保存三個 instances of states。這已經(jīng)被考慮到了:你看得到的 init_state placeholder 有 batch_size 行。
下一步,我們會創(chuàng)建進(jìn)行真實(shí) RNN 運(yùn)算的計算圖部分。
# 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
注意第六行的串聯(lián)(concatenation),我們實(shí)際上想要做的,是計算兩個仿射變形(affine transforms)的 current_input * Wa + current_state *Wbin,見下圖。通過串聯(lián)這兩個張量,你會=只會使用一個矩陣乘法。偏差 b 的加法,會在 batch 里的所有樣本上傳播。
上面代碼示例中矩陣第八行的計算,非線性變形的反正切(arctan)被忽略。
你也許會好奇變量 truncated_backprop_length 其名稱的含義。當(dāng)一個 RNN 被訓(xùn)練,事實(shí)上它被作為是一個深度神經(jīng)網(wǎng)絡(luò)的特殊情況:在每一層有重復(fù)出現(xiàn)的權(quán)重。這些層不會展開到一開始的時候,這么干的計算成本太高,因而時間步的數(shù)量被截為有限的數(shù)目。在上面的圖示中,誤差在 batch 中被反向傳播三步。
這是計算圖的最后一步,一個從狀態(tài)到輸出的全連接 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)
最后一行加入的是訓(xùn)練功能。TensorFlow 會自動運(yùn)行反向傳播——對每一個 mini-batch,計算圖會執(zhí)行一次;網(wǎng)絡(luò)權(quán)重會漸進(jìn)式更新。
注意 API 調(diào)用 ”sparse_softmax_cross_entropy_with_logits“,它在內(nèi)部自動計算 softmax,然后計算 cross-entropy。在我們的例子里,這些 class 是互相排斥的,要么是 1 要么是 0,這便是使用 “Sparse-softmax” 的原因。你可以在 API 中了解更多。
這里面有可視化函數(shù),所以我們能在訓(xùn)練時看到神經(jīng)網(wǎng)絡(luò)中發(fā)生了什么。它會不斷繪制損失曲線,展示訓(xùn)練輸入、訓(xùn)練輸出,以及在一個訓(xùn)練 batch 的不同樣本序列上神經(jīng)網(wǎng)絡(luò)的現(xiàn)有預(yù)測。
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] < 0.5 else 0) for out in one_hot_output_series])
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)
到了把一切歸總、訓(xùn)練網(wǎng)絡(luò)的時候了。在 TensorFlow 中,計算圖要在一個大環(huán)節(jié)中執(zhí)行。新數(shù)據(jù)在每個小環(huán)節(jié)生成(并不是通常的方式,但它在這個例子中有用。以為所有東西都是可預(yù)測的)。
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 行),但設(shè)置不同的移動幅度是可能的。該話題在下面進(jìn)一步討論。據(jù)雷鋒網(wǎng)了解,這么做的壞處是,truncated_backprop_length 需要比 time dependencies 大很多(在我們的例子中是三步),才能隔離相關(guān)訓(xùn)練數(shù)據(jù)。否則可能會有許多“丟失”,如下圖。
方塊時間序列,升起的黑塊代表 echo-output,在 echo input(黑塊)三步之后激活?;瑒?nbsp;batch 窗口每次也移動三步,在我們的例子中,這意味著沒有 batch 會隔離 dependency,所以它無法訓(xùn)練。
雷鋒網(wǎng)提醒,這只是一個解釋 RNN 工作原理的簡單例子,該功能可以很容易地用幾行代碼編寫出來。該神經(jīng)網(wǎng)絡(luò)將能夠準(zhǔn)確地學(xué)習(xí) echo 行為,所以沒有必要用測試數(shù)據(jù)。
該程序會隨訓(xùn)練更新圖表。請見下面的圖例。藍(lán)條代表訓(xùn)練輸入信號(二元),紅條表示訓(xùn)練輸出的 echo,綠條是神經(jīng)網(wǎng)絡(luò)產(chǎn)生的 echo。不同的條形塊代表了當(dāng)前 batch 的不同樣本序列。
我們的算法能夠相當(dāng)快速地學(xué)習(xí)該任務(wù)。左上角的圖展示了隨時函數(shù)的輸出,但圖中的尖刺是怎么回事?你可以好好想一想,答案在下面。
損失、輸入、輸出訓(xùn)練數(shù)據(jù)(藍(lán)、紅)以及預(yù)測(綠)的可視化。
形成尖刺的原因是:我們正在開始一個新的小環(huán)節(jié),生成新數(shù)據(jù)。由于矩陣被整形過,每一行的新單元與上一行的最后一個單元臨近。除了第一行,所有行的開頭幾個單元有不會被包括在狀態(tài)(state)里的 dependency,因此神經(jīng)網(wǎng)絡(luò)在第一個 batch 上的表現(xiàn)永遠(yuǎn)不怎么樣。
以下便是整個可運(yùn)行的系統(tǒng),你只需要復(fù)制粘貼然后運(yùn)行。
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_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
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] < 0.5 else 0) for out in one_hot_output_series])
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,雷鋒網(wǎng)編譯
從初級到高級,理論+實(shí)戰(zhàn),一站式深度了解 TensorFlow!
本課程面向深度學(xué)習(xí)開發(fā)者,講授如何利用 TensorFlow 解決圖像識別、文本分析等具體問題。課程跨度為 10 周,將從 TensorFlow 的原理與基礎(chǔ)實(shí)戰(zhàn)技巧開始,一步步教授學(xué)員如何在 TensorFlow 上搭建 CNN、自編碼、RNN、GAN 等模型,并最終掌握一整套基于 TensorFlow 做深度學(xué)習(xí)開發(fā)的專業(yè)技能。
兩名授課老師佟達(dá)、白發(fā)川身為 ThoughtWorks 的資深技術(shù)專家,具有豐富的大數(shù)據(jù)平臺搭建、深度學(xué)習(xí)系統(tǒng)開發(fā)項(xiàng)目經(jīng)驗(yàn)。
時間:每周二、四晚 20:00-21:00
開課時長:總學(xué)時 20 小時,分 10 周完成,每周2次,每次 1 小時
線上授課地址:http://www.mooc.ai/
相關(guān)文章:
一文讀懂 CNN、DNN、RNN 內(nèi)部網(wǎng)絡(luò)結(jié)構(gòu)區(qū)別
萬事開頭難!入門TensorFlow,這9個問題TF Boys 必須要搞清楚
TensorFlow在工程項(xiàng)目中的應(yīng)用 公開課視頻+文字轉(zhuǎn)錄(上) | AI 研習(xí)社
一文詳解如何用 TensorFlow 實(shí)現(xiàn)基于 LSTM 的文本分類(附源碼)
雷峰網(wǎng)版權(quán)文章,未經(jīng)授權(quán)禁止轉(zhuǎn)載。詳情見轉(zhuǎn)載須知。