當前位置:
首頁 > 新聞 > 教你從零開始在 TensorFlow 上搭建 RNN(完整代碼)!

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

RNN 是什麼?

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

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

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

RNN 處理系列數據的過程圖解

設置

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


  1. from __future__ import print_function, division

  2. import numpy as np

  3. import tensorflow as tf

  4. import matplotlib.pyplot as plt

  5. num_epochs = 100

  6. total_series_length = 50000

  7. truncated_backprop_length = 15

  8. state_size = 4

  9. num_classes = 2

  10. echo_step = 3

  11. batch_size = 5

  12. num_batches = total_series_length//batch_size//truncated_backprop_length

生成數據

現在生成訓練數據,輸入在本質上是一個隨機的二元矢量。輸出會是輸入的「迴響」(echo),把 echo_step 步驟移到右邊。


  1. def generateData:

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

  3. y = np.roll(x, echo_step)

  4. y[0:echo_step] = 0

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

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

  7. return (x, y)

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

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

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

創建計算圖

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

變數和 placeholder

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


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

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

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

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

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

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

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

  4. 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 指數以數字表示。

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

Unpacking

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


  1. # Unpack columns

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

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

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

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

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

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

Forward pass

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


  1. # Forward pass

  2. current_state = init_state

  3. states_series =

  4. for current_input in inputs_series:

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

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

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

  8. states_series.append(next_state)

  9. current_state = next_state

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

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

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

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

計算損失

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


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

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

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

  4. total_loss = tf.reduce_mean(losses)

  5. 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 的不同樣本序列上神經網路的現有預測。


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

  2. plt.subplot(2, 3, 1)

  3. plt.cla

  4. plt.plot(loss_list)

  5. for batch_series_idx in range(5):

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

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

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

  9. plt.cla

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

  11. left_offset = range(truncated_backprop_length)

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

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

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

  15. plt.draw

  16. plt.pause(0.0001)

運行訓練環節

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


  1. with tf.Session as sess:

  2. sess.run(tf.initialize_all_variables)

  3. plt.ion

  4. plt.figure

  5. plt.show

  6. loss_list =

  7. for epoch_idx in range(num_epochs):

  8. x,y = generateData

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

  10. print("New data, epoch", epoch_idx)

  11. for batch_idx in range(num_batches):

  12. start_idx = batch_idx * truncated_backprop_length

  13. end_idx = start_idx + truncated_backprop_length

  14. batchX = x[:,start_idx:end_idx]

  15. batchY = y[:,start_idx:end_idx]

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

  17. [total_loss, train_step, current_state, predictions_series],

  18. feed_dict={

  19. batchX_placeholder:batchX,

  20. batchY_placeholder:batchY,

  21. init_state:_current_state

  22. })

  23. loss_list.append(_total_loss)

  24. if batch_idx%100 == 0:

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

  26. plot(loss_list, _predictions_series, batchX, batchY)

  27. plt.ioff

  28. plt.show

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

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

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

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

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

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

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

損失、輸入、輸出訓練數據(藍、紅)以及預測(綠)的可視化。

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

整個系統

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


  1. from __future__ import print_function, division

  2. import numpy as np

  3. import tensorflow as tf

  4. import matplotlib.pyplot as plt

  5. num_epochs = 100

  6. total_series_length = 50000

  7. truncated_backprop_length = 15

  8. state_size = 4

  9. num_classes = 2

  10. echo_step = 3

  11. batch_size = 5

  12. num_batches = total_series_length//batch_size//truncated_backprop_length

  13. def generateData:

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

  15. y = np.roll(x, echo_step)

  16. y[0:echo_step] = 0

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

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

  19. return (x, y)

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

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

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

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

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

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

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

  27. # Unpack columns

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

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

  30. # Forward pass

  31. current_state = init_state

  32. states_series =

  33. for current_input in inputs_series:

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

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

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

  37. states_series.append(next_state)

  38. current_state = next_state

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

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

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

  42. total_loss = tf.reduce_mean(losses)

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

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

  45. plt.subplot(2, 3, 1)

  46. plt.cla

  47. plt.plot(loss_list)

  48. for batch_series_idx in range(5):

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

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

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

  52. plt.cla

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

  54. left_offset = range(truncated_backprop_length)

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

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

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

  58. plt.draw

  59. plt.pause(0.0001)

  60. with tf.Session as sess:

  61. sess.run(tf.initialize_all_variables)

  62. plt.ion

  63. plt.figure

  64. plt.show

  65. loss_list =

  66. for epoch_idx in range(num_epochs):

  67. x,y = generateData

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

  69. print("New data, epoch", epoch_idx)

  70. for batch_idx in range(num_batches):

  71. start_idx = batch_idx * truncated_backprop_length

  72. end_idx = start_idx + truncated_backprop_length

  73. batchX = x[:,start_idx:end_idx]

  74. batchY = y[:,start_idx:end_idx]

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

  76. [total_loss, train_step, current_state, predictions_series],

  77. feed_dict={

  78. batchX_placeholder:batchX,

  79. batchY_placeholder:batchY,

  80. init_state:_current_state

  81. })

  82. loss_list.append(_total_loss)

  83. if batch_idx%100 == 0:

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

  85. plot(loss_list, _predictions_series, batchX, batchY)

  86. plt.ioff

  87. 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/

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 雷鋒網 的精彩文章:

官方確認!滴滴完成55億美金融資,未來將投入國際化和智能駕駛布局
阿里巴巴將在巴西開展信貸業務,但至少還需翻越兩大障礙
從《人民的名義》看聲紋識別技術在案件偵查中的應用
速騰聚創邱純潮:一個激光雷達廠商眼中的自動駕駛產業鏈

TAG:雷鋒網 |

您可能感興趣

「Re:從零開始的異世界生活 Memory Snow」OVA動畫新PV公開!
使用PyTorch從零開始構建Elman循環神經網路
風格 | Peter Dundas 由零開始
教程 | 從零開始PyTorch項目:YOLO v3目標檢測實現(下)
從零開始Tensorflow:用AI深度學習來玩戰艦棋
10月新番 OVA動畫 Re:從零開始的異世界生活 Memory Snow
arduino從零開始(一)開始準備
從零開始PyTorch項目:YOLO v3目標檢測實現
從零開始搭建tensorflow人工智慧開發環境
偽·從零開始學Python-1.2 Python的開發工具
Kevin Durant杜蘭特不想「重零開始」,因為……
Zero 從零開始
偽·從零開始學Python-1.1 認識Python
C從零開始寫 SharpDx 應用 控制台創建 Sharpdx 窗口
從零開始學PyTorch(下):邏輯回歸及圖像分類
Python 高手之路:從零開始打造一個Web伺服器
從零開始用python搭建推薦引擎
從零開始用 python 搭建推薦引擎
從零開始教你 KMeans 演算法
從零開始的Windows 10系統安裝初級教程