
點選上方 藍字 關注我呀!
[Python人工智能] 十一.循環神經網絡RNN和LSTM原理詳解及TensorFlow編寫RNN分類案例
從本專欄開始,正式開始研究Python深度學習、神經網絡及人工智能相關知識。前一篇講解了TensorFlow如何儲存變量和神經網絡參數,通過Saver儲存神經網絡,再通過Restore調用訓練好的神經網絡。本文将詳細講解循環神經網絡RNN和長短期記憶網絡LSTM的原理知識,并采用TensorFlow實作手寫數字識别的RNN分類案例。基礎性文章,希望對您有所幫助,如果文章中存在錯誤或不足之處,還請海涵~
前文:[Python人工智能] 一.TensorFlow2.0環境搭建及神經網絡入門[Python人工智能] 二.TensorFlow基礎及一進制直線預測案例[Python人工智能] 三.TensorFlow基礎之Session、變量、傳入值和激勵函數[Python人工智能] 四.TensorFlow建立回歸神經網絡及Optimizer優化器[Python人工智能] 五.Tensorboard可視化基本用法及繪制整個神經網絡[Python人工智能] 六.TensorFlow實作分類學習及MNIST手寫體識别案例[Python人工智能] 七.什麼是過拟合及dropout解決神經網絡中的過拟合問題[Python人工智能] 八.卷積神經網絡CNN原理詳解及TensorFlow編寫CNN[Python人工智能] 九.Tensorflow+Opencv實作CNN自定義圖像分類案例及與機器學習KNN圖像分類算法對比[Python人工智能] 十.Tensorflow如何儲存神經網絡參數
文章目錄:
- 一.循環神經網絡
- 1.RNN原理
- 2.RNN應用
- 二.LSTM RNN原理詳解
- 1.為什麼引入LSTM
- 2.LSTM
- 三.Tensorflow編寫RNN代碼
一.循環神經網絡
在編寫代碼之前,我們需要介紹什麼是RNN,RNN是怎樣運作的以及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機器翻譯: 輸入和輸出分别兩個,對應的是中文和英文,如下圖所示。
matlab編寫識别手寫數字_[Python人工智能] 十一.循環神經網絡RNN和LSTM原理詳解及TensorFlow編寫RNN分類案例...一.循環神經網絡二.LSTM RNN原理詳解三.Tensorflow編寫RNN代碼
二.LSTM RNN原理詳解
接下來我們看一個更強大的結構,稱為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是延緩記憶的良藥,進而帶來更好的結果。
matlab編寫識别手寫數字_[Python人工智能] 十一.循環神經網絡RNN和LSTM原理詳解及TensorFlow編寫RNN分類案例...一.循環神經網絡二.LSTM RNN原理詳解三.Tensorflow編寫RNN代碼
三.Tensorflow編寫RNN代碼
接下來我們通過手寫數字圖檔集資料編寫RNN代碼。RNN是基于順序的資料,想象下圖檔的順序,它是一行一行像素組成的,最終判定圖檔的數字屬于哪類。
第一步,打開Anaconda,然後選擇已經搭建好的“tensorflow”環境,運作Spyder。
第二步,導入擴充包。
import tensorflow as tffrom tensorflow.examples.tutorials.mnist import input_data
第三步,下載下傳資料集。
由于MNIST資料集是TensorFlow的示例資料,是以我們隻需要下面一行代碼,即可實作資料集的讀取工作。如果資料集不存在它會線上下載下傳,如果資料集已經被下載下傳,它會被直接調用。
# 下載下傳手寫數字圖像資料集mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
第四步,定義參數。
# 設定參數learning_rate = 0.001 # 學習效率train_iters = 100000 # 訓練次數batch_size = 128 # 自定義n_inputs = 28 # MNIST 輸入圖像形狀 28*28 黑白圖檔高度為1n_steps = 28 # time steps 輸入圖像的28行資料n_hidden_units = 128 # 神經網絡隐藏層數量n_classes = 10 # 分類結果 數字0-0
第五步,定義placeholder,用于傳入值xs和ys至神經網絡。
# 設定傳入的值xs和ysx = tf.placeholder(tf.float32, [None, n_steps, n_inputs]) #每張圖檔28*28=784個點y = tf.placeholder(tf.float32, [None, n_classes]) #每個樣本有10個輸出
第六步,定義權重和誤差變量。
權重和偏置包括輸入和輸出值,需要注意其設定的形狀。
# 定義權重 進入RNN前的隐藏層 輸入&輸出weights = { # (28, 128) 'in': tf.Variable(tf.random_normal([n_inputs, n_hidden_units])), # (128, 10) 'out': tf.Variable(tf.random_normal([n_hidden_units, n_classes])),}# 定義偏置 進入RNN前的隐藏層 輸入&輸出biases = { # (128, ) 'in': tf.Variable(tf.constant(0.1, shape=[n_hidden_units, ])), # (10, ) 'out': tf.Variable(tf.constant(0.1, shape=[n_classes, ])),}
第七步,定義RNN神經網絡。
RNN定義分别對應三層,X輸入、Cell為中心計算、H為最終輸出,需要注意資料形狀的變化。在RNN運算過程中,每一步的輸出都存儲在outputs序列中,LSTM包括c_state(主線)和m_state(分線)。最終輸出結果為Cell的輸出和權重輸出的乘積,再加上輸出偏置。(詳見注釋)
#---------------------------------定義RNN-------------------------------def RNN(X, weights, biases): # hidden layer for input to cell ####################################################### # X (128 batch, 28 steps, 28 inputs) 28行*28列 # X ==> (128*28, 28 inputs) X = tf.reshape(X, [-1, n_inputs]) # 隐藏層 輸入 # X_in ==> (128batch*28steps, 128 hidden) X_in = tf.matmul(X, weights['in']) + biases['in'] # 二維資料轉換成三維資料 # 注意:神經網絡學習時要注意其形狀如何變化 # X_in ==> (128 batch, 28 steps, 128 hidden) X_in = tf.reshape(X_in, [-1, n_steps, n_hidden_units]) # 128個隐藏層 # cell ####################################################### # Cell結構 隐藏層數 forget初始偏置為1.0(初始時不希望forget) lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(n_hidden_units, forget_bias=1.0, state_is_tuple=True) # RNN會保留每一步計算的結果state # lstm cell is divided into two parts (c_state, m_state) 主線c_state 分線m_state _init_state = lstm_cell.zero_state(batch_size, dtype=tf.float32) # RNN運算過程 每一步的輸出都存儲在outputs序列中 # 正常RNN隻有m_state LSTM包括c_state和m_state outputs, states = tf.nn.dynamic_rnn(lstm_cell, X_in, initial_state=_init_state, time_major=False) # hidden layer for output as final results ####################################################### # 第三層加工最終的輸出 # 最終輸出=Cell的輸出*權重輸出+偏置資料 # states包含了主線劇情和分線劇情 states[1]表示分線劇情的結果 即為outputs[-1]最後一個輸出結果 results = tf.matmul(states[1], weights['out']) + biases['out'] # 第二種方法 # 解包 unpack to list [(batch, outputs)..] * steps #outputs = tf.unstack(tf.transpose(outputs, [1,0,2])) # states is the last outputs #results = tf.matmul(outputs[-1], weights['out']) + biases['out'] return results
第八步,定義誤差和準确度。
#---------------------------------定義誤差和訓練-------------------------------pre = RNN(x, weights, biases)# 預測值與真實值誤差cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=pre, labels=y))# 訓練學習 學習效率設定為0.001train_step = tf.train.AdamOptimizer(learning_rate).minimize(cost) #梯度下降減小誤差# 預測正确個數correct_pred = tf.equal(tf.argmax(pre, 1), tf.argmax(y, 1))# 準确度accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
第九步,初始化及訓練。
#---------------------------------初始化及訓練-------------------------------init = tf.initialize_all_variables()with tf.Session() as sess: sess.run(init) step = 0 # 循環每次提取128個樣本 while step * batch_size < train_iters: # 從下載下傳好的資料集提取128個樣本 batch_xs, batch_ys = mnist.train.next_batch(batch_size) # 形狀修改 [128, 28, 28] batch_xs = batch_xs.reshape([batch_size, n_steps, n_inputs]) # 訓練 sess.run([train_step], feed_dict={ x: batch_xs, y: batch_ys, }) # 每隔20步輸出結果 if step % 20 == 0: # 20*128 print(sess.run(accuracy, feed_dict={ x: batch_xs, y: batch_ys, })) step += 1
最終輸出結果如下所示,可以看到,最早預測的準确度結果非常低為2.187%,最後提升到了96.87%,其結果高于之前的一般神經網絡的結果87.79%(第六篇部落格),由此可見TensorFlow RNN的分類學習效果還不錯,并且在不斷學習中。
Extracting MNIST_data\train-images-idx3-ubyte.gzExtracting MNIST_data\train-labels-idx1-ubyte.gzExtracting MNIST_data\t10k-images-idx3-ubyte.gzExtracting MNIST_data\t10k-labels-idx1-ubyte.gz0.21875000.67968750.82812500.82031250.83593750.89843750.88281250.83593750.9062500....0.98437500.96093750.94531250.96093750.97656250.93750000.99218750.96093750.99218750.9687500
完整代碼如下:
# -*- coding: utf-8 -*-import tensorflow as tffrom tensorflow.examples.tutorials.mnist import input_data# 下載下傳手寫數字圖像資料集mnist = input_data.read_data_sets('MNIST_data', one_hot=True)# 設定參數learning_rate = 0.001 # 學習效率train_iters = 100000 # 訓練次數batch_size = 128 # 自定義n_inputs = 28 # MNIST 輸入圖像形狀 28*28 黑白圖檔高度為1n_steps = 28 # time steps 輸入圖像的28行資料n_hidden_units = 128 # 神經網絡隐藏層數量n_classes = 10 # 分類結果 數字0-0#-----------------------------定義placeholder輸入-------------------------# 設定傳入的值xs和ysx = tf.placeholder(tf.float32, [None, n_steps, n_inputs]) #每張圖檔28*28=784個點y = tf.placeholder(tf.float32, [None, n_classes]) #每個樣本有10個輸出# 定義權重 進入RNN前的隐藏層 輸入&輸出weights = { # (28, 128) 'in': tf.Variable(tf.random_normal([n_inputs, n_hidden_units])), # (128, 10) 'out': tf.Variable(tf.random_normal([n_hidden_units, n_classes])),}# 定義偏置 進入RNN前的隐藏層 輸入&輸出biases = { # (128, ) 'in': tf.Variable(tf.constant(0.1, shape=[n_hidden_units, ])), # (10, ) 'out': tf.Variable(tf.constant(0.1, shape=[n_classes, ])),}#---------------------------------定義RNN-------------------------------def RNN(X, weights, biases): # hidden layer for input to cell ####################################################### # X (128 batch, 28 steps, 28 inputs) 28行*28列 # X ==> (128*28, 28 inputs) X = tf.reshape(X, [-1, n_inputs]) # 隐藏層 輸入 # X_in ==> (128batch*28steps, 128 hidden) X_in = tf.matmul(X, weights['in']) + biases['in'] # 二維資料轉換成三維資料 # 注意:神經網絡學習時要注意其形狀如何變化 # X_in ==> (128 batch, 28 steps, 128 hidden) X_in = tf.reshape(X_in, [-1, n_steps, n_hidden_units]) # 128個隐藏層 # cell ####################################################### # Cell結構 隐藏層數 forget初始偏置為1.0(初始時不希望forget) lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(n_hidden_units, forget_bias=1.0, state_is_tuple=True) # RNN會保留每一步計算的結果state # lstm cell is divided into two parts (c_state, m_state) 主線c_state 分線m_state _init_state = lstm_cell.zero_state(batch_size, dtype=tf.float32) # RNN運算過程 每一步的輸出都存儲在outputs序列中 # 正常RNN隻有m_state LSTM包括c_state和m_state outputs, states = tf.nn.dynamic_rnn(lstm_cell, X_in, initial_state=_init_state, time_major=False) # hidden layer for output as final results ####################################################### # 第三層加工最終的輸出 # 最終輸出=Cell的輸出*權重輸出+偏置資料 # states包含了主線劇情和分線劇情 states[1]表示分線劇情的結果 即為outputs[-1]最後一個輸出結果 results = tf.matmul(states[1], weights['out']) + biases['out'] # 第二種方法 # 解包 unpack to list [(batch, outputs)..] * steps #outputs = tf.unstack(tf.transpose(outputs, [1,0,2])) # states is the last outputs #results = tf.matmul(outputs[-1], weights['out']) + biases['out'] return results#---------------------------------定義誤差和訓練-------------------------------pre = RNN(x, weights, biases)# 預測值與真實值誤差cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=pre, labels=y))# 訓練學習 學習效率設定為0.001train_step = tf.train.AdamOptimizer(learning_rate).minimize(cost) #梯度下降減小誤差# 預測正确個數correct_pred = tf.equal(tf.argmax(pre, 1), tf.argmax(y, 1))# 準确度accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))#---------------------------------初始化及訓練-------------------------------init = tf.initialize_all_variables()with tf.Session() as sess: sess.run(init) step = 0 # 循環每次提取128個樣本 while step * batch_size < train_iters: # 從下載下傳好的資料集提取128個樣本 batch_xs, batch_ys = mnist.train.next_batch(batch_size) # 形狀修改 [128, 28, 28] batch_xs = batch_xs.reshape([batch_size, n_steps, n_inputs]) # 訓練 sess.run([train_step], feed_dict={ x: batch_xs, y: batch_ys, }) # 每隔20步輸出結果 if step % 20 == 0: # 20*128 print(sess.run(accuracy, feed_dict={ x: batch_xs, y: batch_ys, })) step += 1
注意,在運作代碼過程中可能會報錯“ValueError: Variable rnn/basic_lstm_cell/kernel already exists, disallowed. Did you mean to set reuse=True or reuse=tf.AUTO_REUSE in VarScope?”
在Spyder中有kernel選項,點選選擇 “ Restart & RunAll ” 重新運作代碼即可解決問題。
希望文章對大家有所幫助,如果有錯誤或不足之處,還請海涵。 真心想把自己所學所感所做分享出來,還請各位多多指教,真誠邀請您的關注!謝謝。
點個贊,證明你還愛我