吳恩達老師的RNN課程已經學了一遍了,但總覺得自己學得不夠明白,怎麼辦?再來一遍啊。書讀百遍其義自見嘛!
第一週的課程主要介紹序列模型的應用場景;深度學習中的RNN基礎結構、GRU、LSTM、BRNN;語言模型和序列採樣的知識。
本課程中,我們將學習序列模型,深度學習令人興奮的領域之一。RNN這類模型已經改變了語音識別、自然語言處理等其他領域。在本門課中,你將學習如何自己構建這些模型。我們首先來看一些序列模型派得上用處的例子。
上述這些問題都可以定位成有監督學習,訓練集爲(x,y)。但它們又屬於不同的序列類型問題。有些序列問題的輸入
上一節中,我們看到了序列模型廣泛的應用領域。我們先來定義一些構建序列模型的符號。
假設我們要構建一個序列模型,輸入
那麼在NLP中我們怎樣表示這些單詞呢,
還有一個問題是,如果我們遇到一個不在詞彙表中的單詞該怎麼辦呢?這時候創造一個單詞
本節討論如何構建一個模型、構建一個神經網絡來學習從x到y的映射。我們可以嘗試使用標準神經網絡來解決這個問題,比如,將9個one-hot向量輸入,經過一些隱藏層,再經過輸出層,得到輸出,每個輸出單元告訴我們每個單詞是否是人名的一部分,但是,事實證明,這個方法並不好。如下圖所示,主要有兩個問題:第一,輸入和輸出在不同的例子中長度是不同的。即使我們設置句子的最大長度,可以用特定符號進行填充,使句子達到最大的長度,這仍然不是一個好的表達;第二,這個問題就更嚴重了,這樣簡單的網絡結構並不能共享那些從不同文本位置上學習到的特徵。特別的,比如當Harry在位置1時,我們學到了它是人名的一部分,當Harry出現在別的位置的時候,我們希望模型也能判斷出它是人名的一部分,有點類似與CNN,希望模型對特徵的位置不太敏感。使用更好的錶帶也會減少模型中參數的數量,之前我們使用得到是one-hot向量,每個向量的長度都是10k,這將是一個非常大的輸入層,總的輸入大小將是
下面要介紹的RNN就沒有上述兩個問題。那麼,什麼是RNN呢?如果我們從左到右閱讀句子,第一個單詞是
預測
RNN從左往右掃描數據,每一時刻所使用的參數也是共享的,從輸入x到隱藏層輸出a的參數設爲
下面用這張整潔的圖來說明RNN中的前向計算。前向計算的關鍵計算式如圖中所示,其中,計算隱藏層輸出的激活函數通常用tanh,偶爾也用relu,使用tanh後,我們有其他方法來避免梯度消失,避免方法將在本週後面進行討論。計算輸出層輸出的激活函數通常取決於問題本身,如果是二分類問題就用sigmoid,如果是多分類問題就用softmax。對於NER問題來說,y只可能是0或者1,那麼這裏的激活函數就使用sigmoid。圖中還給出了更普遍的前向傳播公式。
爲了幫助我們構建更復雜的神經網絡,我們將上圖中的前向傳播公式進行簡化。如下圖所示,將畫藍線的部分進行重寫,主要運用分塊矩陣的思想。最終可將前向傳播公式中參數的腳標簡化爲只有一個變量。
我們已經學習了RNN的基礎結構,本節將學習RNN中的後向傳播是怎麼進行的。顯然,與之前一樣,反向傳播的計算步驟與前向傳播是相反的。
前向傳播如圖所示,爲了計算反向傳播,我們還需要一個損失函數。我們先來定義一個元素的損失函數,它對應序列中一個具體的單詞,使用交叉熵損失函數,如下圖所示。然後,對單個單詞的損失函數按照時間求和,就可得到整個序列的損失。反向傳播如紅線所示,通過對損失函數求導,梯度下降法更新,不斷優化參數。
目前爲止,我們只學到了一種RNN的結構,即輸出和輸出等長的RNN。下面我們要學習其他類型的RNNs。
目前爲止,我們已經瞭解了輸出序列和輸出序列等長時的RNN。但對於其他應用來說,
大家是否還記得下圖所示的ppt,這是我們在本週第一課中的ppt。這裏面展示了輸入和輸出的各種情況。比如,音樂生成的例子,輸入就是空集或者一個整數;再比如電影情感分類,輸出就是一個1-5的整數,而輸入確實一個序列;在NER中,輸入和輸出向等長的;但也有一些應用中,輸入和輸出是不等長的,比如機器翻譯。因此,我們可以通過修改基本的RNN模型來處理這些問題。
如下圖所示。
現在來總結一下RNN的結構類型。如下圖所示。如果將一對一的
下面將介紹序列生成的相關內容。
語言建模是NLP中最基礎也是最重要的任務之一。而RNN再這一方面就做得很好。本節我們將學習到如何用RNN來構建一個語言模型。
那麼,什麼是語言模型呢?假設我們在構建一個語音識別系統,聽到了一句」The apple and pear salad was delicious.」 那麼,所說的可能是下面兩種可能的句子,如圖所示。人會認爲說的是第二個句子,而這就是一個好的語音識別系統所要做的事情,儘管這兩個句子聽起來幾乎一樣。而讓語音識別系統選擇第二個句子的方法就是使用語言模型。語言模型能輸出這兩句話出現的概率。比如,某一語言模型計算出兩句話的概率分別如下圖所示。因此,語言模型所做的工作就是,給定任何一句話,告訴我們這句話出現的概率。這一點是語音識別系統和機器翻譯的基礎。因此,語言模型的基本工作就是對輸入的句子序列
那麼,如何建立一個語言模型呢?用RNN建立一個這樣的模型,首先需要一個訓練集,它包含大量英文文本語料,或者是任何一種想要在其之上建立語言模型的語種文本。單詞corpus是NLP中的專有名詞,表示大量文本或句子的集合。
假設語料中有一句話」Cats average 15 hours of sleep a day.」 那麼,我們首先要做的就是,符號化句子(tokenize this sentence),意思就是利用訓練集形成一個詞彙表,然後將每個單詞映射爲詞彙表中的索引,再將索引值轉化爲one-hot向量,記爲
完成符號化步驟之後(即,將輸入句子映射爲在詞彙表中的一個個獨立的符號)。
接下來,我們構建一個RNN來對這些不同序列的概率進行建模。在這個RNN中,我們設
然後,爲了訓練模型,我們需要定義代價函數,由於輸出層是softmax激活函數,我們這裏使用softmax的損失函數,那麼,整個序列的代價就是把各個時刻的損失都相加起來。
而最終,這個序列出現的概率就是把所有的輸出相乘,就得到了這個序列的聯合概率密度。
上述就是訓練RNN語言模型的基本結構,也許一些細節對你來說還比較抽象,別擔心,這周的編程練習會讓你更加清楚。
下面,我們將用語言模型進行一個非常有趣的事情,那就是從語言模型中採樣一個序列。
在你訓練完成一個序列模型之後,一種讓你能大概瞭解到模型學了些什麼的方法就是進行一次序列採樣。我們來看看應該怎麼做。
首先要記得,序列模型對任意特定單詞序列的概率進行建模,即對序列的聯合概率進行建模。而我們要做的就是從這個概率分佈中進行採樣,進而生成新的單詞序列。
模型的訓練使用下圖中上面所示的結構。而對於採樣就稍有不同了,我們要對模型生成的第一個單詞進行採樣。初始輸入仍然是
那麼,怎樣知道序列結束了呢?如果eos是詞彙表中的一部分,那麼我們可以不斷採樣,直到採樣到eos;如果eos不在詞彙表中,我們可以設定採樣的長度,直到序列達到規定長度爲止。不過在這一過程中,也有可能產生unk這個符號,如果你想確定算法不會產生這個符號,一種可行的方法就是拒絕任何unk的輸出,當輸出是unk的時候就繼續採樣。
上述就是如何從RNN語言模型中生成隨機序列的例子。
目前爲止,我們構建了一個單詞層面的RNN模型,即詞彙表中都是一些英語單詞。取決於我們的應用場景,我們還可以構建一個字符層面的RNN模型。在這種情況下,詞彙表就包含了a-z的26個字母、空格符、標點符號、0-9的10個數字,如果想區分大小寫,還可以加上大寫的26個字母。我們可以通過觀察詞彙表出現的字符得到字符層面的詞彙表。在字符層面上,
下面是一些有趣的例子,它們都是從語言模型中採樣出來的。
以上就是基礎的RNN結構,以及如何進行訓練和採樣。接下來將討論訓練RNN的挑戰,以及如何適應這些挑戰,特別是梯度消失問題,通過建立更強大的RNN模型來解決。
我們已經瞭解了RNN是如何工作的、如何運用RNN解決命名實體識別以及語言建模等問題。但是基礎的RNN算法有一個問題,那就是梯度消失問題。我們先來討論這個問題,然後再說明怎麼解決這個問題。
我們看到的RNN都長下圖的樣子。現在,我們以語言模型爲例進行說明。句子如下圖藍色筆跡所示,其中,cat和was對應,cats和were對應。這是一個句子中有長程依賴的例子。但是基礎的RNN結構並不善於捕捉這種長程依賴。爲什麼呢?你可能還記得我們在討論非常深的神經網絡時,說到的梯度消失問題。當網絡很深的時候,就很難將輸出的梯度反向傳播到最初的幾層上。RNN也是同樣的道理。當序列很長的時候,後面時刻的梯度就很難傳播到最初時刻上加以計算。實際上,就是網絡只能記住它前面幾個時刻的信息,隔得太遠就記不住了。除了梯度消失,還有個梯度爆炸的問題。但是梯度爆炸要好發現和解決的多,當你看到計算結果有很多NaN出現,這就說明出現梯度爆炸了,其中一個解決方法就是使用gradient clipping。其思想就是給梯度值設置一個範圍,如果在這個範圍內就用梯度原來的值;如果小於範圍內的最小值,就將梯度設置爲範圍內的最小值;如果大於範圍內的最大值,就將梯度設置爲範圍內的最大值。
相較而言,梯度消失問題就比較難解決了。下面我們將重點討論梯度消失的問題及其解決方法。
我們已經瞭解了基本的RNN是怎樣工作的。本節我們將學習GRU,它是RNN中隱藏層的一種改進,使得RNN可以更好的捕捉長程依賴,從而有助於梯度消失問題的解決。
下圖中激活值的計算想必大家都很熟悉了,將這個表達式轉化成計算圖的形式,就如圖中左邊部分所示。
在GRU單元中,爲了使RNN能具有長程依賴,我們增加一個變量
另一個比GRU還強大和通用的網絡單元就是LSTM。下圖給出了它們兩個的計算對比。LSTM有3個門函數,分別是更新門、遺忘門和輸出門。
將LSTM的計算式轉化成計算圖如下圖右上角所示。而真正的使用了LSTM的RNN結構如下圖下面的結構所示。其中紅線表示的一條路徑就是網絡中的記憶信息的傳輸過程,我們可以通過設置恰當的參數,使得
在此基礎上也有些變體的版本,如綠色筆跡所示,在計算
什麼時候使用GRU,什麼時候使用LSTM並沒有一個統一的標準。GRU 更簡單,因此更容易創建一個更大的網絡,運算也會更快。而LSTM更強大和靈活,因爲它有3個門。如果一定要選擇一個的話,大多數人還是會選擇LSTM
目前爲止,你已經瞭解到了RNN中大多數重要的組件。但還有兩個思想可以幫助你構建更強大的模型。其中一個思想就是雙向RNN,它可以幫助我們在某個時刻同時使用之前和之後的信息。另一個就是深度RNN,我們將在下節介紹。
我們先來看看雙向RNN。例子仍然是我們之前討論的那個NER的問題。下圖中所示的矩形框既可以表示標準的RNN塊,也可以表示GRU或者LSTM。只要這些模塊都是前向的。
那麼,BRNN又是什麼樣子的呢?它的隱藏層有一個前向的循環單元,依次計算各個隱藏單元的輸出;同時,它還有一個後向的循環單元。前、後向的隱藏單元都計算完成後,就可以計算預測結果了。同樣的,下圖中的矩形塊可以是基礎RNN、GRU或LSTM中的一種。事實上,除了實時的語音識別系統用的是更復雜的網絡結構外,對於很多NLP問題,有LSTM單元的BRNN是用的很普遍的。因此,當我們在進行NLP問題時,並且句子都是完整的,就可以使用BRNN,而這也成爲RNN的缺點。
目前我們所看到的不同版本的RNN都能工作的很好。但在學習非常複雜的函數的時候,把多個RNN堆疊起來構建更深的網絡會更有用。本小結將學習如何構建更深的RNN。
下圖左邊畫出了一個標準神經網絡,深度RNN和這個有點像,如下圖所示。並以
有時還會看到的一種網絡結構就是下圖中所示的這樣,在輸出層之前再加幾層隱藏層,但是隱藏層之間沒有水平連接。這種結構我們會見的多一點。同樣的,這些矩陣塊可以是基礎的RNN、GRU或者LSTM,我們也可以把這個網絡構建成雙向的。