《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_y。x^{< 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_a,b_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_x與T_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)