本文將會使用大量的圖片和公式推導通俗易懂地講解RNN,LSTM,Seq2Seq和attention注意力機制(結合colah's blog 和CS583),但願幫助初學者更好掌握且入門。node
目錄
-
RNN -
LSTM -
Seq2Seq -
注意力機制 -
參考
RNN(遞歸神經網絡)
咱們知道人類並非從零開始思考東西,就像你讀這篇文章的時候,你對每一個字的理解都是創建在前幾個字上面。你讀完每一個字後並非直接丟棄而後又從零開始讀下一個字,由於你的思想是具備持續性的,不少東西你要經過上下文才能理解。git
然而傳統的神經網絡並不能作到持續記憶理解這一點,這是傳統神經網絡的主要缺點。舉個例子,你打算使用傳統的神經網絡去對電影裏每一個時間點發生的事情進行分類的時候,傳統的神經網絡先讓不能使用前一個事件去推理下一個事件。
github
RNN(遞歸神經網絡)能夠解決這個問題。他們是帶有循環的神經網絡,容許信息在其中保留。在上圖中,A表明神經網絡主體, 表示網絡的輸入, 表示網絡的輸出。循環結構容許信息從當前輸出傳遞到下一次(下個時間點)的網絡輸入。web
這些循環讓遞歸神經網絡看起來有點神祕,然而若是你再思考一下,RNN其實和傳統的神經網絡並無太多的不一樣。RNN能夠看做是一個網絡的屢次拷貝,其中每次網絡的輸出都是下一次的輸入。咱們能夠思考一下咱們若是展開這個循環結構會是什麼樣的:這種像是鏈狀的網絡結構代表RNN和序列以及列表有着自然的聯繫,他們是處理這些序列數據的自然的神經網絡。並且很明顯咱們能夠看出,輸入輸出的序列是具備相同的時間長度的,其中的每個權值都是共享的(不要被鏈式形狀誤導,本質上只有一個cell)。數組
在最近的幾年,RNN在不少問題上都取得了成功:好比語音識別,語音模型,翻譯,圖片註釋等等,可是RNN存在着梯度消息/爆炸以及對長期信息不敏感的問題,因此LSTM就被提出來了。如今不少問題的成功都必須歸功於LSTM,它是遞歸神經網絡的一種,它在許多的任務中表現都比普通的RNN更好,因此接下來咱們來探索一下這個神奇的網絡。微信
LSTM
長期依賴問題
人們但願RNN能夠將一些以前的信息鏈接到當前的任務中來,好比使用以前的視頻幀來幫助理解當前幀。若是RNN能夠作到將會很是有用。那實際RNN能作到嗎?這要視狀況而定。網絡
有時候,咱們只須要當前的信息來完成當前的任務。舉個例子,一個語音模型試圖基於以前的單詞去預測下一個單詞。若是咱們嘗試預測「the clouds are in the sky」,咱們不須要太多的上下文信息——很明顯最後一個單詞會是sky。在像這樣不須要太多的相關信息的場合下,RNN能夠學習到以前使用的信息。可是咱們要注意,也有不少場景須要使用更多的上下文。當咱們試圖去預測「I grew up in France… I speak fluent French」這句話的最後一個單詞,最近的信息會代表這應該是一種語言的名字,可是若是咱們須要知道具體是哪種語語言,咱們須要France這個在句子中比較靠前的上下文信息,相關信息和須要預測的點的間隔很大的狀況是常常發生的。app
不幸的是,隨着間隔變大,RNN變得沒法鏈接到太前的信息。理論上RNN徹底能夠處理這種長期依賴(long-term dependencies)的問題。人們能夠經過當心地選擇參數來解決這個問題。使人悲傷的是,實踐代表RNN並不能很好地解決這個問題,Hochreiter (1991) [German] and Bengio, et al. (1994)發現了RNN爲何在這些問題上學習很困難的緣由。編輯器
而LSTM則沒有這個問題。svg
LSTM網絡
長期短時間記憶網絡-一般叫作LSTM-是一種特殊結構的RNN,它可以學習長期依賴。它在大量的問題有驚人的效果,如今已經被普遍使用。
LSTM被明確設計來避免長期依賴問題,記住長時間的信息對LSTM來講只是常規操做,不像RNN那樣費力不討好。
全部的RNN都有不斷重複網絡自己的鏈式形式。在標準的RNN中,這個重複複製的模塊只有一個很是簡單的結果。例如一個tanh層:LSTM也有這樣的鏈式結構,可是這個重複的模塊和上面RNN重複的模塊結構不一樣:LSTM並非只是增長一個簡單的神經網絡層,而是四個,他們以一種特殊的形式進行交互:讀者不須要擔憂看不懂,接下來咱們將會一步步理解這個LSTM圖。首先咱們先了解一下圖中的符號:在上圖中,每條線表示一個向量,從一個輸出節點到其餘節點的輸入節點。粉紅色的圓圈表示逐點式操做,就像向量加法。黃色的盒子是學習好的神經網絡層。線條合表明聯結,線條分叉則表示內容被複制到不一樣的地方。
LSTM背後的核心思想
LSTM的核心之處就是它的cell state(神經元狀態),在下圖中就是那條貫穿整個結果的水平線。這個cell state就像是一個傳送帶,他只有很小的線性做用,但卻貫穿了整個鏈式結果。信息很容易就在這個傳送帶上流動可是狀態卻不會改變。cell state上的狀態至關於長期記憶,而下面的 則表明短時間記憶。LSTM有能力刪除或者增長cell state中的信息,這一個機制是由被稱爲門限的結構精心設計的。
門限是一種讓信息選擇性經過的方式,它們是由sigmoid神經網絡層和逐點相乘器作成的。sigmoid層輸出0和1之間的數字來描述一個神經元有多少信息應該被經過。輸出0表示這些信息所有不能經過,而輸出1則表示讓全部信息都經過。
一個LSTM有三個這樣的門限,去保護和控制神經元的狀態。
一步步推導LSTM
LSTM的第一步就是決定什麼信息應該被神經元遺忘。這是一個被稱爲「遺忘門層」的sigmod層組成。他輸入 和 (上一次的輸出以及這輪的輸入),而後在 的每一個神經元狀態輸出0和1之間的數字。同理1表示徹底保留這些信息,0表示徹底遺忘這個信息。
讓咱們再次回到一開始舉的例子:根據以前的詞語去預測下一個單詞的語言模型。在這個問題中,cell state或許包括當前主語中的性別信息,因此咱們可使用正確的代詞。而當咱們看到一個新的主語(輸入),咱們會去遺忘以前的性別信息。咱們使用下圖中的公式計算咱們的「遺忘係數」 下一步就是決定咱們要在cell state中保留什麼信息。這包括兩個部分。首先,一個被稱爲「輸入門層」的sigmoid層會決定咱們要更新的數值。而後一個tanh層生成一個新的候選數值 ,它會被增長到cell state中。在下一步中,咱們將會組合這兩步去生成一個新的更新狀態值。
在那個語言模型例子中,咱們想給cell state增長主語的性別,來替換咱們將要遺忘的舊的主語。如今是時候去更新舊的神經元狀態 到新的神經元狀態 。以前咱們已經決定了要作什麼,下一步咱們就去作。
咱們給舊的狀態乘一個遺忘係數 ,來遺忘掉咱們以前決定要遺忘的信息,而後咱們增長 。這是新的候選值,由咱們想多大程度更新每一個狀態的值決定。
在語言模型中,就像上面描述的,這是咱們實際上要丟棄以前主語的性別信息,增長新的主語的性別信息的地方。最後,咱們須要決定咱們要輸出什麼。這個輸出是創建在咱們的cell state的基礎上,可是這裏會有一個濾波器。首先,咱們使用sigmoid層決定哪一部分的神經元狀態須要被輸出;而後咱們讓cell state通過tanh(讓輸出值變成-1到1之間)層而且乘上sigmod門限的輸出,這樣咱們就只輸出咱們想要輸出的。
對於那個語言模型的例子,當咱們看到一個新的主語的時候,或許咱們想輸出相關動詞的信息,由於動詞是跟在主語後面的。例如,它或許要輸出主語是單數仍是複數的,而後咱們就知道主語後動詞的語態了。
LSTM的一些變體
上面講的都是一些常規的LSTM,但並非全部的LSTM都是上面這種形式。實際上如今不少包含LSTM的論文都有小的差別,可是它值得一提。
Gers & Schmidhuber (2000)引入了一個流行的LSTM變體,它增長了一個窺視孔鏈接。這意味着咱們讓門限層監視cell state的狀態。上圖中給每個門限都增長了窺視孔,可是有些論文只是給一部分的門限增長窺視孔,並非所有都加上。
另一個變體是使用組合遺忘和輸入門,而不是分開決定哪些神經元須要遺忘信息,哪些須要增長新的信息,咱們組合起來決定。咱們只遺忘那些須要被放入新信息的狀態,一樣咱們旨在舊信息被遺忘以後才輸入新的信息。一個更神奇的LSTM變體是門遞歸單元(也就是你們常說的GRU),它組合遺忘門和輸入門爲一個更新門,它合併了cell state和隱層狀態,而且作了一些其餘的改變。最終這個模型比標準的LSTM更簡單,而且變得愈來愈流行。這裏只介紹了幾個最有名的LSTM的變體,還有更多變體沒有介紹,就像Yao, et al.(2015)深度門遞歸神經網絡(Depth Gated RNNs)。這裏也有一些處理長期依賴問題問題的徹底不一樣的方法,就像Koutnik, et al(2014)提出的時鐘機遞歸神經網絡(Clockwork RNNs)。
結論
咱們一開始提到人們使用RNN取得了卓越的成果,但其實本質上都是使用LSTM取得的,他們的確在多數任務上表現得更好。
寫下來一系列等式之後,LSTM看起來挺嚇人,但在文中一步步解釋後它變得能夠理解了。咱們不由想問:是否有比LSTM更好的模型?學者一致認爲:那就是attention注意力機制。核心觀點就是讓RNN每一步都監視一個更大的信息集合並從中挑選信息。例如:若是你使用RNN去爲一個圖像生成註釋,它會從圖像中挑選一部分去預測輸出的單詞。接下來在講解attention以前,咱們會先聊聊Seq2Seq。
Seq2Seq
我將會結合一個機器翻譯的例子來給你們形象地介紹Seq2Seq。在這個例子中,咱們試圖將英語轉換爲德語,這裏要注意這裏是一個多對多的模型,並且輸入和輸出的長度都不固定。
準備數據
由於只是作一個例子,因此咱們在www.manythings.org/anki/這個網站選一個小規模的數據來訓練一個簡單的Seq2Seq便可,咱們能夠看到左邊是英語句子,右邊則是翻譯的德語句子。咱們先進行一下預處理,好比把大寫字母變成小寫,把標點符號去掉等等。預處理完以後咱們要作tokenization,即把一句話分紅不少個單詞或者字符,這裏要注意作tokenization的時候要用兩個tokenization,英語用一個,德語用一個;tokenization以後要創建兩個字典,一個英語字典,一個德語字典,後面會解釋我爲何要這麼作。tokenization能夠是char-level,也能夠是word-level,顧名思義前者就是會把一句話分爲一個個字符,然後者則會把一句話分紅一個個單詞,爲了簡單方便,咱們使用char-level來講明。通過tokenization以後一句話變成了一個list,每一個元素都是一個字符,但實際中通常都使用word-level,由於他們的數據集足夠大,這在以後會解釋。咱們前面說了tokenization要用兩個不一樣的字典,這是由於不一樣的語言它的字母表不一樣,沒法進行統一的映射,如上圖所示。若是你使用word-level,那就更有必要使用兩個不一樣的字典,好比不少德語單詞在英語字典中是找不到的,並且不一樣語言分詞方便也是不同的。左邊是英語字典,包括26個字母和一個空格符,德語字典刪去了一些不經常使用字母后再加入空格符,另外能夠發現德語字典多了一個起始符和一個終止符,這裏用什麼都行,只要別跟字典字符衝突就能夠,後面你們就知道這兩個符號的做用。tokenization結束以後每句話就變成了一個字符字典,而後原字符通過字典映射後就變成了下面這個序列,對於德語也是同樣。接下來咱們還能夠把這些數字變成One-hot向量表示,黑色表示1,白色表示0。通過One-hot每一個字符就變成了一個向量,每句話就變成了一個矩陣,這就是咱們的輸入,如今數組準備好了,咱們來搭建咱們的Seq2Seq模型。
搭建並訓練Seq2Seq模型
Seq2Seq有一個編碼器和一個解碼器,編碼器通常是LSTM或者其餘模型用於提取特徵,它的最後一個輸出就是從這句話得出的最後的特徵,而其餘的隱層輸出都被丟棄。編碼器提取特徵以後就到了解碼器,解碼器靠編碼器最後輸出的特徵也就是 來知道這句話是"go away",這裏要強調一下Decoder的初始狀態就是Encoder的最後一個狀態,如今Decoder開始輸出德語字母,這裏Decoder也是一個LSTM模型,他每次接受一個輸入而後輸出下一個字母的機率,第一個輸入必須是起始符,這就是咱們爲何要在德語字典中要加入起始符的緣由。Decoder會輸出一個機率分佈p向量,起始符後面的第一個字母是m,咱們將m作一個one-hot編碼做爲y標籤,用標籤y和預測p作一個CrossEntropy來做爲咱們的損失函數優化,梯度從Decoder傳回Encoder。而後輸入是兩個字符,起始符和m,下一個字母是a,咱們將a作one-hot編碼做爲y標籤,將它與咱們輸出的機率分佈作一個CrossEntropy來做爲損失函數,一直進行這個循環,應該就很好理解了。最後一輪將整句德語做爲輸入,將中止符作標籤y,再進行CrossEntropy,拿全部的英語和德語來訓練咱們的編碼器和解碼器,這就是咱們的訓練過程了。總結一下,咱們使用英語句子的one-hot矩陣做爲encoder的輸入,encoder網絡由LSTM組成來提取特徵,它的輸出是最後一個狀態 和傳送帶 ,decoder網絡的初始狀態是 ,decoder網絡的輸入是德語句子,decoder輸出當前狀態 `,而後全鏈接層輸出下一個字符的預測,這樣咱們的訓練階段就結束了。
預測階段
一樣,咱們先把句子輸入到咱們的Encoder裏面,Encoder會輸入最後狀態 ,做爲這句話的特徵送給Decoder。 做爲Decoder的初始狀態,這樣解碼器就知道這句話是go away,首先把起始符輸入,有了新的狀態解碼器就會把狀態更新爲 而且預測下一個字符,decoder輸出的是每一個字符的機率值,咱們能夠根據這個機率值進行預測,好比咱們能夠選取機率值最大的字符,也能夠對機率進行隨機抽樣,我可能會獲得字符m,因而我把m記錄下來。如今狀態是 ,把新生成的字符m做爲LSTM的輸入,接下來再更新狀態爲 ,而且輸出一個機率分佈,根據機率分佈抽樣咱們獲得字符a,記錄下字符a,並一直進行這個循環。運行14輪了狀態是 ,再結合上一輪生成的字符e,根據decoder輸出的機率分佈抽樣,咱們抽到了終止符,一旦抽到了終止符,就終止文本生成,並返回記錄下的字符串,德語也就被成功翻譯了。
總結
Seq2Seq模型有一個encoder網絡和一個Decoder網絡,在咱們的例子中encoder的輸入是英語句子,每輸入一個詞RNN就會更新狀態並記錄下來,encoder最後一個狀態就是這個句子的特徵,並把以前的狀態丟棄。把這個狀態做爲decoder的初始狀態,初始化後decoder就知道這個句子了,首先把起始符做爲decoder的輸入,而後一步步更新,輸出狀態和機率分佈預測下一個字符,再把預測的字符做爲下一個輸入,重複這個過程,最後直到預測終止符就返回輸出的這個序列。
如何提高?
咱們的encoder和decoder都是LSTM,encoder把全部句子的特徵壓縮到最後一個狀態,理想狀況下encoder最後一個狀態包含完整的信息,假如句子很長,那麼句子有些信息就會被遺忘,那麼Decoder就沒有完整的句子信息,那decoder輸出的德語句子就不完整。一種簡單方法就是使用雙向LSTM,雙向LSTM簡單來講就是用兩條鏈,從左到右這條鏈可能會遺忘最左邊的信息,而從右往左的這條鏈可能會遺忘右邊的信息,這樣結合起來就不容易遺忘句子信息,這裏要注意只是encoder用雙向LSTM,decoder是單向LSTM,他要生成正確順序的序列。此次咱們用的是char-level比較方便,可是最好仍是使用word-level,由於用單詞代替字母,序列就會短大概4.5倍,就不容易遺忘,可是用word-level須要大的數據集,獲得的單詞大概就是一萬,one-hot以後向量的維度也就是一萬,太大了,須要embedding進行降維,由於embedding參數不少,因此若是數據集不夠很容易過擬合。另一種方法改進就是multi-Task learning,咱們還能夠多加入幾個任務,好比讓英語句子讓他本身翻譯成英語句子,這樣encoder只有一個可是數據多了一倍,這樣encoder就能被訓練的更好,固然你還能夠添加其餘語言的任務,經過藉助其餘語言更好訓練encoder,這樣雖然decoder沒有變得更好,可是由於encoder提取的更好最後效果也會變好。
固然還有一個方法就是使用注意力機制,這個對機器翻譯提升做用很大,咱們接下來就講解這個注意力機制。
注意力機制
咱們知道Seq2Seq模型有一個缺點就是句子太長的話encoder會遺忘,那麼decoder接受到的句子特徵也就不徹底,咱們看一下下面這個圖,縱軸BLUE是機器翻譯的指標,橫軸是句子的單詞量,咱們能夠看出用了attention以後模型的性能大大提高。用了注意力機制,Decoder每次更新狀態的時候都會再看一遍encoder全部狀態,還會告訴decoder要更關注哪部分,這也是attention名字的由來。可是缺點就是計算量很大。
attention原理
在encoder結束以後,attention和decoder同時工做,回憶一下,decoder的初始狀態 是encoder最後一個狀態,不一樣於常規的Seq2Seq,encoder全部狀態都要保留,這裏須要計算 與每一個狀態的相關性,我使用 這個公式表示計算二者相關性,把結果即爲 ,記作Weight,encoder有m個狀態,因此一共有m個 ,這裏全部的值都是介於0和1的實數,所有加起來爲1。下面看一下怎麼計算這個類似性。第一種方法是把 和 作concat獲得更高的向量,而後求矩陣W與這個向量的乘積,獲得一個向量,而後再將tanh做用於向量每個元素,將他壓到-1和1之間,最後計算向量V與剛纔計算出來的向量的內積,這裏的向量V和矩陣W都是參數,須要從訓練數據裏學習,算出m個 後,須要對他們作一個softmax變換,把輸出結果記作 到 ,由於是softmax輸出,因此他們都大於0相加爲1,這是第一篇attention論文提出計算的方法,日後有不少其餘計算的方法,咱們來介紹一種更經常使用的方法。輸入仍是 和 ,第一步是分別使用兩個參數矩陣 , 作線性變換,獲得 和 這兩個向量,這兩個參數矩陣要從訓練數據中學習。第二步是計算 與 的內積,因爲有m個K向量,因此獲得L個 。第三步就是對這些值作一個softmax變換, 到 ,由於是softmax輸出,因此他們都大於0相加爲1。這種計算方法被Transformer模型採用,Transformer模型是當前不少nlp問題採用的先進模型。剛纔講了兩種方法來計算 和 的相關性,如今咱們獲得了m個相關性 ,每一個 對應每一個狀態 ,有了這些權重 咱們能夠對m個狀態計算加權平均,獲得一個Context vector 。每個Context vector都會對應一個decoder狀態 接下來咱們來看一下decoder是怎麼計算新的狀態的。咱們來回顧一下,假如不用attention,咱們是這樣更新狀態的,新的狀態 是舊狀態 與新輸入 `的函數,看一下下圖左邊的公式,將二者作concat,而後乘上權重矩陣加上偏置b,最後經過tanh就是咱們的新狀態,也就是說狀態的更新僅僅是根據上一個狀態,並不會看encoder的狀態。用attention的話更新狀態還要用到咱們計算出的Context vector ,把三個參數一塊兒作concat後更新。回憶一下, 是全部encoder狀態 的加權平均,因此 知道輸入 到 的完整信息,decoder新的狀態 依賴於 ,這樣RNN遺忘的問題就解決了。下一步則是計算context vector ,跟以前同樣,先計算權重 ,這裏是計算 跟以前encoder全部狀態的相關性,獲得了m個 ,注意一下這裏的權重也是要更新的,上一輪算的是跟 的相關性如今算的是跟 的相關性,這樣就能夠經過加權平均計算出新的 。Decoder接受新的輸入 ,仍是用那個公式計算出新狀態,而後一直循環下去直到結束。咱們知道在這個過程當中咱們會計算出不少權重 ,咱們思考一下咱們究竟計算了多少個 ?想要計算出一個context vector ,咱們要計算出m個類似性權重 ,因此每輪更新都須要計算m個權重,假如一共有t個state,那麼一共就要計算m×t個權重,也就是encoder和decoder數量的乘積。attention爲了避免遺忘,代價就是高數量級的計算。
權重 的實際意義
這張圖下面是encoder,上面是decoder,attention會把decoder全部狀態與encoder全部狀態計算類似性,也就是 .在這張圖中每條線就對應一個 ,線越粗說明相關性越高。好比下面,法語中的zone就是英語的Area,因此二者的線就很粗。
總結
此次僅僅是從機器翻譯的角度介紹了attention的一個應用,attention在業內仍是有不少應用的,好比self-attention,Transformer應用,但願以此爲印子可以打開讀者attention的大門,以爲有用的讀者能夠點波關注+右下角的在看。
參考
1.colah's bloghttp://colah.github.io/posts/2015-08-Understanding-LSTMs/
2.CS583 https://github.com/wangshusen/DeepLearning
本文分享自微信公衆號 - 計算機視覺漫談()。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。