摘要:本篇文章將分享迴圈神經網路LSTM RNN如何實現迴歸預測。
本文分享自華為雲社群《[Python人工智慧] 十四.迴圈神經網路LSTM RNN迴歸案例之sin曲線預測 丨【百變AI秀】》,作者:eastmount。
一.RNN和LSTM回顧
1.RNN
(1) RNN原理
迴圈神經網路英文是Recurrent Neural Networks,簡稱RNN。假設有一組資料data0、data1、data2、data3,使用同一個神經網路預測它們,得到對應的結果。如果資料之間是有關係的,比如做菜下料的前後步驟,英文單詞的順序,如何讓資料之間的關聯也被神經網路學習呢?這就要用到——RNN。
假設存在ABCD數字,需要預測下一個數字E,會根據前面ABCD順序進行預測,這就稱為記憶。預測之前,需要回顧以前的記憶有哪些,再加上這一步新的記憶點,最終輸出output,迴圈神經網路(RNN)就利用了這樣的原理。
首先,讓我們想想人類是怎麼分析事物之間的關聯或順序的。人類通常記住之前發生的事情,從而幫助我們後續的行為判斷,那麼是否能讓計算機也記住之前發生的事情呢?
在分析data0時,我們把分析結果存入記憶Memory中,然後當分析data1時,神經網路(NN)會產生新的記憶,但此時新的記憶和老的記憶沒有關聯,如上圖所示。在RNN中,我們會簡單的把老記憶呼叫過來分析新記憶,如果繼續分析更多的資料時,NN就會把之前的記憶全部累積起來。
RNN結構如下圖所示,按照時間點t-1、t、t+1,每個時刻有不同的x,每次計算會考慮上一步的state和這一步的x(t),再輸出y值。在該數學形式中,每次RNN執行完之後都會產生s(t),當RNN要分析x(t+1)時,此刻的y(t+1)是由s(t)和s(t+1)共同創造的,s(t)可看作上一步的記憶。多個神經網路NN的累積就轉換成了迴圈神經網路,其簡化圖如下圖的左邊所示。
總之,只要你的資料是有順序的,就可以使用RNN,比如人類說話的順序,電話號碼的順序,影像畫素排列的順序,ABC字母的順序等。在前面講解CNN原理時,它可以看做是一個濾波器滑動掃描整幅影像,通過卷積加深神經網路對影像的理解。
而RNN也有同樣的掃描效果,只不過是增加了時間順序和記憶功能。RNN通過隱藏層週期性的連線,從而捕獲序列化資料中的動態資訊,提升預測結果。
(2) RNN應用
RNN常用於自然語言處理、機器翻譯、語音識別、影像識別等領域,下面簡單分享RNN相關應用所對應的結構。
- RNN情感分析: 當分析一個人說話情感是積極的還是消極的,就用如下圖所示的RNN結構,它有N個輸入,1個輸出,最後時間點的Y值代表最終的輸出結果。
- RNN影像識別: 此時有一張圖片輸入X,N張對應的輸出。
- RNN機器翻譯: 輸入和輸出分別兩個,對應的是中文和英文,如下圖所示。
2.LSTM
接下來我們看一個更強大的結構,稱為LSTM。
(1) 為什麼要引入LSTM呢?
RNN是在有序的資料上進行學習的,RNN會像人一樣對先前的資料發生記憶,但有時候也會像老爺爺一樣忘記先前所說。為了解決RNN的這個弊端,提出了LTSM技術,它的英文全稱是Long short-term memory,長短期記憶,也是當下最流行的RNN之一。
假設現在有一句話,如下圖所示,RNN判斷這句話是紅燒排骨,這時需要學習,而“紅燒排骨“在句子開頭。
"紅燒排骨"這個詞需要經過長途跋涉才能抵達,要經過一系列得到誤差,然後經過反向傳遞,它在每一步都會乘以一個權重w引數。如果乘以的權重是小於1的數,比如0.9,0.9會不斷地乘以誤差,最終這個值傳遞到初始值時,誤差就消失了,這稱為梯度消失或梯度離散。
反之,如果誤差是一個很大的數,比如1.1,則這個RNN得到的值會很大,這稱為梯度爆炸。
梯度消失或梯度爆炸:在RNN中,如果你的State是一個很長的序列,假設反向傳遞的誤差值是一個小於1的數,每次反向傳遞都會乘以這個數,0.9的n次方趨向於0,1.1的n次方趨向於無窮大,這就會造成梯度消失或梯度爆炸。
這也是RNN沒有恢復記憶的原因,為了解決RNN梯度下降時遇到的梯度消失或梯度爆炸問題,引入了LSTM。
(2) LSTM
LSTM是在普通的RNN上面做了一些改進,LSTM RNN多了三個控制器,即輸入、輸出、忘記控制器。左邊多了個條主線,例如電影的主線劇情,而原本的RNN體系變成了分線劇情,並且三個控制器都在分線上。
- 輸入控制器(write gate): 在輸入input時設定一個gate,gate的作用是判斷要不要寫入這個input到我們的記憶體Memory中,它相當於一個引數,也是可以被訓練的,這個引數就是用來控制要不要記住當下這個點。
- 輸出控制器(read gate): 在輸出位置的gate,判斷要不要讀取現在的Memory。
- 忘記控制器(forget gate): 處理位置的忘記控制器,判斷要不要忘記之前的Memory。
LSTM工作原理為:如果分線劇情對於最終結果十分重要,輸入控制器會將這個分線劇情按重要程度寫入主線劇情,再進行分析;如果分線劇情改變了我們之前的想法,那麼忘記控制器會將某些主線劇情忘記,然後按比例替換新劇情,所以主線劇情的更新就取決於輸入和忘記控制;最後的輸出會基於主線劇情和分線劇情。
通過這三個gate能夠很好地控制我們的RNN,基於這些控制機制,LSTM是延緩記憶的良藥,從而帶來更好的結果。
二.LSTM RNN迴歸案例說明
前面我們講解了RNN、CNN的分類問題,這篇文章將分享一個迴歸問題。在LSTM RNN迴歸案例中,我們想要用藍色的虛線預測紅色的實線,由於sin曲線是波浪迴圈,所以RNN會用一段序列來預測另一段序列。
程式碼基本結構包括:
- (1) 生成資料的函式get_batch()
- (2) 主體LSTM RNN
- (3) 三層神經網路,包括input_layer、cell、output_layer,和之前分類RNN的結構一樣。
def add_input_layer(self,):
pass
def add_cell(self):
pass
def add_output_layer(self):
pass
- (4) 計算誤差函式 computer_cost
- (5) 誤差weight和偏置biases
- (6) 主函式建立LSTM RNN模型
- (7) TensorBoard視覺化神經網路模型,matplotlib視覺化擬合曲線、
最後再補充下BPTT,就開始我們的程式碼編寫。
(1) 普通RNN
假設我們訓練含有1000000個資料的序列,如果全部訓練的話,整個的序列都feed進RNN中,容易造成梯度消失或爆炸的問題。所以解決的方法就是截斷反向傳播 (Truncated Backpropagation,BPTT) ,我們將序列截斷來進行訓練(num_steps)。
一般截斷的反向傳播是:在當前時間t,往前反向傳播num_steps步即可。如下圖,長度為6的序列,截斷步數是3,Initial State和Final State在RNN Cell中傳遞。
(2) TensorFlow版本的BPTT
但是Tensorflow中的實現並不是這樣,它是將長度為6的序列分為了兩部分,每一部分長度為3,前一部分計算得到的final state用於下一部分計算的initial state。如下圖所示,每個batch進行單獨的截斷反向傳播。此時的batch會儲存Final State,並作為下一個batch的初始化State。
參考:深度學習(07)RNN-迴圈神經網路-02-Tensorflow中的實現 - 莫失莫忘Lawlite
三.程式碼實現
第一步,開啟Anaconda,然後選擇已經搭建好的“tensorflow”環境,執行Spyder。
第二步,匯入擴充套件包。
import tensorflow as tf import numpy as np import matplotlib.pyplot as plt
第三步,編寫生成資料的函式get_batch(),它生成了sin曲線的序列。
# 獲取批量資料 def get_batch(): global BATCH_START, TIME_STEPS # xs shape (50batch, 20steps) xs = np.arange(BATCH_START, BATCH_START+TIME_STEPS*BATCH_SIZE).reshape((BATCH_SIZE, TIME_STEPS)) / (10*np.pi) seq = np.sin(xs) res = np.cos(xs) BATCH_START += TIME_STEPS # 顯示原始曲線 plt.plot(xs[0, :], res[0, :], 'r', xs[0, :], seq[0, :], 'b--') plt.show() # 返回序列seq 結果res 輸入xs return [seq[:, :, np.newaxis], res[:, :, np.newaxis], xs]
此時的輸出結果如下圖所示,注意它只是模擬的預期曲線,還不是我們神經網路學習的結構。
第四步,編寫LSTMRNN類,它用於定義我們的迴圈神經網路結構,初始化操作和所需變數。
初始化init()函式的引數包括:
- n_steps表示batch中的步驟,共有3步。
- input_size表示傳入batch data時,每個input的長度,該例項中input_size和output_size均為1。如下圖所示,假設我們batch長度為一個週期(0-6),每個input是線的x值,input size表示每個時間點有多少個值,只有一個點故為1。
- output_size表示輸出的值,輸出對應input線的y值,其大小值為1。
- cell_size表示RNN Cell的個數,其值為10。
- batch_size表示一次性傳給神經網路的batch數量,設定為50。
該部分程式碼如下,注意xs和ys的形狀。同時,我們需要使用Tensorboard視覺化RNN的結構,所以呼叫tf.name_scope()設定各神經層和變數的名稱空間名稱,詳見第五篇文章。
#----------------------------------定義引數---------------------------------- BATCH_START = 0 TIME_STEPS = 20 BATCH_SIZE = 50 # BATCH數量 INPUT_SIZE = 1 # 輸入一個值 OUTPUT_SIZE = 1 # 輸出一個值 CELL_SIZE = 10 # Cell數量 LR = 0.006 BATCH_START_TEST = 0 #----------------------------------LSTM RNN---------------------------------- class LSTMRNN(object): # 初始化操作 def __init__(self, n_steps, input_size, output_size, cell_size, batch_size): self.n_steps = n_steps self.input_size = input_size self.output_size = output_size self.cell_size = cell_size self.batch_size = batch_size # TensorBoard視覺化操作使用name_scope with tf.name_scope('inputs'): #輸出變數 self.xs = tf.placeholder(tf.float32, [None, n_steps, input_size], name='xs') self.ys = tf.placeholder(tf.float32, [None, n_steps, output_size], name='ys') with tf.variable_scope('in_hidden'): #輸入層 self.add_input_layer() with tf.variable_scope('LSTM_cell'): #處理層 self.add_cell() with tf.variable_scope('out_hidden'): #輸出層 self.add_output_layer() with tf.name_scope('cost'): #誤差 self.compute_cost() with tf.name_scope('train'): #訓練 self.train_op = tf.train.AdamOptimizer(LR).minimize(self.cost)
第五步,接著開始編寫三個函式(三層神經網路),它是RNN的核心結構。
# 輸入層
def add_input_layer(self,):
pass
# cell層
def add_cell(self):
pass
# 輸出層
def add_output_layer(self):
pass
這三個函式也是增加在LSTMRNN的Class中,核心程式碼及詳細註釋如下所示:
#--------------------------------定義核心三層結構----------------------------- # 輸入層 def add_input_layer(self,): # 定義輸入層xs變數 將xs三維資料轉換成二維 # [None, n_steps, input_size] => (batch*n_step, in_size) l_in_x = tf.reshape(self.xs, [-1, self.input_size], name='2_2D') # 定義輸入權重 (in_size, cell_size) Ws_in = self._weight_variable([self.input_size, self.cell_size]) # 定義輸入偏置 (cell_size, ) bs_in = self._bias_variable([self.cell_size,]) # 定義輸出y變數 二維形狀 (batch * n_steps, cell_size) with tf.name_scope('Wx_plus_b'): l_in_y = tf.matmul(l_in_x, Ws_in) + bs_in # 返回結果形狀轉變為三維 # l_in_y ==> (batch, n_steps, cell_size) self.l_in_y = tf.reshape(l_in_y, [-1, self.n_steps, self.cell_size], name='2_3D') # cell層 def add_cell(self): # 選擇BasicLSTMCell模型 # forget初始偏置為1.0(初始時不希望forget) 隨著訓練深入LSTM會選擇性忘記 lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(self.cell_size, forget_bias=1.0, state_is_tuple=True) # 設定initial_state全為0 視覺化操作用name_scope with tf.name_scope('initial_state'): self.cell_init_state = lstm_cell.zero_state(self.batch_size, dtype=tf.float32) # RNN迴圈 每一步的輸出都儲存在cell_outputs序列中 cell_final_state為最終State並傳入下一個batch中 # 常規RNN只有m_state LSTM包括c_state和m_state self.cell_outputs, self.cell_final_state = tf.nn.dynamic_rnn( lstm_cell, self.l_in_y, initial_state=self.cell_init_state, time_major=False) # 輸出層 (類似輸入層) def add_output_layer(self): # 轉換成二維 方能使用W*X+B # shape => (batch * steps, cell_size) l_out_x = tf.reshape(self.cell_outputs, [-1, self.cell_size], name='2_2D') Ws_out = self._weight_variable([self.cell_size, self.output_size]) bs_out = self._bias_variable([self.output_size, ]) # 返回預測結果 # shape => (batch * steps, output_size) with tf.name_scope('Wx_plus_b'): self.pred = tf.matmul(l_out_x, Ws_out) + bs_out
注意,上面呼叫了reshape()進行形狀更新,為什麼要將三維變數改成二維呢?因為只有變成二維變數之後,才能計算W*X+B。
第六步,定義計算誤差函式。
這裡需要注意:我們使用了seq2seq函式。它求出的loss是整個batch每一步的loss,然後把每一步loss進行sum求和,變成了整個TensorFlow的loss,再除以batch size平均,最終得到這個batch的總cost,它是一個scalar數字。
# 定義誤差計算函式 def compute_cost(self): # 使用seq2seq序列到序列模型 # tf.nn.seq2seq.sequence_loss_by_example() losses = tf.contrib.legacy_seq2seq.sequence_loss_by_example( [tf.reshape(self.pred, [-1], name='reshape_pred')], [tf.reshape(self.ys, [-1], name='reshape_target')], [tf.ones([self.batch_size * self.n_steps], dtype=tf.float32)], average_across_timesteps=True, softmax_loss_function=self.msr_error, name='losses' ) # 最終得到batch的總cost 它是一個數字 with tf.name_scope('average_cost'): # 整個TensorFlow的loss求和 再除以batch size self.cost = tf.div( tf.reduce_sum(losses, name='losses_sum'), self.batch_size, name='average_cost') tf.summary.scalar('cost', self.cost)
後面的文章我們會詳細寫一篇機器翻譯相關的內容,並使用seq2seq模型。
Seq2Seq模型是輸出的長度不確定時採用的模型,這種情況一般是在機器翻譯的任務中出現,將一句中文翻譯成英文,那麼這句英文的長度有可能會比中文短,也有可能會比中文長,所以輸出的長度就不確定了。如下圖所,輸入的中文長度為4,輸出的英文長度為2。
在網路結構中,輸入一箇中文序列,然後輸出它對應的中文翻譯,輸出的部分的結果預測後面,根據上面的例子,也就是先輸出“machine”,將"machine"作為下一次的輸入,接著輸出"learning",這樣就能輸出任意長的序列。
機器翻譯、人機對話、聊天機器人等等,這些都是應用在當今社會都或多或少的運用到了我們這裡所說的Seq2Seq。
第七步,定義msr_error計算函式、誤差計算函式和偏置計算函式。
# 該函式用於計算 # 相當於msr_error(self, y_pre, y_target) return tf.square(tf.sub(y_pre, y_target)) def msr_error(self, logits, labels): return tf.square(tf.subtract(logits, labels)) # 誤差計算 def _weight_variable(self, shape, name='weights'): initializer = tf.random_normal_initializer(mean=0., stddev=1.,) return tf.get_variable(shape=shape, initializer=initializer, name=name) # 偏置計算 def _bias_variable(self, shape, name='biases'): initializer = tf.constant_initializer(0.1) return tf.get_variable(name=name, shape=shape, initializer=initializer)
寫到這裡,整個Class就定義完成。
第八步,接下來定義主函式,進行訓練和預測操作,這裡先嚐試TensorBoard視覺化展現。
#----------------------------------主函式 訓練和預測---------------------------------- if __name__ == '__main__': # 定義模型並初始化 model = LSTMRNN(TIME_STEPS, INPUT_SIZE, OUTPUT_SIZE, CELL_SIZE, BATCH_SIZE) sess = tf.Session() merged = tf.summary.merge_all() writer = tf.summary.FileWriter("logs", sess.graph) sess.run(tf.initialize_all_variables())
四.完整程式碼及視覺化展示
該階段的完整程式碼如下,我們先嚐試執行下程式碼:
# -*- coding: utf-8 -*- """ Created on Thu Jan 9 20:44:56 2020 @author: xiuzhang Eastmount CSDN """ import tensorflow as tf import numpy as np import matplotlib.pyplot as plt #----------------------------------定義引數---------------------------------- BATCH_START = 0 TIME_STEPS = 20 BATCH_SIZE = 50 # BATCH數量 INPUT_SIZE = 1 # 輸入一個值 OUTPUT_SIZE = 1 # 輸出一個值 CELL_SIZE = 10 # Cell數量 LR = 0.006 BATCH_START_TEST = 0 # 獲取批量資料 def get_batch(): global BATCH_START, TIME_STEPS # xs shape (50batch, 20steps) xs = np.arange(BATCH_START, BATCH_START+TIME_STEPS*BATCH_SIZE).reshape((BATCH_SIZE, TIME_STEPS)) / (10*np.pi) seq = np.sin(xs) res = np.cos(xs) BATCH_START += TIME_STEPS # 返回序列seq 結果res 輸入xs return [seq[:, :, np.newaxis], res[:, :, np.newaxis], xs] #----------------------------------LSTM RNN---------------------------------- class LSTMRNN(object): # 初始化操作 def __init__(self, n_steps, input_size, output_size, cell_size, batch_size): self.n_steps = n_steps self.input_size = input_size self.output_size = output_size self.cell_size = cell_size self.batch_size = batch_size # TensorBoard視覺化操作使用name_scope with tf.name_scope('inputs'): #輸出變數 self.xs = tf.placeholder(tf.float32, [None, n_steps, input_size], name='xs') self.ys = tf.placeholder(tf.float32, [None, n_steps, output_size], name='ys') with tf.variable_scope('in_hidden'): #輸入層 self.add_input_layer() with tf.variable_scope('LSTM_cell'): #處理層 self.add_cell() with tf.variable_scope('out_hidden'): #輸出層 self.add_output_layer() with tf.name_scope('cost'): #誤差 self.compute_cost() with tf.name_scope('train'): #訓練 self.train_op = tf.train.AdamOptimizer(LR).minimize(self.cost) #--------------------------------定義核心三層結構----------------------------- # 輸入層 def add_input_layer(self,): # 定義輸入層xs變數 將xs三維資料轉換成二維 # [None, n_steps, input_size] => (batch*n_step, in_size) l_in_x = tf.reshape(self.xs, [-1, self.input_size], name='2_2D') # 定義輸入權重 (in_size, cell_size) Ws_in = self._weight_variable([self.input_size, self.cell_size]) # 定義輸入偏置 (cell_size, ) bs_in = self._bias_variable([self.cell_size,]) # 定義輸出y變數 二維形狀 (batch * n_steps, cell_size) with tf.name_scope('Wx_plus_b'): l_in_y = tf.matmul(l_in_x, Ws_in) + bs_in # 返回結果形狀轉變為三維 # l_in_y ==> (batch, n_steps, cell_size) self.l_in_y = tf.reshape(l_in_y, [-1, self.n_steps, self.cell_size], name='2_3D') # cell層 def add_cell(self): # 選擇BasicLSTMCell模型 # forget初始偏置為1.0(初始時不希望forget) 隨著訓練深入LSTM會選擇性忘記 lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(self.cell_size, forget_bias=1.0, state_is_tuple=True) # 設定initial_state全為0 視覺化操作用name_scope with tf.name_scope('initial_state'): self.cell_init_state = lstm_cell.zero_state(self.batch_size, dtype=tf.float32) # RNN迴圈 每一步的輸出都儲存在cell_outputs序列中 cell_final_state為最終State並傳入下一個batch中 # 常規RNN只有m_state LSTM包括c_state和m_state self.cell_outputs, self.cell_final_state = tf.nn.dynamic_rnn( lstm_cell, self.l_in_y, initial_state=self.cell_init_state, time_major=False) # 輸出層 (類似輸入層) def add_output_layer(self): # 轉換成二維 方能使用W*X+B # shape => (batch * steps, cell_size) l_out_x = tf.reshape(self.cell_outputs, [-1, self.cell_size], name='2_2D') Ws_out = self._weight_variable([self.cell_size, self.output_size]) bs_out = self._bias_variable([self.output_size, ]) # 返回預測結果 # shape => (batch * steps, output_size) with tf.name_scope('Wx_plus_b'): self.pred = tf.matmul(l_out_x, Ws_out) + bs_out #--------------------------------定義誤差計算函式----------------------------- def compute_cost(self): # 使用seq2seq序列到序列模型 # tf.nn.seq2seq.sequence_loss_by_example() losses = tf.contrib.legacy_seq2seq.sequence_loss_by_example( [tf.reshape(self.pred, [-1], name='reshape_pred')], [tf.reshape(self.ys, [-1], name='reshape_target')], [tf.ones([self.batch_size * self.n_steps], dtype=tf.float32)], average_across_timesteps=True, softmax_loss_function=self.msr_error, name='losses' ) # 最終得到batch的總cost 它是一個數字 with tf.name_scope('average_cost'): # 整個TensorFlow的loss求和 再除以batch size self.cost = tf.div( tf.reduce_sum(losses, name='losses_sum'), self.batch_size, name='average_cost') tf.summary.scalar('cost', self.cost) # 該函式用於計算 # 相當於msr_error(self, y_pre, y_target) return tf.square(tf.sub(y_pre, y_target)) def msr_error(self, logits, labels): return tf.square(tf.subtract(logits, labels)) # 誤差計算 def _weight_variable(self, shape, name='weights'): initializer = tf.random_normal_initializer(mean=0., stddev=1.,) return tf.get_variable(shape=shape, initializer=initializer, name=name) # 偏置計算 def _bias_variable(self, shape, name='biases'): initializer = tf.constant_initializer(0.1) return tf.get_variable(name=name, shape=shape, initializer=initializer) #----------------------------------主函式 訓練和預測---------------------------------- if __name__ == '__main__': # 定義模型並初始化 model = LSTMRNN(TIME_STEPS, INPUT_SIZE, OUTPUT_SIZE, CELL_SIZE, BATCH_SIZE) sess = tf.Session() merged = tf.summary.merge_all() writer = tf.summary.FileWriter("logs", sess.graph) sess.run(tf.initialize_all_variables())
此時會在Python檔案目錄下新建一個“logs”資料夾和events的檔案,如下圖所示。
接下來嘗試開啟它。首先調出Anaconda Prompt,並啟用TensorFlow,接著去到events檔案的目錄,呼叫命令“tensorboard --logdir=logs執行即可,如下圖所示。注意,這裡只需要指引到資料夾,它就會自動索引到你的檔案。
activate tensorflow
cd\
cd C:\Users\xiuzhang\Desktop\TensorFlow\blog
tensorboard --logdir=logs
此時訪問網址“http://localhost:6006/”,選擇“Graphs”,執行之後如下圖所示,我們的神經網路就出現了。
神經網路結構如下圖所示,包括輸入層、LSTM層、輸出層、cost誤差計算、train訓練等。
詳細結構如下圖所示:
通常我們會將train部分放置一邊,選中“train”然後滑鼠右鍵點選“Remove from main graph”。核心結構如下,in_hidden是接受輸入的第一層,之後是LSTM_cell,最後是輸出層out_hidden。
- in_hidden: 包括了權重Weights和biases,計算公式Wx_plus_b。同時,它包括了reshape操作,2_2D和2_3D。
- out_hidden: 包括了權重weights、偏置biases、計算公式Wx_plus_b、二維資料2_2D,並且輸出結果為cost。
- cost: 計算誤差。
- 中間是LSTM_cell: 包括RNN迴圈神經網路,初始化initial_state,之後會被state更新替換。
注意版本問題,讀者可以結合自己的TensorFlow版本進行適當修改執行。作者版本版本資訊為:Python3.6、Anaconda3、Win10、Tensorflow1.15.0。
如果您報錯 AttributeError: module ‘tensorflow._api.v1.nn’ has no attribute ‘seq2seq’,這是TensorFlow 版本升級,方法呼叫更改。解決方式:
如果您報錯 TypeError: msr_error() got an unexpected keyword argument ‘labels’,msr_error() 函式得到一個意外的關鍵引數 ‘lables’。其解決方式:定義msr_error() 函式時,使用 labels,logits 指定,將
def msr_error(self, y_pre, y_target): return tf.square(tf.subtract(y_pre, y_target))
改為:
def msr_error(self, logits, labels): return tf.square(tf.subtract(logits, labels))
如果您報錯 ValueError: Variable in_hidden/weights already exists, disallowed. Did you mean to set reuse=True or reuse=tf.AUTO_REUSE in VarScope? ,則重新啟動kernel即可執行。
五.預測及曲線擬合
最後,我們在主函式中編寫RNN訓練學習和預測的程式碼。
首先我們來測試cost學習的結果。程式碼如下,if判斷中cell_init_state為前面已初始化的state,之後更新state(model.cell_init_state: state ),其實就是將Final State換成下一個batch的Initial State,從而符合我們定義的結構。
#----------------------------------主函式 訓練和預測---------------------------------- if __name__ == '__main__': # 定義模型並初始化 model = LSTMRNN(TIME_STEPS, INPUT_SIZE, OUTPUT_SIZE, CELL_SIZE, BATCH_SIZE) sess = tf.Session() merged = tf.summary.merge_all() writer = tf.summary.FileWriter("logs", sess.graph) sess.run(tf.initialize_all_variables()) # Tensorboard視覺化展現神經網路結果 #------------------------------RNN學習------------------------------------- # 訓練模型 for i in range(200): # 用seq預測res (序列-seq 結果-res 輸入-xs) seq, res, xs = get_batch() # 第一步賦值 之後會更新cell_init_state if i == 0: feed_dict = { model.xs: seq, model.ys: res, # create initial state (前面cell_init_state已初始化state) } else: feed_dict = { model.xs: seq, model.ys: res, model.cell_init_state: state # use last state as the initial state for this run } # state為final_state _, cost, state, pred = sess.run( [model.train_op, model.cost, model.cell_final_state, model.pred], feed_dict=feed_dict) # 每隔20步輸出結果 if i % 20 == 0: print('cost: ', round(cost, 4))
每隔20步輸出結果,如下所示,誤差從最初的33到最後的0.335,神經網路在不斷學習,誤差在不斷減小。
cost: 33.1673 cost: 9.1332 cost: 3.8899 cost: 1.3271 cost: 0.2682 cost: 0.4912 cost: 1.0692 cost: 0.3812 cost: 0.63 cost: 0.335
接下來增加matplotlib視覺化的sin曲線動態擬合過程,最終完整程式碼如下所示:
# -*- coding: utf-8 -*- """ Created on Thu Jan 9 20:44:56 2020 @author: xiuzhang Eastmount CSDN """ import tensorflow as tf import numpy as np import matplotlib.pyplot as plt #----------------------------------定義引數---------------------------------- BATCH_START = 0 TIME_STEPS = 20 BATCH_SIZE = 50 # BATCH數量 INPUT_SIZE = 1 # 輸入一個值 OUTPUT_SIZE = 1 # 輸出一個值 CELL_SIZE = 10 # Cell數量 LR = 0.006 BATCH_START_TEST = 0 # 獲取批量資料 def get_batch(): global BATCH_START, TIME_STEPS # xs shape (50batch, 20steps) xs = np.arange(BATCH_START, BATCH_START+TIME_STEPS*BATCH_SIZE).reshape((BATCH_SIZE, TIME_STEPS)) / (10*np.pi) seq = np.sin(xs) res = np.cos(xs) BATCH_START += TIME_STEPS # 顯示原始曲線 # plt.plot(xs[0, :], res[0, :], 'r', xs[0, :], seq[0, :], 'b--') # plt.show() # 返回序列seq 結果res 輸入xs return [seq[:, :, np.newaxis], res[:, :, np.newaxis], xs] #----------------------------------LSTM RNN---------------------------------- class LSTMRNN(object): # 初始化操作 def __init__(self, n_steps, input_size, output_size, cell_size, batch_size): self.n_steps = n_steps self.input_size = input_size self.output_size = output_size self.cell_size = cell_size self.batch_size = batch_size # TensorBoard視覺化操作使用name_scope with tf.name_scope('inputs'): #輸出變數 self.xs = tf.placeholder(tf.float32, [None, n_steps, input_size], name='xs') self.ys = tf.placeholder(tf.float32, [None, n_steps, output_size], name='ys') with tf.variable_scope('in_hidden'): #輸入層 self.add_input_layer() with tf.variable_scope('LSTM_cell'): #處理層 self.add_cell() with tf.variable_scope('out_hidden'): #輸出層 self.add_output_layer() with tf.name_scope('cost'): #誤差 self.compute_cost() with tf.name_scope('train'): #訓練 self.train_op = tf.train.AdamOptimizer(LR).minimize(self.cost) #--------------------------------定義核心三層結構----------------------------- # 輸入層 def add_input_layer(self,): # 定義輸入層xs變數 將xs三維資料轉換成二維 # [None, n_steps, input_size] => (batch*n_step, in_size) l_in_x = tf.reshape(self.xs, [-1, self.input_size], name='2_2D') # 定義輸入權重 (in_size, cell_size) Ws_in = self._weight_variable([self.input_size, self.cell_size]) # 定義輸入偏置 (cell_size, ) bs_in = self._bias_variable([self.cell_size,]) # 定義輸出y變數 二維形狀 (batch * n_steps, cell_size) with tf.name_scope('Wx_plus_b'): l_in_y = tf.matmul(l_in_x, Ws_in) + bs_in # 返回結果形狀轉變為三維 # l_in_y ==> (batch, n_steps, cell_size) self.l_in_y = tf.reshape(l_in_y, [-1, self.n_steps, self.cell_size], name='2_3D') # cell層 def add_cell(self): # 選擇BasicLSTMCell模型 # forget初始偏置為1.0(初始時不希望forget) 隨著訓練深入LSTM會選擇性忘記 lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(self.cell_size, forget_bias=1.0, state_is_tuple=True) # 設定initial_state全為0 視覺化操作用name_scope with tf.name_scope('initial_state'): self.cell_init_state = lstm_cell.zero_state(self.batch_size, dtype=tf.float32) # RNN迴圈 每一步的輸出都儲存在cell_outputs序列中 cell_final_state為最終State並傳入下一個batch中 # 常規RNN只有m_state LSTM包括c_state和m_state self.cell_outputs, self.cell_final_state = tf.nn.dynamic_rnn( lstm_cell, self.l_in_y, initial_state=self.cell_init_state, time_major=False) # 輸出層 (類似輸入層) def add_output_layer(self): # 轉換成二維 方能使用W*X+B # shape => (batch * steps, cell_size) l_out_x = tf.reshape(self.cell_outputs, [-1, self.cell_size], name='2_2D') Ws_out = self._weight_variable([self.cell_size, self.output_size]) bs_out = self._bias_variable([self.output_size, ]) # 返回預測結果 # shape => (batch * steps, output_size) with tf.name_scope('Wx_plus_b'): self.pred = tf.matmul(l_out_x, Ws_out) + bs_out #--------------------------------定義誤差計算函式----------------------------- def compute_cost(self): # 使用seq2seq序列到序列模型 # tf.nn.seq2seq.sequence_loss_by_example() losses = tf.contrib.legacy_seq2seq.sequence_loss_by_example( [tf.reshape(self.pred, [-1], name='reshape_pred')], [tf.reshape(self.ys, [-1], name='reshape_target')], [tf.ones([self.batch_size * self.n_steps], dtype=tf.float32)], average_across_timesteps=True, softmax_loss_function=self.msr_error, name='losses' ) # 最終得到batch的總cost 它是一個數字 with tf.name_scope('average_cost'): # 整個TensorFlow的loss求和 再除以batch size self.cost = tf.div( tf.reduce_sum(losses, name='losses_sum'), self.batch_size, name='average_cost') tf.summary.scalar('cost', self.cost) # 該函式用於計算 # 相當於msr_error(self, y_pre, y_target) return tf.square(tf.sub(y_pre, y_target)) def msr_error(self, logits, labels): return tf.square(tf.subtract(logits, labels)) # 誤差計算 def _weight_variable(self, shape, name='weights'): initializer = tf.random_normal_initializer(mean=0., stddev=1.,) return tf.get_variable(shape=shape, initializer=initializer, name=name) # 偏置計算 def _bias_variable(self, shape, name='biases'): initializer = tf.constant_initializer(0.1) return tf.get_variable(name=name, shape=shape, initializer=initializer) #----------------------------------主函式 訓練和預測---------------------------------- if __name__ == '__main__': # 定義模型並初始化 model = LSTMRNN(TIME_STEPS, INPUT_SIZE, OUTPUT_SIZE, CELL_SIZE, BATCH_SIZE) sess = tf.Session() merged = tf.summary.merge_all() writer = tf.summary.FileWriter("logs", sess.graph) sess.run(tf.initialize_all_variables()) # Tensorboard視覺化展現神經網路結果 #------------------------------RNN學習------------------------------------- # 互動模式啟動 plt.ion() plt.show() # 訓練模型 for i in range(200): # 用seq預測res (序列-seq 結果-res 輸入-xs) seq, res, xs = get_batch() # 第一步賦值 之後會更新cell_init_state if i == 0: feed_dict = { model.xs: seq, model.ys: res, # create initial state (前面cell_init_state已初始化state) } else: feed_dict = { model.xs: seq, model.ys: res, model.cell_init_state: state # use last state as the initial state for this run } # state為final_state _, cost, state, pred = sess.run( [model.train_op, model.cost, model.cell_final_state, model.pred], feed_dict=feed_dict) # plotting # 獲取第一批資料xs[0,:] 獲取0到20區間的預測資料pred.flatten()[:TIME_STEPS] plt.plot(xs[0, :], res[0].flatten(), 'r', xs[0, :], pred.flatten()[:TIME_STEPS], 'b--') plt.ylim((-1.2, 1.2)) plt.draw() plt.pause(0.3) # 每隔20步輸出結果 if i % 20 == 0: print('cost: ', round(cost, 4)) # result = sess.run(merged, feed_dict) # writer.add_summary(result, i)
寫道這裡,這篇文章終於寫完了。文章非常長,但希望對您有所幫助。LSTM RNN通過一組資料預測另一組資料。預測效果如下圖所示,紅色的實線表示需要預測的線,藍色的虛線表示RNN學習的線,它們在不斷地逼近,藍線學到了紅線的規律,最終將藍線基本擬合到紅線上。
六.總結
本文介紹完了,更多TensorFlow深度學習文章會繼續分享,接下來我們會分享監督學習、GAN、機器翻譯、文字識別、影像識別、語音識別等內容。如果讀者有什麼想學習的,也可以私聊我,我去學習並應用到你的領域。
最後,希望這篇基礎性文章對您有所幫助,如果文章中存在錯誤或不足之處,還請海涵~作為人工智慧的菜鳥,我希望自己能不斷進步並深入,後續將它應用於影像識別、網路安全、對抗樣本等領域,指導大家撰寫簡單的學術論文,一起加油!
程式碼下載地址(歡迎大家關注點贊):