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