這篇文章是im2latex系列的第一篇,希望能夠講清楚基於attention的Sequence-to-Sequence和Beam Search的一些基本概念。從公式圖片直接到latex代碼已經有好些解決方案了,比如這個網站,用戶評價還不錯:
如果你對Seq2Seq很熟悉了,想直接跳到tensorflow代碼部分的話。
直接看代碼吧
在上一篇命名實體識別中,博主介紹了一個相對簡單的任務:如何去預測一個單詞的屬性。如果是翻譯之類的任務就需要設計比較複雜的系統了。我們知道最近神經機器翻譯已經取得了很大的突破---(幾乎)可以達到人類的水平了(比如Google翻譯已經被大量應用在生活中,具體參考文章Googles Multilingual Neural Machine Translation System: Enabling Zero-Shot Translation)。這些新的結構依賴於一種稱為Encoder-Decoder的的結構,可以用於產生特定實體的目標序列。
閱讀這篇文章前,假定你對深度學習的基礎知識(卷積CNN,長短時記憶LSTM…)之類的基本概念已經很熟悉了。如果還對這些CV和NLP方面的知識不是很熟悉,建議可以先看看大名鼎鼎的斯坦福公開課CS231n和CS224n。
先介紹我們使用的模型中最重要的Sequence to Sequence框架。先從翻譯任務中最簡單的版本說起。
舉個??,翻譯」how are you「成中文「幹哈呢」.(法語:comment vas tu)
Seq2Seq模型依賴於encoder-decoder結構。Encoder端對輸入序列編碼,然後Decoder端產生解碼後的目標序列。
上個例子中我們的輸入序列是「how are U」。每個輸入序列的單詞被轉化為了一個向量? (這一過程通過速查表實現)。上個例子中有3個單詞,這樣輸入將會被轉化為? .之後,輸入向量序列進入LSTM,存儲下LSTM輸出的最後一層隱藏層狀態:這就是編碼器的編碼結果 了。寫下這裡的隱藏層狀態: [ ?] (本例中是? )。
現在有了向量 捕獲了輸入序列的信息,下面使用它來一個單詞一個單詞的產生目標單詞序列。隱層狀態 ?和特殊的開始標記 ? 向量作為輸入來送入LSTM單元。LSTM計算了下一個隱藏層狀態 ?。然後應用函數 ? ,這樣? 是辭彙表中同樣大小的向量。
之後,? 經過softmax後進入概率矩陣? 。每個實體的 ? 用來衡量它和單詞表中的單詞的相似度。可以說「幹」(「你幹啥?」中的「幹」)具有最高的概率(也就說? 對應「幹」的索引)。得到對應的向量 ?,然後重複這個過程:繼續將隱藏層狀態? 和 輸入LSTM,LSTM再輸出對應第二個詞的概率矩陣 ?...
當解碼器遇到特殊停止符(往往是"<eos>")的時候,解碼過程就會停止。
直覺上說,隱藏層向量就是還未被解碼的信息。
上述方法旨在下一個單詞在句子開頭已知情況下的條件概率分佈:
簡寫為:
託attention的福,這兩年上面的Seq2Seq模型已經大變樣了。Attention是一種讓模型在解碼時學習集中關注輸入序列特定部分的模型,而不再僅僅依靠解碼中LSTM隱藏層向量的部分。關於attention的詳細介紹請參閱文章Neural Machine Translation by Jointly Learning to Align and Translate。這裡我們稍微修改一下上面的公式,在LSTM的輸入部分加入一個新的向量? :
,
向量 就是注意力(或稱上下文context)向量。每一步解碼的過程計算一個新的注意力向量。首先,使用函數? 計算編碼器每一個隱藏層狀態? 的分數;之後使用softmax規範化 ?,然後與? 加權計算 ?:
for all
在這裡,計算注意力向量? 的函數?那可就多啦,一般來說常用的也就下面幾種:
,
這樣子注意力權重? 很容易解釋了。當產生單詞vas的時候(對應英語are),我們期望? 接近1,而? 和? 接近0。直覺上注意力向量? 大致接近are的隱含層向量,它有助於產生法語單詞vas。
通過將注意力權重送入矩陣(行=輸入序列,列=輸出序列),我們就可以「對齊」輸入的英語單詞和輸出的法語單詞了(參見文章第六頁)。關於Seq2Seq模型還有好多可以探討的地方,比如編碼器可以雙向處理輸入序列,限於篇幅,這裡不再展開了。
如果第一次產生的序列並不太確定是comment還是vas(訓練開始很大程度都可能遇到)的話,會怎麼樣呢?那整個輸出序列就全亂了,模型也很難學到什麼東西了....
如果我們使用訓練中預測的輸出來作為下一步的輸入,錯誤就會一直累積下去,模型很難收斂到正確的輸入分佈上。這會使訓練過程很慢甚至不可訓練。為加快訓練過程,可以將真實的輸出序列 (`<sos>` `comment` `vas` `tu`)送入解碼LSTM來預測每次下一步驟產生的輸出 (comment``vas tu <eos>)。
<sos>` `comment
vas
tu
comment``vas
<eos>
解碼器在每一步輸出概率矩陣? 。對於每一個給定的目標輸出序列 ?,我們在每一步計算每個輸出的概率:
這裡? 代表在第i個解碼步驟中提取第? 個實體的概率? 。這樣,我們可以計算真實的目標序列概率。一個完美的系統可以輸出目標序列接近1的概率,所以我們訓練網路來最大化輸出目標序列的概率,也就是最小化這個目標:
在我們這個例子中,也就等於
這不就是標準的交叉熵嗎!我們實際上在最小化目標分佈和我們模型(概率? )預測的輸出分佈之間的交叉熵!
上述討論中主要的問題是對於同一個模型,我們可以有不同的行為。特別在加速訓練的時候。
那在推理/測試階段呢?是不是有另外的方式來解碼(翻譯)一個句子呢?
的確,在測試階段有兩種方法來解碼(測試就是翻譯一個我們還沒有翻譯過的句子)。一種就是我們文章開始提到的解碼方式:greedy decoding。它包含了上次預測的值最可能進入下次的方式,也是最自然的解碼方式。
但你不覺得這種方式也在累積誤差嗎?
即便訓練好了模型,也可能恰巧模型出了點小毛病(可能首次預測 vas 的概率高於 comment )。那整個的解碼過程就徹底崩了!
comment
有種更好的解碼過程,叫Beam Search:相對於僅預測最高分數的輸出,可以在預測的時候跟蹤最有可能的K個候選(比如K=5,我們就使用5作為Beam Search的大小)。在每一個新的預測(解碼)步驟,對每5個候選我們可以有V個新的輸出。這一下子就有了5V個新的候選。然後再選擇其中5個最靠譜的,這一過程繼續下去.... 上公式,定義? 為第t個步驟的解碼候選集合。
再舉個??,如果k=2,這時 可能就是:
現在通過增加新的字元,我們考慮所有從 產生的可能的候選 ?,:
保持K個最高的得分序列。在我們的例子中:
我們假設最高的兩個是:
每次候選遇到 <eos> 標誌,就返回最高得分的候選。
如果我們使用Beam Search,因為我們保持著最優候選集合,初始步驟的小錯誤會在下一步被修正。
本文介紹了Seq2Seq的幾個概念。可以看到訓練和解碼部分不太一樣。也介紹了兩種解碼方法:greedy和beam search。雖然beam search可以取得較好的結果,但是它依然存在exposure bias。訓練階段,模型不會exposed到錯誤。他也會有Loss-Evaluation mismatch。模型是通過輸出字元級別的交叉熵來優化的,而我們更感興趣的是整個句子結構的重建....
下面,讓我們應用Seq2Seq來將公式圖片自動生成LaTex公式吧! 上代碼!
Seq2Seq經典論文:
試圖解決一些一些侷限性的文章:
全文翻譯自Guillaume Genthial的博客。
推薦閱讀: