# 李宏毅_ATDL_Lecture_24 ###### tags: `Hung-yi Lee` `NTU` `Advance Topics in Deep Learning` [課程撥放清單](https://www.youtube.com/channel/UC2ggjtuuWvxrHHHiaDH1dlQ/playlists) ## Gated RNN and Sequence Generation (Recorded at Fall, 2017) [課程連結](https://www.youtube.com/watch?v=T8mGfIy9dWM&list=PLJV_el3uVTsPMxPbjeX7PicgWbY7F8wW9&index=24) ### Recurrent Neural Network ![](https://i.imgur.com/71w2lWv.png) 這邊秒複習RNN,RNN適用於input為sequence的資料,以聲音訊號為例,聲音是以vector-sequence來表示。 另一種說法來說明RNN的話,RNN本身有一個basic function-$f(h,x)$,不論你的輸入有多長,它就是同一個function不斷的反覆被使用。 舉例,有三個輸入$x^1,x^2,x^3$: 1. 第一個輸入$x^1$會帶有$h^0$經過function-$f$,其中$h^0$初始可以是zero vector 2. 輸出會有兩個$y^1,h^1$ 3. 第二個輸入$x^2$搭配$h^1$再經過同一個function-$f$ 4. 得到輸出$y^2,h^2$ 5. $\vdots$ 要注意一點,$h^0$與$h^1$<sub>(即$h$與$h'$)</sub>的輸出維度一定是相同的,因為使用的是同一個function-$f$,維度若不同就無法計算了。 因為使用的是同一個function-$f$,因此參數量並不會因為你的輸入而有所不同,這是RNN的一個優點。 雖然用標準的DNN也是可以處理不同長度的序列資料,但你需要開一個最大長度的輸入來滿足你的可能變化,而且參數量也會因此變大,也更容易overfitting。 ### Deep RNN ![](https://i.imgur.com/EcQ5s7S.png) RNN也可以是deep的,假設我們有兩個basic function-$f_1(h,x),f_2(b,y)$,其中$f_2(b,y)$的output為$b',c$,作法一樣,以相同的function來反覆的計算。 ### Bi-directional RNN ![](https://i.imgur.com/iuznRa9.png) RNN可以是雙向的,兩個function-$f_1,f_2$,但它們的輸入是不同方向的,一個由前到後,一個由後到前,兩個輸出最終再由第三個function-$f_3$來決定最終的輸出-$y$,function-$f_3$的輸入即為$f_1,f_2$的輸出$a^1,c^1$。 ### Naive RNN ![](https://i.imgur.com/oAQ7FAQ.png) RNN的計算邏輯如下說明: * $h'=\sigma(w^h \cdot h+w^i \cdot x)$ * $y = \sigma(w^o \cdot h')$ 其中三個$w$是為各自的權重參數,$y$是由$h'$所計算而來。 ### LSTM ![](https://i.imgur.com/ZdFCvWi.png) 用一個比較不一樣的說明來瞭解LSTM。LSTM本身與一般的RNN在不看內部構造的時候是一樣的<sub>(純看I/O)</sub>,上圖來看很清楚的知道,LSTM的input有三個$x^t, h^{t-1}, c^{t-1}$,然後輸出$y^t, h^t, c^t$。但其實你如果將$h,c$兩個vector併起來看的話,它跟Naive RNN是一模一樣的。 LSTM將input拆分為$c,h$的原因: 1. $c$的變化較慢,這意味著$c^t$與$c^{t-1}$的差異是小的 2. $h$的變化是快的,這代表$h$與$h^{t-1}$的差異是大的。 我們常聽到一個說法,LSTM的好處在於可以記得較久的事情,這個特性就是來自於$c$的變化很慢。 ### LSTM ![](https://i.imgur.com/l08YT58.png) LSTM的內部構造說明: * $x^t$是外部的輸入,$h^{t-1},c^{t-1}$是LSTM內部前一個時步的output * 首先,將$x^t,h^{t-1}$併在一起變為比較長的vector,乘上權重$W$,通過$tanh$之後得到另一個vector-$z$ * $z = tanh(w \cdot (x^th^{t-1}))$ * 簡報上較粗的箭頭就代表乘上一組參數的線性計算 * 再將$x^t,h^{t-1}$的vector乘上另一組權重$W^i$,通過sigmoid function得到$z^i$ * $z^i = \sigma(w^i \cdot (x^th^{t-1}))$ * input gate * 再將$x^t,h^{t-1}$的vector乘上另一組權重$W^f$,通過sigmoid function得到$z^f$ * $z^f = \sigma(w^f \cdot (x^th^{t-1}))$ * forget gate * 再將$x^t,h^{t-1}$的vector乘上另一組權重$W^o$,通過sigmoid function得到$z^o$ * $z^o = \sigma(w^o \cdot (x^th^{t-1}))$ * output gate ### LSTM ![](https://i.imgur.com/6HE4Lan.png) 實作上你也可以將$c^{t-1}$併入向量內,這種作法稱為『peephole』,偷窺孔的意思。因為你已經看到memory cell的內容了。 這麼做的時候要注意到,與$c^{t-1}$相乘的權重部份通常會設置為diagonal,也就是只有對角線有值,其餘為0,這是為了讓參數不要過多,因而造成overfitting,因此強制為0。設置為diagonal的意思就是,$c$的第一維只影響$z$的第一維,這時候只是單純的乘上一個數值,而不是做線性轉換。 ### LSTM ![](https://i.imgur.com/Evyi55C.png) \*符號約定:圈圈裡面一個點代表向量元素相乘 取得四個向量之後的計算邏輯如下說明: * $c^t = z^f \odot c^{t-1} + z^i \odot z$ * 首先由$z^i$決定$z$是否可以被使用 * 再由$z^f$決定$c^{t-1}$的內容要不要在接下來的output被使用 * 可以發現,$c^t$與$c^{t-1}$之間的差異只是單純的元素相乘之後再加一個項目,變動不大。 * $h^t = z^o \odot \tanh(c^t)$ * 由$z^o$決定是否可以輸出 * 這邊使用$\tanh$的用意在於不要讓$c^t$的值out of bounds。 * 實作上有沒有加入$\tanh$對結果的影響非常大 * 因為$h^{t-1}$是經過一連串的計算才變為$h^t$,因此這時候的$h^t$與$h^{t-1}$非常的不像。 * LSTM的一個特性,就是保留了變化很快的$h$以及變化很慢的$c$ * $y^t = \sigma(W'h^t)$ * 這個輸出就是下一個時步的輸入 ### LSTM ![](https://i.imgur.com/JUV500B.png) 得到output之後再反覆相同的計算即可。 註:簡報上的最右邊output應該是$h^{t+1}$ ### GRU ![](https://i.imgur.com/CDvwHUu.png) 相較於LSTM,GRU的參數量是較少的,不像LSTM有分為$h,c$,單純的$h$而以: * 將$h^{t-1}$與$x^t$合併在一起 * 經過兩個不同的權重計算得到$r, z$ * r:reset gate, z:update gate * 將$r$與$h^{t-1}$做元素相乘,將結果與$x^t$合併在一起再乘上一組權重計算得到$h'$ * 接著$z$一人飾二角 * $h^t = z \odot h^{t-1} + (1-z) \odot h'$ * $z$與$h^{t-1}$做元素相乘 * $1-z$與$h'$做元素相乘 * 一邊大,一邊就小,有相互制衡的關係 我們可以發現,$h^t$與$h^{t-1}$是相似的,它們之間的差異就是元素相乘再加上另一個向量而以,也因此在GRU裡面的$h$是比較類似於LSTM的$c$。 GRU可以做到類似於LSTM的事情,也就是讓傳遞給下一個時步的資訊保存比較久。但在GRU內只用三組參數。 對比於LSTM的$c^t = z^f \odot c^{t-1} + z^i \odot z$,GRU的$h^t = z \odot h^{t-1} + (1-z) \odot h'$是類似的計算,可以發現,GRU的$z$是相對於LSTM的fotget gate的角色。 GRU的計算$h^t = z \odot h^{t-1} + (1-z) \odot h'$可以想成是,有新的東西進來才會忘掉舊的東西,GRU是將input gate與forget gate連在一起。 ### LSTM: A Search Space Odyssey ![](https://i.imgur.com/TGs06Uo.png) [論文連結_LSTM: A Search Space Odyssey](https://arxiv.org/abs/1503.04069) [Odyssey](https://zh.wikipedia.org/wiki/%E5%A5%A5%E5%BE%B7%E8%B5%9B) 圖上的每一個點都代表一個LSTM,橫軸代表分類錯誤,縱軸代表該LSTM的訓練時間,不同的顏色代表不同的架構。 幾點觀察: * 青色的點效能非常差(NOAF_沒有output activation function_也就是拿掉$\tanh$) * 黃色的點效能好,但訓練時間也長(FGR_連同上一個時步的$z$也拿來併成一個更長的Vector) * 藍色的點訓練的久,效能也差(NFG_no forget gate) ### LSTM: A Search Space Odyssey ![](https://i.imgur.com/p4u0OEN.png) 論文也整理整個實驗結果,每個column代表一個實驗架構: * 背景長柱代表神經網路的參數量 * 最左邊的V代表目現所說明的標準LSTM * 可以看到CIFG的效能也不錯,CIFG即是GRU,但參數量是較少的 * FGR參數量過多,雖然效果不錯 * 其餘部份就是拿掉其中一個gate的activation function,效果明顯效差 這意味著,標準LSTM是有良好的效能,而GRU在參數量較少情況下也可以有不錯的效果,拿掉peephole對效能影響不大。 ### An Empirical Exploration of Recurrent Network Architectures ![](https://i.imgur.com/K16pIov.png) [論文連結-An Empirical Exploration of Recurrent Network Architectures](http://proceedings.mlr.press/v37/jozefowicz15.pdf) 這是另一篇論文的比較,基本上結果與上面一個結果是類似的: * 每一個column是不同的task * Tanh是原始的RNN * LSTM-f代表拿掉fotget gate,比較LSTM-f, LSTM-i, LSTM-o可以發現,拿掉fotget gate的影響是最大的 * LSTM-b此項為加大fotget gate的bias,這意味著讓LSTM傾向於記住過去的東西,實驗結果來看是有幫助的,這驗證了過去人們所說的,保留過往的經驗有助於模型效能 * GRU沒比較差 * MUT是基因演算法,透過迭代雜交取出好的部份來得到RNN的架構 ### An Empirical Exploration of Recurrent Network Architectures ![](https://i.imgur.com/jcZYOBs.png) 不是很好看出有什麼特點,但有一個共通點,三個模型都有$h_t \odot (1-z)$,這個項目正是GRU所有。 ### Neural Architecture Search with Reinforcement Learning ![](https://i.imgur.com/tR4ueXV.png) 這是google用強化學習硬train一發得到的RNN架構(右部),把每一個圈圈視為可執行的action,而最終的模型reward是高的。這跟玩遊戲不一樣,每一次就是一個LSTM的模型,因此暴力異常。雖然看不出什麼,但是可以看的出來模型中自我學習出了gate的架構。 ### Generation ![](https://i.imgur.com/sWNOUa2.png) 進入課程主題,如何利用RNN來生成句子。 一個句子可以是由character或是word來組成,產生句子的基本概念如下<sub>(以token來函蓋character與word)</sub>: * 拿出你的句子以及function * 以上一個時步的token做為輸入-$x$ * 通常以1-of-N encoding來描述 * 輸出-$y$為所有可以產生的token的分佈 * 由這個分佈來sample出一個token 舉例來說,簡報右上為例,輸入-$x$是我,以1-of-N encoding來表示,因此只會在我的那個索引有值,而輸出-$y$是後面可能的token的機率,『是』的機率0.7,『很』的機率0.3。 ### Generation ![](https://i.imgur.com/F3YIjUj.png) 假設我們已經訓練好一個RNN,用它的產生句子的流程是這樣的: * 起始的部份給定一個代表開始的token(BOS_Begin of Sentence)的輸入-$x^1$ * 輸出-$y^1$代表給定BOS之後產生w的機率,即為$y^1:P(w \vert <BOS>)$ * 假設為『床』,選擇的方式有兩種,sample(隨機)以及argmax(選機率最高 * 如果選擇argmax,那每次生成的句子就可能都是同一句 * 以『床』為輸入-$x^2$,經過basic function-$f$之後得到$y^2$,『前』 * $x^2$是『床』的1-of-N encoding * $y^2:P(w \vert <BOS>,床)$,即看到\<BOS\>與床之後要產生那一個詞彙的機率分佈 * 這邊basic function不只是看輸入\-$x^2$\-『床』,還有$h^1$,這裡面含有\<BOF\>的資訊,因此機器是同時考慮了『床』與『\<BOF\>』 * 以『前』為輸入-$x^3$,經過basic function-$f$之後得到$y^3$,『明』 * $\vdots$ * 最終產生一個結束的token-\<EOS\> * 這是由機器所決定,因此每次產生的長度可能不一樣 ### Generation ![](https://i.imgur.com/nvAGnY4.png) 這邊說明如何訓練RNN,假設我們的訓練資料為:『春眠不覺曉』,流程說明: * 首先,輸入\<BOF\>,我們希望第一個詞彙的輸出-$y^1$跟『春』愈接近愈好 * cross-entropy愈小愈好 * 以『春』為$x^2$,經過basic function,加上$h^1$,我們希望輸出是愈接近『眠』愈好 * $\vdots$ ### Generation ![](https://i.imgur.com/SbQ8J8p.png) 除了生成語句,還可以讓RNN產生圖片,雖然圖片是由pixels所構成,但我們可以將它視為一個特別的句子,將每一個pixel的顏色用對應的顏色寫出來,用相同的架構來訓練這個顏色句子就可以。 ### Generation - PixelRNN ![](https://i.imgur.com/X8K69Bw.png) PixelRNN雖然在架構上有些許調整,但直接以RNN來硬train一發也是可以成功,但要先進一點的話還是需要一點設計,大致原理如下: * 在產生灰色的時候所觀注的應該是藍色,而不是黃色 * 在產生黑色的時候不只參考灰色,還要考慮紅色 細節部份暫時跳過(之前的課程有說明過,可回頭查詢) ### Conditional Generation ![](https://i.imgur.com/bRIaJXG.png) 剛才所提的Generation是讓機器隨機的產生句子,但我們希望機器依我們所給的情境來產生句子,比方說看一段小影片,然後描述這段內容。或者是Chat-bot(聊天機器人),依據輸入來決定Chat-bot的回應。 ### Conditional Generation ![](https://i.imgur.com/zOx6Ftt.png) 假設我們要做的是看一張圖片來產生描述: * 圖片經過CNN產生一個Vector * 將Vector做為Generator的input * 這時候輸出的部份所考慮就不再只是前一個時步的輸出,還包含了圖片的資訊 * 每一個時步都可以將相同的圖片資訊合併前一個時步的輸出做為輸入,避免遺失照片資訊 ### Conditional Generation ![](https://i.imgur.com/RTCJAx8.png) 如果是翻譯或是聊天機器人的話,那Condition的部份就是Sequence,而不是Vector,這時候機器做的事就是給定一個句子,然後生成一個句子,假設這是一個翻譯的項目,要將機器學習翻譯成英文: * 將輸入的句子描述成一個Vector * 這需要再一個RNN,將最後一個時步的輸出取出 * 文獻上這部份稱為Encoder * 以此Vector做為Generator的輸入 * 文獻上這部份稱為Decoder 訓練的時候這兩個是一起訓練的,而不是分開訓練,而這個架構又有一個名稱,『Sequence-to-Sequence Learning』 ### Conditional Generation ![](https://i.imgur.com/KiOcY79.png) [影片連結](https://www.youtube.com/watch?v=e2MpOmyQJw4) [論文連結_Building End-To-End Dialogue Systems Using Generative Hierarchical Neural Network Models](https://arxiv.org/abs/1507.04808) 在聊天機器人也是一樣的作法,假設使用者輸入『Hi』,那就將這個輸入encoder,然後經過decoder得到輸出『Hi』,但這會有一個問題,就是我們希望機器考慮的不單是目前的問題,而是整個上下文。這可以透過調整模型架構來達成,將過去的歷史記錄也做為輸入。 在論文中的作法,先將所有的輸入encode,再用一個RNN來讀入,再產生decode之後的句子。 ### Dynamic Conditional Generation ![](https://i.imgur.com/0Po0jEH.png) 剛才說明Sequence-to-Sequence的作法中,我們會先將一整個語句壓縮成一個vector再做為decoder的input,但實務上這種作法是非常困難的,因為壓縮的過程中可能就會遺漏很多的資訊,因此有了這種進階版的作法,Dynamic Conditional Generation: * 一樣通過一個RNN,但不是在最後一個時步取Vector,而是保留每一個時步的output(encoder pool) * decoder過程中會至encoder pool內選擇它需要的資訊 * 假設翻譯machine,那就至encoder pool中取出$h^1,h^2$做為vector-$c^1$ * 假設翻譯learning,那就至encoder pool中取出$h^3,h^4$做為vector-$c^2$ 至於機器怎麼決定從encoder pool中取那幾個output,這部份下面說明。 ### Machine Translation ![](https://i.imgur.com/EMnxXvX.png) Dynamic Conditional Generation又稱為Attention-based model,以機器翻譯為例: * 將機器學習四個字丟到RNN中,產生四個output-$h^1 \sim h^4$ * 初始化vector-$z^0$ * 手動設置,或者視為參數讓機器自己學習 * 讓$z^0$與$h^1$通過match function得到$\alpha^1_0$ * 要從資料庫(encoder pool)中取資訊的時候,會由match function決定$h^1$有多相關,而$z^0$的角色就像是一把key,從資料庫中找出最相關的詞彙。 * $\alpha^1_0$,scalar,代表相關性。 match function可以由自己設置: * Cosine similarity<sub>(沒有參數計算需求)</sub> * 可以是一個`nn`,輸入兩個vector,輸出為scalar * 可以設置為$\alpha=h^TW_z$,其中$W$是學習出來的 當match function是有參數的情況下,訓練的方式就是jointly learned就可以,與encoder、decoder一起訓練。 ### Machine Translation ![](https://i.imgur.com/37gWT4f.png) 有了match function之後就可以對機器學習的四個output都做計算: * 以match function計算得到$\alpha_0^1 \sim \alpha_0^4$ * 對$\alpha_0^1 \sim \alpha_0^4$做softmax得到$\hat{\alpha}_0^1 \sim \hat{\alpha}_0^4$ * 這部份可有可無 * 對$\hat{\alpha}_0^1 \sim \hat{\alpha}_0^4$做weighted sum得到$c^0$ * $c^0 = \hat{\alpha}_0^1 \times h^1 + \hat{\alpha}_0^2 \times h^2 + \hat{\alpha}_0^3 \times h^3 + \hat{\alpha}_0^4 \times h^4$ * \alpha視為atteition weight * 假設得到的是0.5, 0.5, 0.0, 0.0,這意味著機器在翻譯的時候只考慮前兩個詞彙 * 得到的$c^0$做為input,得到第一個詞彙machine,同時也輸出另一個key-$z^1$ * $z^1$並不是一個`nn`,而是RNN的一個output,即RNN除了output翻譯的詞彙之外也另外output下一個key ### Machine Translation ![](https://i.imgur.com/eSSFPN2.png) 一樣的,拿$z^1$與$h^1 \sim h^4$計算,得到$\alpha_1^1 \sim \alpha_1^4$,一樣經過sofrmax得到$\hat{\alpha}_1^1 \sim \hat{\alpha}_1^4$之後做weighted sum得到$c^1$,再以$c^1$做為input得到下一個詞彙-learning。 ### Machine Translation ![](https://i.imgur.com/VMcU5ua.png) 相同的計算不斷循環,直到句子產生\<EOS\>或是句點,讓整個翻譯結束。 ### Application ![](https://i.imgur.com/8Ybdu6D.png) [論文連結_Listen, Attend and Spell](https://arxiv.org/abs/1508.01211) 語音辨識是一個很好的應用,input vector sequence,output character sequence,這種情況就可以用Sequence-to-Sequece的方式來硬train一發。 每一段audio在block上只要是黑的,就代表attention的值愈大。雖然這種方法的效能還不能勝過傳統Deep learning,左下方可以看到,CLDNN+HMM的error rate是較低的。 ### Image Caption Generation ![](https://i.imgur.com/diAR3hK.png) ![](https://i.imgur.com/Pvzi4hu.png) Image Caption Generation的input是image,output為句子。雖然稍早提到可以用Vector來表示一張image,但這麼做會損失很多資訊,因此作法上是將image切成小塊,而且小塊都用一個vector來表示,這樣就有比較豐富的資訊。 作法類似: * 初始化$z^0$ * 以match function計算每一個vector * 較高分數的部份代表現在機器要產生詞彙的時候注意的是那個小塊 * 將計算所得做weighted sum得到$z^1$ * 產生第一個詞彙Word 1 * 再以$z^1$利用match function計算得到$z^2$ * 產生第二個詞彙Word 2 * ..... ### Image Caption Generation ![](https://i.imgur.com/ikUwZJU.png) [論文連結_Show, Attend and Tell: Neural Image Caption Generation with Visual Attention](https://arxiv.org/abs/1502.03044) 照片上愈亮的地方就代表機器注意力在上面愈多,照片描述的底線代表機器注意在什麼地方的時候產生這個詞彙。可以發現結果還蠻不錯的。 ### Image Caption Generation ![](https://i.imgur.com/GdejkRr.png) 這邊說明的是機器描述錯誤的範例,不過這些錯看起來又蠻有道理的。把焦距較遠的長頸鹿視為鳥,或者把衣服上的裝飾看成時鐘.. ### Video Caption Generation ![](https://i.imgur.com/6SjVBF1.png) [論文連結_Describing Videos by Exploiting Temporal Structure](https://arxiv.org/abs/1502.08029) 這邊的應用是讓機器看一小段影片,然後描述內容。上面範例參照的是『A man and a woman ride a motorcycle』,但機器產生的是『A man and a woman are talking on the road』。簡報上將四個詞彙不同顏色表示,可以看的出來不同的詞彙在不同的frame上出現的時間點。 下面範例參照的是『A woman is frying food』,機器產生的是『Someone is frying a fish in a pot』,一樣可以看的到機器產生詞彙的時候所在注意力。 ### Attention ![](https://i.imgur.com/wTTDbBR.png) 符號約定: * $\alpha^i_t$:$i$代表注意在那一個component上,$t$代表產生第幾個word的attention weight 這邊說明Generation的小技巧。機器在生成說明的時候是這樣的: * 產生一排的attention weight($\alpha^1_1...\alpha^4_1$),然後產生word1-$w_1$ * 再對每一個frame產生attention weight($\alpha^1_2...\alpha^4_2$),產生word2-$w_2$ * 再對每一個frame產生attention weight($\alpha^1_3...\alpha^4_3$),產生word3-$w_3$ * 再對每一個frame產生attention weight($\alpha^1_4...\alpha^4_4$),產生word4-$w_4$ 但部份情況會發現到,attention weight的分佈是不平均,這會造成生成的語句整個崩潰,就好像範例上$\alpha^2_2, \alpha^2_4$只注意到第二個frame,這會造成生成語句都是『woman』。 因此我們會希望機器它可以將video內的每一個frame都看過,而不是專注意某一個frame上,這可以利用加入Regularization term來達到限制attention weight: * $\sum_i(\tau-\sum_t\alpha^i_t)^2$ * $\sum_i$:對所有的video的frame都做加總 * $\sum_t$:對同一個frame-$i$的所有時步-$t$的值通通加總 * 讓每一個frame的值都接近於$\tau$的附近,讓分佈平均 ### Mismatch between Train and Test ![](https://i.imgur.com/sTzxku9.png) 在生成語句的時候會發現,訓練與測試的過程是不一樣的,上圖為例,訓練過程中,第一個時步我們希望產出A,接著在給定A的情況下產出B,在給定B的情況下再產出B,而每一個時步cross-entropy的合要愈小愈好。 但每一個時步的input都是來自於實際資料,而不是生成的資料,這意昧著即使訓練過程中第一個時步生成的是B,下一個時步的輸入依然會是A。 ### Mismatch between Train and Test ![](https://i.imgur.com/OndBkjX.png) 但測試過程的時候就不一樣了,我們希望生成的是ABB,但第一個時步生成的是B,下一個時步的輸入就是B,並不會像訓練過程一樣依然是A,也就是下一個時步的輸入就是當前時步的輸出。 這造成訓練與測試結果的不一致,這種情況稱為『Exposure Bias』 ### Mismatch between Train and Test ![](https://i.imgur.com/r2N8IjH.png) 以樹的結構來說明,上圖是訓練過程,下圖是測試過程。 訓練過程中,機器知道第一個時步的輸出是A,第二個時步在輸入A之後產出B,第三個時步有輸入A、B之後產出B。但即使機器第一個時步的輸出是B,它還是知道第二、第三個時步的輸出應該都是B,因為它有參考來源。 但測試過程就不是這樣,第一個時步輸出是B的時候,因為機器從來沒有看過第一個時步輸出是B的案例,這會造成機器後面的輸出全部通通是亂選。 ### Modifying Training Process? ![](https://i.imgur.com/kKVqQ47.png) 如果在訓練過程就直接以每一個時步的輸出做為下一個時步的輸入的話,這會造成訓練過程的不穩定。另一個問題是,機器訓練過程中的學習會錯誤,原本第二個時步的輸入是B,當第一個時步的輸出正確之後,第二個時步的輸入變成是A,那之前的訓練都浪費了,每次迭代的輸出入都在改變情況下,學習會非常不穩定。 ### Scheduled Sampling ![](https://i.imgur.com/Z7SoqKe.png) 實作上有一種方式,稱為『Scheduled Sampling』,每次時步的輸入讓機器隨機決定採用參考資料或是自己生成的資料,但這個隨機是一種動態的設置,一開始要比較多使用參考資料,到後續比較多使用模型生成資料,先讓模型魯棒性較佳之後再多使用自己的輸出。 ### Scheduled Sampling ![](https://i.imgur.com/1ZSnFB5.png) 上表是使用Scheduled Sampling的結果。 ### Beam Search ![](https://i.imgur.com/Kc8CrFo.png) 剛才提到,機器在產生sequence的時候,可能是每一個時步都做採樣,或做argmax取機率最大(greedy algorithm 貪婪演算法),舉例來說,這世界只有兩個詞彙,共八種可能變化。每次生成都取機率最大的,那就會產生ABB。 但也有一種可能,當第一個字生成是B,後面也都往機率高的生成的話,產生BBB,它的分數不見得比ABB還要來的低。 ### Beam Search ![](https://i.imgur.com/hdqcLMt.png) Beam Search只在測試的時候需要考慮,訓練的時候是不需要考慮到的,觀念如下: * 定義Beam size * 在所有可能路徑中,保留幾條最好的 * 範例為2,代表保留兩條 * 第一個時步輸出 * 有可能是A、B,剛好兩條,都保留 * 計算AA、AB、BA、BB四條路徑,保留兩條最好的 * 假設是AB、BB * 計算ABA、ABB、BBA、BBB四條路徑,取最好的 這種方式或許可以得到比Greedy Search還要好的結果。 ### Better Idea? ![](https://i.imgur.com/3deH1Oq.png) 這邊說明為什麼實作中要sample,而不直接使用上一個時步的輸出做為輸入。假設訓練資料中有很多『I am..』、『You are..』的句子,當機器要生成第一個詞彙的時候,『I、You』的機率都是相當的,但機器透過sample或argmax來決定使用I或是You,下一個時步在收到You的時候就會知道要接are,而不是am。 但如果是直接將輸出(distribution)做為下一個時步的輸入的話,剛才提過,『I、You』的分數是非常接近的,因此對下一個時步來說,輸入了兩個相當的結果,就對造成可能出現的是錯誤的資料,像是『I are』或『You am』,因為你並沒有給機器一個明確的輸入。 ### Object level v.s. Component level ![](https://i.imgur.com/P2XqAL9.png) ![](https://i.imgur.com/cY28jwf.png) Generator出來的東西是一個序列(sequence),它是由很多component所組成的object。訓練過程中考慮的是component level的loss,但在確認一個句子好壞的時候是看整個句子的語意,而不是單一個詞彙。 因此,錯一個字就是一個loss、兩個字就是兩個loss,舉例來說,有一句『The dog is running fast』,在生成出『The dog is is fat』與『The dog is running fast』這兩句的時候,其實它們兩者之間的loss差異不大,因為我們計算的是component loss。 解決的方法可能要改變loss,調整為object level而不是component loss,比較兩個sequence有多接近,而不是一個詞彙有多接近,但要注意,如果不能微分的話就不能計算,這時候記得,把它當做強化學習的任務硬train一發就可以。 把`nn`視為agent,把object function視為reward、action就是你的詞彙,有十萬個詞彙就代表有十萬個action,這樣就可以了。 可[參考論文_Sequence Level Training with Recurrent Neural Networks](https://arxiv.org/abs/1511.06732)