深度學習之RNN(迴圈神經網路)

笨拙的石頭發表於2018-05-28

一 RNN概述

    前面我們敘述了BP演算法, CNN演算法, 那麼為什麼還會有RNN呢?? 什麼是RNN, 它到底有什麼不同之處? RNN的主要應用領域有哪些呢?這些都是要討論的問題.

    1) BP演算法,CNN之後, 為什麼還有RNN?

    細想BP演算法,CNN(卷積神經網路)我們會發現, 他們的輸出都是隻考慮前一個輸入的影響而不考慮其它時刻輸入的影響, 比如簡單的貓,狗,手寫數字等單個物體的識別具有較好的效果. 但是, 對於一些與時間先後有關的, 比如視訊的下一時刻的預測,文件前後文內容的預測等, 這些演算法的表現就不盡如人意了.因此, RNN就應運而生了.

    2) 什麼是RNN?

    RNN是一種特殊的神經網路結構, 它是根據"人的認知是基於過往的經驗和記憶"這一觀點提出的. 它與DNN,CNN不同的是: 它不僅考慮前一時刻的輸入,而且賦予了網路對前面的內容的一種'記憶'功能.

    RNN之所以稱為迴圈神經網路,即一個序列當前的輸出與前面的輸出也有關。具體的表現形式為網路會對前面的資訊進行記憶並應用於當前輸出的計算中,即隱藏層之間的節點不再無連線而是有連線的,並且隱藏層的輸入不僅包括輸入層的輸出還包括上一時刻隱藏層的輸出。

    3) RNN的主要應用領域有哪些呢?

    RNN的應用領域有很多, 可以說只要考慮時間先後順序的問題都可以使用RNN來解決.這裡主要說一下幾個常見的應用領域:

    ① 自然語言處理(NLP): 主要有視訊處理文字生成, 語言模型, 影像處理

    ② 機器翻譯, 機器寫小說

    ③ 語音識別

    ④ 影像描述生成

    ⑤ 文字相似度計算

    ⑥ 音樂推薦網易考拉商品推薦Youtube視訊推薦等新的應用領域.

 

二 RNN(迴圈神經網路)

    1) RNN模型結構

    前面我們說了RNN具有時間"記憶"的功能, 那麼它是怎麼實現所謂的"記憶"的呢?

                                                                                      圖1 RNN結構圖 

    如圖1所示, 我們可以看到RNN層級結構較之於CNN來說比較簡單, 它主要有輸入層,Hidden Layer, 輸出層組成.

並且會發現在Hidden Layer 有一個箭頭表示資料的迴圈更新, 這個就是實現時間記憶功能的方法.

    如果到這裡你還是沒有搞懂RNN到底是什麼意思,那麼請繼續往下看!

                                                                               圖2 Hidden Layer的層級展開圖

    如圖2所示為Hidden Layer的層級展開圖. t-1, t, t+1表示時間序列. X表示輸入的樣本. St表示樣本在時間t處的的記憶,St = f(W*St-1 +U*Xt). W表示輸入的權重, U表示此刻輸入的樣本的權重, V表示輸出的樣本權重.

    在t =1時刻, 一般初始化輸入S0=0, 隨機初始化W,U,V, 進行下面的公式計算:

                                                    

    其中,f和g均為啟用函式. 其中f可以是tanh,relu,sigmoid等啟用函式,g通常是softmax也可以是其他。

    時間就向前推進,此時的狀態s1作為時刻1的記憶狀態將參與下一個時刻的預測活動,也就是:

                                                                          

    以此類推, 可以得到最終的輸出值為:

                                                                          

    注意: 1. 這裡的W,U,V在每個時刻都是相等的(權重共享).

             2. 隱藏狀態可以理解為:  S=f(現有的輸入+過去記憶總結

    2) RNN的反向傳播

    前面我們介紹了RNN的前向傳播的方式, 那麼RNN的權重引數W,U,V都是怎麼更新的呢?

    每一次的輸出值Ot都會產生一個誤差值Et, 則總的誤差可以表示為:.

    則損失函式可以使用交叉熵損失函式也可以使用平方誤差損失函式.

    由於每一步的輸出不僅僅依賴當前步的網路,並且還需要前若干步網路的狀態,那麼這種BP改版的演算法叫做Backpropagation Through Time(BPTT) , 也就是將輸出端的誤差值反向傳遞,運用梯度下降法進行更新.(不熟悉BP的可以參考這裡)

    也就是要求引數的梯度:

                                                           

    首先我們求解W的更新方法, 由前面的W的更新可以看出它是每個時刻的偏差的偏導數之和. 

    在這裡我們以 t = 3時刻為例, 根據鏈式求導法則可以得到t = 3時刻的偏導數為:

                                                                           

    此時, 根據公式我們會發現, S3除了和W有關之外, 還和前一時刻S2有關.

    對於S3直接展開得到下面的式子:

                                                                     

 

    對於S2直接展開得到下面的式子:

 

                                                                     

 

    對於S1直接展開得到下面的式子:

 

                                                                      

    將上述三個式子合併得到:

                                                                      

    這樣就得到了公式:

                                                                    

    這裡要說明的是:表示的是S3對W直接求導, 不考慮S2的影響.(也就是例如y = f(x)*g(x)對x求導一樣)

    其次是對U的更新方法. 由於引數U求解和W求解類似,這裡就不在贅述了,最終得到的具體的公式如下:

                                                                   

    最後,給出V的更新公式(V只和輸出O有關):

                                                                     

三 RNN的一些改進演算法

    前面我們介紹了RNN的演算法, 它處理時間序列的問題的效果很好, 但是仍然存在著一些問題, 其中較為嚴重的是容易出現梯度消失或者梯度爆炸的問題(BP演算法長時間依賴造成的). 注意: 這裡的梯度消失和BP的不一樣,這裡主要指由於時間過長而造成記憶值較小的現象.

    因此, 就出現了一系列的改進的演算法, 這裡介紹主要的兩種演算法: LSTM GRU.

    LSTM 和 GRU對於梯度消失或者梯度爆炸的問題處理方法主要是:

 

    對於梯度消失: 由於它們都有特殊的方式儲存”記憶”,那麼以前梯度比較大的”記憶”不會像簡單的RNN一樣馬上被抹除,因此可以一定程度上克服梯度消失問題。

    對於梯度爆炸:用來克服梯度爆炸的問題就是gradient clipping,也就是當你計算的梯度超過閾值c或者小於閾值-c的時候,便把此時的梯度設定成c或-c。 

    1) LSTM演算法(Long Short Term Memory, 長短期記憶網路 ) --- 重要的目前使用最多的時間序列演算法

                            

                                                                                 圖3 LSTM演算法結構圖

    如圖3為LSTM演算法的結構圖. 

    和RNN不同的是: RNN中,就是個簡單的線性求和的過程. 而LSTM可以通過“”結構來去除或者增加“細胞狀態”的資訊,實現了對重要內容的保留和對不重要內容的去除. 通過Sigmoid層輸出一個0到1之間的概率值,描述每個部分有多少量可以通過,0表示“不允許任務變數通過”,1表示“執行所有變數通過 ”.

    用於遺忘的門叫做"遺忘門", 用於資訊增加的叫做"資訊增加門",最後是用於輸出的"輸出門". 這裡就不展開介紹了.

    此外,LSTM演算法的還有一些變種.

     如圖4所示, 它增加“peephole connections”層 , 讓門層也接受細胞狀態的輸入.

                                                     

                                                                              圖4 LSTM演算法的一個變種

    如圖5所示為LSTM的另外一種變種演算法.它是通過耦合忘記門和更新輸入門(第一個和第二個門);也就是不再單獨的考慮忘記什麼、增加什麼資訊,而是一起進行考慮。

                                                     

                                                                                圖5 LSTM演算法的一個變種

 

    2) GRU演算法

    GRU是2014年提出的一種LSTM改進演算法. 它將忘記門和輸入門合併成為一個單一的更新門, 同時合併了資料單元狀態和隱藏狀態, 使得模型結構比之於LSTM更為簡單.

                                                       

    其各個部分滿足關係式如下:

                                                                     

四 基於Tensorflow的基本操作和總結

    使用tensorflow的基本操作如下:

# _*_coding:utf-8_*_

import tensorflow as tf
import numpy as np

'''
    TensorFlow中的RNN的API主要包括以下兩個路徑:
        1) tf.nn.rnn_cell(主要定義RNN的幾種常見的cell)
        2) tf.nn(RNN中的輔助操作)
'''
# 一 RNN中的cell
# 基類(最頂級的父類): tf.nn.rnn_cell.RNNCell()
# 最基礎的RNN的實現: tf.nn.rnn_cell.BasicRNNCell()
# 簡單的LSTM cell實現: tf.nn.rnn_cell.BasicLSTMCell()
# 最常用的LSTM實現: tf.nn.rnn_cell.LSTMCell()
# RGU cell實現: tf.nn.rnn_cell.GRUCell()
# 多層RNN結構網路的實現: tf.nn.rnn_cell.MultiRNNCell()

# 建立cell
# cell = tf.nn.rnn_cell.BasicRNNCell(num_units=128)
# print(cell.state_size)
# print(cell.output_size)

# shape=[4, 64]表示每次輸入4個樣本, 每個樣本有64個特徵
# inputs = tf.placeholder(dtype=tf.float32, shape=[4, 64])

# 給定RNN的初始狀態
# s0 = cell.zero_state(4, tf.float32)
# print(s0.get_shape())

# 對於t=1時刻傳入輸入和state0,獲取結果值
# output, s1 = cell.call(inputs, s0)
# print(output.get_shape())
# print(s1.get_shape())

# 定義LSTM cell
lstm_cell = tf.nn.rnn_cell.LSTMCell(num_units=128)
# shape=[4, 64]表示每次輸入4個樣本, 每個樣本有64個特徵
inputs = tf.placeholder(tf.float32, shape=[4, 48])
# 給定初始狀態
s0 = lstm_cell.zero_state(4, tf.float32)
# 對於t=1時刻傳入輸入和state0,獲取結果值
output, s1 = lstm_cell.call(inputs, s0)
print(output.get_shape())
print(s1.h.get_shape())
print(s1.c.get_shape())

    當然, 你可能會發現使用cell.call()每次只能呼叫一個得到一個狀態, 如有多個狀態需要多次重複呼叫較為麻煩, 那麼我們怎麼解決的呢? 可以參照後面的基於RNN的手寫數字識別單詞預測的例項查詢解決方法.

 

    本文主要介紹了一種時間序列的RNN神經網路及其基礎上衍生出來的變種演算法LSTM和GRU演算法, 也對RNN演算法的使用場景作了介紹. 

 

    當然, 由於篇幅限制, 這裡對於雙向RNNs和多層的RNNs沒有介紹. 另外, 對於LSTM的引數更新演算法在這裡也沒有介紹, 後續補上吧!

    最後, 如果你發現了任何問題, 歡迎一起探討, 共同進步!!

相關文章