1. 程式人生 > >RNN概述-深度學習 -神經網絡

RNN概述-深度學習 -神經網絡

connect 參數更新 自然語言處理 使用 方法 循環 損失函數 鏈式 物體

一 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的參數更新算法在這裏也沒有介紹, 後續補上吧!

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

RNN概述-深度學習 -神經網絡