Course 5-Recurrent Neural Networks--Week 1

吳恩達老師的RNN課程已經學了一遍了,但總覺得自己學得不夠明白,怎麼辦?再來一遍啊。書讀百遍其義自見嘛!
第一週的課程主要介紹序列模型的應用場景;深度學習中的RNN基礎結構、GRU、LSTM、BRNN;語言模型和序列採樣的知識。

1.1 爲什麼需要序列模型?

本課程中,我們將學習序列模型,深度學習令人興奮的領域之一。RNN這類模型已經改變了語音識別、自然語言處理等其他領域。在本門課中,你將學習如何自己構建這些模型。我們首先來看一些序列模型派得上用處的例子。

  • 語音識別中,輸入是一個裁剪過的語音片段 x ,要求將其映射爲文本 y ,這裏的輸入和輸出都是序列數據,前者是語音序列、後者是文字序列。因此,像RNN及其變種的序列模型在語音識別領域非常有用。
  • 音樂生成是另一個有關序列數據的例子,但在這個例子中,只有輸出 y 是序列,輸入可以是空集或是一個指定音樂種類的整數或者是音樂的前幾個音符。
  • 情感分類中,輸入 x 是一個序列,輸出是一個評分。
  • 序列模型在DNA序列分析中也非常有用,DNA是字母A,C,G,T的排列組合。因此,給定一個DNA序列,你可以標記出該DNA序列的哪部分對應一個蛋白質嗎?
  • 機器翻譯中,給定一個輸入序列,要求輸出另外一種語言的翻譯。
  • 視頻行爲識別中,給定一個視頻幀序列,要求識別行爲。
  • 命名實體識別中,給定一個句子,要求確定句子中的人名。

這裏寫圖片描述
上述這些問題都可以定位成有監督學習,訓練集爲(x,y)。但它們又屬於不同的序列類型問題。有些序列問題的輸入 x 和輸出 y 都是序列,在這種情況下,輸入 x 和輸出 y 有可能等長(DNA序列分析,命名實體識別),也有可能不等長(翻譯)。在有些序列問題中,只有輸入 X 或輸出 Y 是序列。

1.2 符號

上一節中,我們看到了序列模型廣泛的應用領域。我們先來定義一些構建序列模型的符號。
假設我們要構建一個序列模型,輸入 x 如圖所示,取自與《哈利波特》系列。現在要構建一個序列模型來告訴我們句子中人名的位置。這就是命名實體識別(NER)的問題,可用於搜索引擎。比如找出最近24小時內新聞提到的人名,進而對這些人名建立索引。NER可用於查找人名、公司名、時間、地點、國名、貨幣名等。在這個例子中,我們要構建一個模型,使得對於輸入中的每一個單詞,都有對應的輸出。如果某個單詞是人名的一部分,則對應輸出位置爲1,否則爲0。技術上來說,這並不是最好的輸出形式,好的表示形式不僅能夠告訴我們這是否是人名的一部分,還能告訴我們人名的起始位置。但在這裏爲了說明問題,採用的是一種比較簡單的方式。我們的輸入是由9個單詞組成的序列,因此,我們會用9個特徵來表示這9個單詞,並將這些特徵索引爲序列中的位置。使用 x<1>,x<2>,,x<t>,,x<9> 來索引輸入中的位置,同樣的,使用 y<1>,y<2>,,y<t>,,y<9> 來索引輸出中的位置。使用 Tx 表示輸入 x 的長度, Ty 表示輸出 y 的長度,本例中 Tx=Ty=9 。結合之前的標記知識,那麼 X(i)<t> 表示第 i 個樣本中的第 t 個元素。因爲 Tx 表示輸入 x 的長度,那麼 T(i)x 就表示第 i 個輸入樣本的長度。
這裏寫圖片描述
那麼在NLP中我們怎樣表示這些單詞呢, x<1> 應該是什麼呢?接下來我們討論一下如何表示這些單詞。要想表示句子中的單詞,我們首先需要一個詞彙表,有時也稱之爲字典。即是一個單詞列表,並且用詞彙表中的單詞表示句子中的單詞。比如詞彙表大小爲10k,如下圖所示。需要注意的是,對於現代NLP來說,10k大小的詞彙表是非常小的,對於商業應用來說,較爲合理的大小是30k到50k,100k也並不奇怪,有些較大的互聯網應用還會用到一百萬甚至更大的詞彙表。選定詞彙表的大小後,怎麼樣構造出詞彙表呢?一種方法就是遍歷訓練集,找出出現頻率最高的前10k個單詞;還有一種方法就是尋找一些在線字典,它們胡告訴你,英語裏使用最頻繁的前10k個單詞是什麼。接下來,我們就可以用one-hot表示法來表示每一個單詞。例子如圖中所示。每個one-hot向量的長度是10k,那麼我們的目的就是學習一個從x到y的映射。
這裏寫圖片描述
還有一個問題是,如果我們遇到一個不在詞彙表中的單詞該怎麼辦呢?這時候創造一個單詞 <UNK> ,來表示所有不在詞彙表中的單詞。

1.3 RNN模型

本節討論如何構建一個模型、構建一個神經網絡來學習從x到y的映射。我們可以嘗試使用標準神經網絡來解決這個問題,比如,將9個one-hot向量輸入,經過一些隱藏層,再經過輸出層,得到輸出,每個輸出單元告訴我們每個單詞是否是人名的一部分,但是,事實證明,這個方法並不好。如下圖所示,主要有兩個問題:第一,輸入和輸出在不同的例子中長度是不同的。即使我們設置句子的最大長度,可以用特定符號進行填充,使句子達到最大的長度,這仍然不是一個好的表達;第二,這個問題就更嚴重了,這樣簡單的網絡結構並不能共享那些從不同文本位置上學習到的特徵。特別的,比如當Harry在位置1時,我們學到了它是人名的一部分,當Harry出現在別的位置的時候,我們希望模型也能判斷出它是人名的一部分,有點類似與CNN,希望模型對特徵的位置不太敏感。使用更好的錶帶也會減少模型中參數的數量,之前我們使用得到是one-hot向量,每個向量的長度都是10k,這將是一個非常大的輸入層,總的輸入大小將是 10k ,第一個隱含層的參數也將非常多。
這裏寫圖片描述
下面要介紹的RNN就沒有上述兩個問題。那麼,什麼是RNN呢?如果我們從左到右閱讀句子,第一個單詞是 x<1> ,我們將其輸入進神經網絡,得到隱藏層輸出,進一步可得到輸出層輸出,告訴我們這個單詞是否是人名的一部分。RNN所做的就是,當讀到句子中第二個單詞 x<2> 時,不僅僅用 x<2>
預測 y<2> ,還會用時刻1時隱藏層計算的輸出 a<1> ;下一時刻也是如此,直到最後時刻 Tx 。本例子 Tx=Ty ,RNN結構如下圖所示;如果 TxTy ,那麼RNN的結構就要做出一些改變。要啓動這個RNN,我們還要給出初始的激活值 a<0> >,有些研究人員會隨機初始化 a<0> >,但用全0向量初始化,也是一個普遍的選擇。
RNN從左往右掃描數據,每一時刻所使用的參數也是共享的,從輸入x到隱藏層輸出a的參數設爲 Wax ,從上一時刻隱藏層輸出a到當前時刻隱藏層輸出a的參數設爲 Waa ,從隱藏層輸出a到輸出y的參數設爲 Wya 。這樣,如下圖所示,在計算 y<3> 時,不僅使用了 x<3> 的信息,還使用了 x<2> x<1> 的信息。不過目前,這個網絡還有一個缺點,就是它在做預測的時候,只使用了當前單詞及其之前的單詞,並沒有使用當前單詞之後的單詞。比如,給定圖中所示兩個句子,它們前三個單詞都是一樣的,但顯然Teddy在第一個句子中時人名的一部分,而在第二個句子中並不是。稍後將介紹雙向循環神經網絡(BRNNs)。不過目前這個單向的RNN就足夠我們說明問題了。
這裏寫圖片描述
下面用這張整潔的圖來說明RNN中的前向計算。前向計算的關鍵計算式如圖中所示,其中,計算隱藏層輸出的激活函數通常用tanh,偶爾也用relu,使用tanh後,我們有其他方法來避免梯度消失,避免方法將在本週後面進行討論。計算輸出層輸出的激活函數通常取決於問題本身,如果是二分類問題就用sigmoid,如果是多分類問題就用softmax。對於NER問題來說,y只可能是0或者1,那麼這裏的激活函數就使用sigmoid。圖中還給出了更普遍的前向傳播公式。
這裏寫圖片描述
爲了幫助我們構建更復雜的神經網絡,我們將上圖中的前向傳播公式進行簡化。如下圖所示,將畫藍線的部分進行重寫,主要運用分塊矩陣的思想。最終可將前向傳播公式中參數的腳標簡化爲只有一個變量。
這裏寫圖片描述

1.4 BPTT

我們已經學習了RNN的基礎結構,本節將學習RNN中的後向傳播是怎麼進行的。顯然,與之前一樣,反向傳播的計算步驟與前向傳播是相反的。
這裏寫圖片描述
前向傳播如圖所示,爲了計算反向傳播,我們還需要一個損失函數。我們先來定義一個元素的損失函數,它對應序列中一個具體的單詞,使用交叉熵損失函數,如下圖所示。然後,對單個單詞的損失函數按照時間求和,就可得到整個序列的損失。反向傳播如紅線所示,通過對損失函數求導,梯度下降法更新,不斷優化參數。
這裏寫圖片描述
目前爲止,我們只學到了一種RNN的結構,即輸出和輸出等長的RNN。下面我們要學習其他類型的RNNs。

1.5 不同類型的RNN網絡

目前爲止,我們已經瞭解了輸出序列和輸出序列等長時的RNN。但對於其他應用來說, Tx Ty 不會總相等,在本小節中,你將看到更豐富的RNN家族。
大家是否還記得下圖所示的ppt,這是我們在本週第一課中的ppt。這裏面展示了輸入和輸出的各種情況。比如,音樂生成的例子,輸入就是空集或者一個整數;再比如電影情感分類,輸出就是一個1-5的整數,而輸入確實一個序列;在NER中,輸入和輸出向等長的;但也有一些應用中,輸入和輸出是不等長的,比如機器翻譯。因此,我們可以通過修改基本的RNN模型來處理這些問題。
這裏寫圖片描述
如下圖所示。

  • NER這種應用,我們稱之爲多對多結構。
  • 情感分類應用,我們稱之爲多對一結構。
  • 當然了,還有一對一結構,這種我們並不感興趣。

這裏寫圖片描述

  • 音樂生成應用,我們稱之爲一對多結構。
  • 機器翻譯應用,我們稱之爲多對多結構,它是一種特殊的多對多,因爲輸入和輸出的長度是不相同的。這種結構由兩部分組成,分別是編碼器和解碼器。
    再這周結束的時候,你將會對這些結構組件有一個更好的理解。從技術上來說,還有一種結構,那就是基於注意的結構。

這裏寫圖片描述
現在來總結一下RNN的結構類型。如下圖所示。如果將一對一的 a<0> 不要,那這就是我們的標準神經網絡。
這裏寫圖片描述
下面將介紹序列生成的相關內容。

1.6 語言模型和序列生成

語言建模是NLP中最基礎也是最重要的任務之一。而RNN再這一方面就做得很好。本節我們將學習到如何用RNN來構建一個語言模型。
那麼,什麼是語言模型呢?假設我們在構建一個語音識別系統,聽到了一句」The apple and pear salad was delicious.」 那麼,所說的可能是下面兩種可能的句子,如圖所示。人會認爲說的是第二個句子,而這就是一個好的語音識別系統所要做的事情,儘管這兩個句子聽起來幾乎一樣。而讓語音識別系統選擇第二個句子的方法就是使用語言模型。語言模型能輸出這兩句話出現的概率。比如,某一語言模型計算出兩句話的概率分別如下圖所示。因此,語言模型所做的工作就是,給定任何一句話,告訴我們這句話出現的概率。這一點是語音識別系統和機器翻譯的基礎。因此,語言模型的基本工作就是對輸入的句子序列 y<1>,y<2>,...,y<Ty> ,計算其出現的概率。
這裏寫圖片描述
那麼,如何建立一個語言模型呢?用RNN建立一個這樣的模型,首先需要一個訓練集,它包含大量英文文本語料,或者是任何一種想要在其之上建立語言模型的語種文本。單詞corpus是NLP中的專有名詞,表示大量文本或句子的集合。
假設語料中有一句話」Cats average 15 hours of sleep a day.」 那麼,我們首先要做的就是,符號化句子(tokenize this sentence),意思就是利用訓練集形成一個詞彙表,然後將每個單詞映射爲詞彙表中的索引,再將索引值轉化爲one-hot向量,記爲 y<1>,y<2>,...,y<Ty> 。我們還要做的一件事是標記句子的結尾。因此,我們還要添加一個額外的標記 <EOS> ,表示end of sentence。如果我們想模型明確捕捉到句子什麼時候結束,就要將EOS標記放在每個句子的結尾處。算上EOS,圖中這句話總共有9個輸入。在我們自己做符號化過程的時候,我們可以自行決定要不要把標點符號考慮在內。顯然,下圖所示的例子忽略了標點符號。如果訓練集中的有些單詞不在詞彙表中,我們應該怎麼辦呢?這時候,我們就把這些單詞替換成一個唯一的符號 <UNK> ,表示unknow word,然後我們只對 <UNK> 進行概率建模,而不再對那些一個個的單詞概率建模。
完成符號化步驟之後(即,將輸入句子映射爲在詞彙表中的一個個獨立的符號)。
這裏寫圖片描述
接下來,我們構建一個RNN來對這些不同序列的概率進行建模。在這個RNN中,我們設 x<t>=y<t1> 。初始狀態下,設 a<0> x<1> 均爲0向量,通過softmax得到 y<1> 最有可能的輸出,也就是得到詞彙表中每個單詞的概率,從第一個單詞a一直到最後一個單詞zulu,再到unk和eos。這樣,softmax的結果就有10002個結果。然後,RNN進入下一時刻,此時的輸入仍然是前一時刻的激活值 a<1> 和當前時刻的輸入值 a<2> ,只不過,這時的 x<2>=y<1> ,輸出 y<2> 同樣經過softmax計算得到,只不過這時相當於在計算一個條件概率 p(y<2>|y<1>) 。之後時刻的輸出也是在計算條件概率,只不過條件是當前預測詞前面所有的單詞。
然後,爲了訓練模型,我們需要定義代價函數,由於輸出層是softmax激活函數,我們這裏使用softmax的損失函數,那麼,整個序列的代價就是把各個時刻的損失都相加起來。
而最終,這個序列出現的概率就是把所有的輸出相乘,就得到了這個序列的聯合概率密度。
這裏寫圖片描述
上述就是訓練RNN語言模型的基本結構,也許一些細節對你來說還比較抽象,別擔心,這周的編程練習會讓你更加清楚。
下面,我們將用語言模型進行一個非常有趣的事情,那就是從語言模型中採樣一個序列。

1.7 採樣新的序列

在你訓練完成一個序列模型之後,一種讓你能大概瞭解到模型學了些什麼的方法就是進行一次序列採樣。我們來看看應該怎麼做。
首先要記得,序列模型對任意特定單詞序列的概率進行建模,即對序列的聯合概率進行建模。而我們要做的就是從這個概率分佈中進行採樣,進而生成新的單詞序列。
模型的訓練使用下圖中上面所示的結構。而對於採樣就稍有不同了,我們要對模型生成的第一個單詞進行採樣。初始輸入仍然是 a<0> x<1> 均爲0向量, y<1> 仍然是一個經過softmax輸出得到的概率分佈,而我們要做的就是在這個softmax輸出上隨機採樣,並把隨機採樣的結果作爲第二時刻的輸入。這裏需要注意的是,在模型訓練過程中, x<t>=y<t1> ;而在採樣序列時, x<t>=y^<t1> 而第二時刻的輸出也是隨機選擇的,後面幾個時刻也是進行相同的操作。
那麼,怎樣知道序列結束了呢?如果eos是詞彙表中的一部分,那麼我們可以不斷採樣,直到採樣到eos;如果eos不在詞彙表中,我們可以設定採樣的長度,直到序列達到規定長度爲止。不過在這一過程中,也有可能產生unk這個符號,如果你想確定算法不會產生這個符號,一種可行的方法就是拒絕任何unk的輸出,當輸出是unk的時候就繼續採樣。
上述就是如何從RNN語言模型中生成隨機序列的例子。
這裏寫圖片描述
目前爲止,我們構建了一個單詞層面的RNN模型,即詞彙表中都是一些英語單詞。取決於我們的應用場景,我們還可以構建一個字符層面的RNN模型。在這種情況下,詞彙表就包含了a-z的26個字母、空格符、標點符號、0-9的10個數字,如果想區分大小寫,還可以加上大寫的26個字母。我們可以通過觀察詞彙表出現的字符得到字符層面的詞彙表。在字符層面上, y<1>,y<2>,...,y<Ty> 就不再是單詞序列了,而是句子序列中的一個個字符。這種表示優缺點並存。優點是不再擔心unknow 單詞的出現;而主要的缺點就是序列的長度變得很長很長。一般的英文句子都是10-20個單詞,包含的字符就很很多,使得單詞之間的依賴關係減弱,計算成本增加。目前在NLP領域還是使用單詞層面的語言模型居多,但隨着硬件的發展,在一些特定領域,如在大量專用或未知單詞的情況下,人們還是會用字符層面的語言模型。
這裏寫圖片描述
下面是一些有趣的例子,它們都是從語言模型中採樣出來的。
這裏寫圖片描述
以上就是基礎的RNN結構,以及如何進行訓練和採樣。接下來將討論訓練RNN的挑戰,以及如何適應這些挑戰,特別是梯度消失問題,通過建立更強大的RNN模型來解決。

1.8 RNN的梯度消失問題

我們已經瞭解了RNN是如何工作的、如何運用RNN解決命名實體識別以及語言建模等問題。但是基礎的RNN算法有一個問題,那就是梯度消失問題。我們先來討論這個問題,然後再說明怎麼解決這個問題。
我們看到的RNN都長下圖的樣子。現在,我們以語言模型爲例進行說明。句子如下圖藍色筆跡所示,其中,cat和was對應,cats和were對應。這是一個句子中有長程依賴的例子。但是基礎的RNN結構並不善於捕捉這種長程依賴。爲什麼呢?你可能還記得我們在討論非常深的神經網絡時,說到的梯度消失問題。當網絡很深的時候,就很難將輸出的梯度反向傳播到最初的幾層上。RNN也是同樣的道理。當序列很長的時候,後面時刻的梯度就很難傳播到最初時刻上加以計算。實際上,就是網絡只能記住它前面幾個時刻的信息,隔得太遠就記不住了。除了梯度消失,還有個梯度爆炸的問題。但是梯度爆炸要好發現和解決的多,當你看到計算結果有很多NaN出現,這就說明出現梯度爆炸了,其中一個解決方法就是使用gradient clipping。其思想就是給梯度值設置一個範圍,如果在這個範圍內就用梯度原來的值;如果小於範圍內的最小值,就將梯度設置爲範圍內的最小值;如果大於範圍內的最大值,就將梯度設置爲範圍內的最大值。
這裏寫圖片描述
相較而言,梯度消失問題就比較難解決了。下面我們將重點討論梯度消失的問題及其解決方法。

1.9 GRU

我們已經瞭解了基本的RNN是怎樣工作的。本節我們將學習GRU,它是RNN中隱藏層的一種改進,使得RNN可以更好的捕捉長程依賴,從而有助於梯度消失問題的解決。
下圖中激活值的計算想必大家都很熟悉了,將這個表達式轉化成計算圖的形式,就如圖中左邊部分所示。
這裏寫圖片描述
在GRU單元中,爲了使RNN能具有長程依賴,我們增加一個變量 c ,並稱之爲記憶單元,爲循環單元提供了記憶能力。並令 c<t>=a<t> ,以下是GRU單元的計算過程

c~<t>=tanh(Wc[c<t1>,x<t>]+bc)

c<t> 決定了每個時刻的記憶。
Γu=σ(Wu[c<t1>,x<t>]+bu)

sigmoid函數的取值基本上不是接近0就是接近1, Γu 決定了什麼時候更新。
c<t>=Γuc~<t>+(1Γu)c<t1>

*表示對應元素的乘積。需要注意的是,如果 Γu=1 ,那麼 c<t>=c~<t> ;如果 Γu=0 ,那麼 c<t>=c<t1> 。這樣就避免了梯度消失問題。
上述一系列式子中, c<t> 表示向量, c~<t> Γu c<t> 具有相同的shape。
這裏寫圖片描述
上述都是簡化過的GRU,下面來討論完整的GRU應該是什麼樣子。(注意,最後一個式子有誤,應該是 (1Γu)c<t1> 。第一個式子中加上 Γr ,其中 r 代表相關性。 Γr 表示 c<t1> c~<t> 之間的相關性。如下圖所示, Γr Γu 的計算方法是一樣的,只不過參數不同。
這裏寫圖片描述

1.10 LSTM

另一個比GRU還強大和通用的網絡單元就是LSTM。下圖給出了它們兩個的計算對比。LSTM有3個門函數,分別是更新門、遺忘門和輸出門。
這裏寫圖片描述
將LSTM的計算式轉化成計算圖如下圖右上角所示。而真正的使用了LSTM的RNN結構如下圖下面的結構所示。其中紅線表示的一條路徑就是網絡中的記憶信息的傳輸過程,我們可以通過設置恰當的參數,使得 c<0> 一直傳輸下去。
在此基礎上也有些變體的版本,如綠色筆跡所示,在計算 ΓuΓfΓo 時,不僅使用 a<t1>,x<t> ,還使用 c<t1> ,這種網絡叫做peephole connection。
這裏寫圖片描述
什麼時候使用GRU,什麼時候使用LSTM並沒有一個統一的標準。GRU 更簡單,因此更容易創建一個更大的網絡,運算也會更快。而LSTM更強大和靈活,因爲它有3個門。如果一定要選擇一個的話,大多數人還是會選擇LSTM

1.11 BiRNN

目前爲止,你已經瞭解到了RNN中大多數重要的組件。但還有兩個思想可以幫助你構建更強大的模型。其中一個思想就是雙向RNN,它可以幫助我們在某個時刻同時使用之前和之後的信息。另一個就是深度RNN,我們將在下節介紹。
我們先來看看雙向RNN。例子仍然是我們之前討論的那個NER的問題。下圖中所示的矩形框既可以表示標準的RNN塊,也可以表示GRU或者LSTM。只要這些模塊都是前向的。
這裏寫圖片描述
那麼,BRNN又是什麼樣子的呢?它的隱藏層有一個前向的循環單元,依次計算各個隱藏單元的輸出;同時,它還有一個後向的循環單元。前、後向的隱藏單元都計算完成後,就可以計算預測結果了。同樣的,下圖中的矩形塊可以是基礎RNN、GRU或LSTM中的一種。事實上,除了實時的語音識別系統用的是更復雜的網絡結構外,對於很多NLP問題,有LSTM單元的BRNN是用的很普遍的。因此,當我們在進行NLP問題時,並且句子都是完整的,就可以使用BRNN,而這也成爲RNN的缺點。
這裏寫圖片描述

1.12 深度RNNs

目前我們所看到的不同版本的RNN都能工作的很好。但在學習非常複雜的函數的時候,把多個RNN堆疊起來構建更深的網絡會更有用。本小結將學習如何構建更深的RNN。
下圖左邊畫出了一個標準神經網絡,深度RNN和這個有點像,如下圖所示。並以 a[2]<3> 爲例,介紹了網絡中的激活單元如何計算。需要中注意的是,同一層的參數都是共享的。對於左邊的標準神經網絡,我們可能見過深度達100層以上的網絡,但是對於RNN來說,三層就已經很多了。因爲要加上時間維度,這樣網絡就變得非常大了。
這裏寫圖片描述
有時還會看到的一種網絡結構就是下圖中所示的這樣,在輸出層之前再加幾層隱藏層,但是隱藏層之間沒有水平連接。這種結構我們會見的多一點。同樣的,這些矩陣塊可以是基礎的RNN、GRU或者LSTM,我們也可以把這個網絡構建成雙向的。
這裏寫圖片描述