吳恩達《序列模型》課程筆記(1)– 迴圈神經網路(RNN)

紅色石頭發表於2018-08-02

《Recurrent Neural Networks》是Andrw Ng深度學習專項課程中的第五門課,也是最後一門課。這門課主要介紹迴圈神經網路(RNN)的基本概念、模型和具體應用。該門課共有3周課時,所以我將分成3次筆記來總結,這是第一節筆記。

1. Why sequence models

序列模型能夠應用在許多領域,例如:

  • 語音識別
  • 音樂發生器

  • 情感分類

  • DNA序列分析

  • 機器翻譯

  • 視訊動作識別

  • 命名實體識別

這些序列模型基本都屬於監督式學習,輸入x和輸出y不一定都是序列模型。如果都是序列模型的話,模型長度不一定完全一致。

2. Notation

下面以命名實體識別為例,介紹序列模型的命名規則。示例語句為:

Harry Potter and Hermione Granger invented a new spell.

該句話包含9個單詞,輸出y即為1 x 9向量,每位表徵對應單詞是否為人名的一部分,1表示是,0表示否。很明顯,該句話中“Harry”,“Potter”,“Hermione”,“Granger”均是人名成分,所以,對應的輸出y可表示為:

一般約定使用y^{< t >}表示序列對應位置的輸出,使用T_y表示輸出序列長度,則1\leq t\leq T_y

對於輸入x,表示為:

同樣,x^{< t >}表示序列對應位置的輸入,T_x表示輸入序列長度。注意,此例中,T_x=T_y,但是也存在T_x\neq T_y的情況。

如何來表示每個x^{< t >}呢?方法是首先建立一個詞彙庫vocabulary,儘可能包含更多的詞彙。例如一個包含10000個詞彙的詞彙庫為:

該詞彙庫可看成是10000 x 1的向量。值得注意的是自然語言處理NLP實際應用中的詞彙庫可達百萬級別的詞彙量。

然後,使用one-hot編碼,例句中的每個單詞x^{< t >}都可以表示成10000 x 1的向量,詞彙表中與x^{< t >}對應的位置為1,其它位置為0。該x^{< t >}為one-hot向量。值得一提的是如果出現詞彙表之外的單詞,可以使用UNK或其他字串來表示。

對於多樣本,以上序列模型對應的命名規則可表示為:X^{(i)< t >}y^{(i)< t >}T_x^{(i)}T_y^{(i)}。其中,i表示第i個樣本。不同樣本的T_x^{(i)}T_y^{(i)}都有可能不同。

3. Recurrent Neural Network Model

對於序列模型,如果使用標準的神經網路,其模型結構如下:

使用標準的神經網路模型存在兩個問題:

第一個問題,不同樣本的輸入序列長度或輸出序列長度不同,即T_x^{(i)}\neq T_x^{(j)}T_y^{(i)}\neq T_y^{(j)},造成模型難以統一。解決辦法之一是設定一個最大序列長度,對每個輸入和輸出序列補零並統一到最大長度。但是這種做法實際效果並不理想。

第二個問題,也是主要問題,這種標準神經網路結構無法共享序列不同x^{< t >}之間的特徵。例如,如果某個x^{< t >}即“Harry”是人名成分,那麼句子其它位置出現了“Harry”,也很可能也是人名。這是共享特徵的結果,如同CNN網路特點一樣。但是,上圖所示的網路不具備共享特徵的能力。值得一提的是,共享特徵還有助於減少神經網路中的引數數量,一定程度上減小了模型的計算複雜度。例如上圖所示的標準神經網路,假設每個x^{< t >}擴充套件到最大序列長度為100,且詞彙表長度為10000,則輸入層就已經包含了100 x 10000個神經元了,權重引數很多,運算量將是龐大的。

標準的神經網路不適合解決序列模型問題,而迴圈神經網路(RNN)是專門用來解決序列模型問題的。RNN模型結構如下:

序列模型從左到右,依次傳遞,此例中,T_x=T_yx^{< t >}\hat y^{< t >}之間是隱藏神經元。a^{< t >}會傳入到第t+1個元素中,作為輸入。其中,$$a^{<0>

RNN的正向傳播(Forward Propagation)過程為:

a^{< t >}=g(W_{aa}\cdot a^{< t-1 >}+W_{ax}\cdot x^{< t >}+ba)

\hat y^{< t >}=g(W_{ya}\cdot a^{< t >}+b_y)

其中,g(\cdot)表示啟用函式,不同的問題需要使用不同的啟用函式。

為了簡化表示式,可以對a^{< t >}項進行整合:

則正向傳播可表示為:

a^{< t >}=g(W_a[a^{< t-1 >},x^{< t >}]+b_a)

\hat y^{< t >}=g(W_{y}\cdot a^{< t >}+b_y)

值得一提的是,以上所述的RNN為單向RNN,即按照從左到右順序,單向進行,\hat y^{< t >}只與左邊的元素有關。但是,有時候\hat y^{< t >}也可能與右邊元素有關。例如下面兩個句子中,單憑前三個單詞,無法確定“Teddy”是否為人名,必須根據右邊單詞進行判斷。

He said, “Teddy Roosevelt was a great President.”

He said, “Teddy bears are on sale!”

因此,有另外一種RNN結構是雙向RNN,簡稱為BRNN。\hat y^{< t >}與左右元素均有關係,我們之後再詳細介紹。

4. Backpropagation through time

針對上面識別人名的例子,經過RNN正向傳播,單個元素的Loss function為:

L^{< t >}(\hat y^{< t >},y^{< t >})=-y^{< t >}log\ \hat y^{< t >}-(1-y^{< t >})log\ (1-\hat y^{< t >})

該樣本所有元素的Loss function為:

L(\hat y,y)=\sum_{t=1}^{T_y}L^{< t >}(\hat y^{< t >},y^{< t >})

然後,反向傳播(Backpropagation)過程就是從右到左分別計算L(\hat y,y)對引數W_{a}W_{y}b_ab_y的偏導數。思路與做法與標準的神經網路是一樣的。一般可以通過成熟的深度學習框架自動求導,例如PyTorch、Tensorflow等。這種從右到左的求導過程被稱為Backpropagation through time。

5. Different types of RNNs

以上介紹的例子中,T_x=T_y。但是在很多RNN模型中,T_x是不等於T_y的。例如第1節介紹的許多模型都是T_x\neq T_y。根據T_xT_y的關係,RNN模型包含以下幾個型別:

  • Many to many: T_x=T_y
  • Many to many: T_x\neq T_y

  • Many to one: T_x>1,T_y=1

  • One to many: T_x=1,T_y>1

  • One to one: T_x=1,T_y=1

不同型別相應的示例結構如下:

6. Language model and sequence generation

語言模型是自然語言處理(NLP)中最基本和最重要的任務之一。使用RNN能夠很好地建立需要的不同語言風格的語言模型。

什麼是語言模型呢?舉個例子,在語音識別中,某句語音有兩種翻譯:

  • The apple and pair salad.
  • The apple and pear salad.

很明顯,第二句話更有可能是正確的翻譯。語言模型實際上會計算出這兩句話各自的出現概率。比如第一句話概率為10^{-13},第二句話概率為10^{-10}。也就是說,利用語言模型得到各自語句的概率,選擇概率最大的語句作為正確的翻譯。概率計算的表示式為:

P(y^{< 1 >},y^{< 2 >},\cdots,y^{< T_y >})

如何使用RNN構建語言模型?首先,我們需要一個足夠大的訓練集,訓練集由大量的單詞語句語料庫(corpus)構成。然後,對corpus的每句話進行切分詞(tokenize)。做法就跟第2節介紹的一樣,建立vocabulary,對每個單詞進行one-hot編碼。例如下面這句話:

The Egyptian Mau is a bread of cat.

One-hot編碼已經介紹過了,不再贅述。還需注意的是,每句話結束末尾,需要加上 < EOS > 作為語句結束符。另外,若語句中有詞彙表中沒有的單詞,用 < UNK > 表示。假設單詞“Mau”不在詞彙表中,則上面這句話可表示為:

The Egyptian < UNK > is a bread of cat. < EOS >

準備好訓練集並對語料庫進行切分詞等處理之後,接下來構建相應的RNN模型。

語言模型的RNN結構如上圖所示,x^{< 1 >}a^{< 0 >}均為零向量。Softmax輸出層\hat y^{< 1 >}表示出現該語句第一個單詞的概率,softmax輸出層\hat y^{< 2 >}表示在第一個單詞基礎上出現第二個單詞的概率,即條件概率,以此類推,最後是出現 < EOS > 的條件概率。

單個元素的softmax loss function為:

L^{< t >}(\hat y^{< t >},y^{< t >})=-\sum_iy_i^{< t >}log\ \hat y_i^{< t >}

該樣本所有元素的Loss function為:

L(\hat y,y)=\sum_tL^{< t >}(\hat y^{< t >},y^{< t >})

對語料庫的每條語句進行RNN模型訓練,最終得到的模型可以根據給出語句的前幾個單詞預測其餘部分,將語句補充完整。例如給出 “Cats average 15”,RNN模型可能預測完整的語句是 “Cats average 15 hours of sleep a day.”

最後補充一點,整個語句出現的概率等於語句中所有元素出現的條件概率乘積。例如某個語句包含y^{< 1 >},y^{< 2 >},y^{< 3 >},則整個語句出現的概率為:

P(y^{< 1 >},y^{< 2 >},y^{< 3 >})=P(y^{< 1 >})\cdot P(y^{< 2 >}|y^{< 1 >})\cdot P(y^{< 3 >}|y^{< 1 >},y^{< 2 >})

7 Sampling novel sequences

利用訓練好的RNN語言模型,可以進行新的序列取樣,從而隨機產生新的語句。與上一節介紹的一樣,相應的RNN模型如下所示:

首先,從第一個元素輸出\hat y^{< 1 >}的softmax分佈中隨機選取一個word作為新語句的首單詞。然後,y^{< 1 >}作為x^{< 2 >},得到\hat y^{< 1 >}的softmax分佈。從中選取概率最大的word作為y^{< 2 >},繼續將y^{< 2 >}作為x^{< 3 >},以此類推。直到產生 < EOS > 結束符,則標誌語句生成完畢。當然,也可以設定語句長度上限,達到長度上限即停止生成新的單詞。最終,根據隨機選擇的首單詞,RNN模型會生成一條新的語句。

值得一提的是,如果不希望新的語句中包含 < UNK > 標誌符,可以在每次產生 < UNK > 時重新取樣,直到生成非 < UNK > 標誌符為止。

以上介紹的是word level RNN,即每次生成單個word,語句由多個words構成。另外一種情況是character level RNN,即詞彙表由單個英文字母或字元組成,如下所示:

Vocabulay=[a,b,c,\cdots,z,.,;,\ ,0,1,\cdots,9,A,B,\cdots,Z]

Character level RNN與word level RNN不同的是,\hat y^{< t >}由單個字元組成而不是word。訓練集中的每句話都當成是由許多字元組成的。character level RNN的優點是能有效避免遇到詞彙表中不存在的單詞 < UNK > 。但是,character level RNN的缺點也很突出。由於是字元表徵,每句話的字元數量很大,這種大的跨度不利於尋找語句前部分和後部分之間的依賴性。另外,character level RNN的在訓練時的計算量也是龐大的。基於這些缺點,目前character level RNN的應用並不廣泛,但是在特定應用下仍然有發展的趨勢。

8. Vanisging gradients with RNNs

語句中可能存在跨度很大的依賴關係,即某個word可能與它距離較遠的某個word具有強依賴關係。例如下面這兩條語句:

The cat, which already ate fish, was full.

The cats, which already ate fish, were full.

第一句話中,was受cat影響;第二句話中,were受cats影響。它們之間都跨越了很多單詞。而一般的RNN模型每個元素受其周圍附近的影響較大,難以建立跨度較大的依賴性。上面兩句話的這種依賴關係,由於跨度很大,普通的RNN網路容易出現梯度消失,捕捉不到它們之間的依賴,造成語法錯誤。關於梯度消失的原理,我們在之前的吳恩達《優化深度神經網路》課程筆記(1)– 深度學習的實用層面已經有過介紹,可參考。

另一方面,RNN也可能出現梯度爆炸的問題,即gradient過大。常用的解決辦法是設定一個閾值,一旦梯度最大值達到這個閾值,就對整個梯度向量進行尺度縮小。這種做法被稱為gradient clipping。

9. Gated Recurrent Unit(GRU)

RNN的隱藏層單元結構如下圖所示:

a^{< t >}的表示式為:

a^{< t >}=tanh(W_a[a^{< t-1 >},x^{< t >}]+b_a)

為了解決梯度消失問題,對上述單元進行修改,新增了記憶單元,構建GRU,如下圖所示:

相應的表示式為:

\tilde c^{< t >}=tanh(W_c[c^{< t-1 >},x^{< t >}]+b_c)

\Gamma_u=\sigma(W_u[c^{< t-1 >},x^{< t >}]+b_u)

c^{< t >}=\Gamma*\tilde c^{< t >}+(1-\Gamma_u)*c^{< t-1 >}

其中,c^{< t-1 >}=a^{< t-1 >}c^{< t >}=a^{< t >}\Gamma_u意為gate,記憶單元。當\Gamma_u=1時,代表更新;當\Gamma_u=0時,代表記憶,保留之前的模組輸出。這一點跟CNN中的ResNets的作用有點類似。因此,\Gamma_u能夠保證RNN模型中跨度很大的依賴關係不受影響,消除梯度消失問題。

上面介紹的是簡化的GRU模型,完整的GRU新增了另外一個gate,即\Gamma_r,表示式如下:

\tilde c^{< t >}=tanh(W_c[\Gamma_r*c^{< t-1 >},x^{< t >}]+b_c)

\Gamma_u=\sigma(W_u[c^{< t-1 >},x^{< t >}]+b_u)

\Gamma_r=\sigma(W_r[c^{< t-1 >},x^{< t >}]+b_r)

c^{< t >}=\Gamma*\tilde c^{< t >}+(1-\Gamma_u)*c^{< t-1 >}

a^{< t >}=c^{< t >}

注意,以上表示式中的 * 表示元素相乘,而非矩陣相乘。

10. Long Short Term Memory(LSTM)

LSTM是另一種更強大的解決梯度消失問題的方法。它對應的RNN隱藏層單元結構如下圖所示:

相應的表示式為:

\tilde c^{< t >}=tanh(W_c[a^{< t-1 >},x^{< t >}]+b_c)

\Gamma_u=\sigma(W_u[a^{< t-1 >},x^{< t >}]+b_u)

\Gamma_f=\sigma(W_f[a^{< t-1 >},x^{< t >}]+b_f)

\Gamma_o=\sigma(W_o[a^{< t-1 >},x^{< t >}]+b_o)

c^{< t >}=\Gamma_u*\tilde c^{< t >}+\Gamma_f*c^{< t-1 >}

a^{< t >}=\Gamma_o*c^{< t >}

LSTM包含三個gates:\Gamma_u\Gamma_f\Gamma_o,分別對應update gate,forget gate和output gate。

如果考慮c^{< t-1 >}\Gamma_u\Gamma_f\Gamma_o的影響,可加入peephole connection,對LSTM的表示式進行修改:

\tilde c^{< t >}=tanh(W_c[a^{< t-1 >},x^{< t >}]+b_c)

\Gamma_u=\sigma(W_u[a^{< t-1 >},x^{< t >},c^{< t-1 >}]+b_u)

\Gamma_f=\sigma(W_f[a^{< t-1 >},x^{< t >},c^{< t-1 >}]+b_f)

\Gamma_o=\sigma(W_o[a^{< t-1 >},x^{< t >},c^{< t-1 >}]+b_o)

c^{< t >}=\Gamma_u*\tilde c^{< t >}+\Gamma_f*c^{< t-1 >}

a^{< t >}=\Gamma_o*c^{< t >}

GRU可以看成是簡化的LSTM,兩種方法都具有各自的優勢。

11. Bidirectional RNN

我們在第3節中簡單提過Bidirectional RNN,它的結構如下圖所示:

BRNN對應的輸出y^{< t >}表示式為:

\hat y^{< t >}=g(W_{y}[a^{\rightarrow < t >},a^{\leftarrow < t >}]+b_y)

BRNN能夠同時對序列進行雙向處理,效能大大提高。但是計算量較大,且在處理實時語音時,需要等到完整的一句話結束時才能進行分析。

12. Deep RNNs

Deep RNNs由多層RNN組成,其結構如下圖所示:

與DNN一樣,用上標[l]表示層數。Deep RNNs中a^{[l]< t >}的表示式為:

a^{[l]< t >}=g(W_a^{[l]}[a^{[l]< t-1 >},a^{[l-1]< t >}]+b_a^{[l]})

我們知道DNN層數可達100多,而Deep RNNs一般沒有那麼多層,3層RNNs已經較複雜了。

另外一種Deep RNNs結構是每個輸出層上還有一些垂直單元,如下圖所示:

至此,第一節筆記介紹完畢!

更多AI資源請關注公眾號:AI有道(ID:redstonewill)

相關文章