http://c.biancheng.net/view/1947.html
seq2seq 是一類特殊的 RNN,在機器翻譯、文本自動摘要和語音識別中有着成功的應用。本節中,我們將討論如何實現神經機器翻譯,得到類似於谷歌神經機器翻譯系統得到的結果(https://research.googleblog.com/2016/09/a-neural-network-for-machine.html)。
關鍵是輸入一個完整的文本序列,理解整個語義,然后輸出翻譯結果作為另一個序列。閱讀整個序列的想法與以前的架構截然不同,在該架構中,一組固定詞匯從一種源語言翻譯成目標語言。
本節受到 Minh-Thang Luong 於 2016 年所寫的博士論文“Neural Machine Translation”的啟發。第一個關鍵概念是編碼器–解碼器架構,其中編碼器將源語句轉換為表示語義的向量,然后這個向量通過解碼器產生翻譯結果。
編碼器和解碼器都是 RNN,它們可以捕捉語言中的長距離依賴關系,例如性別一致性和語法結構,而不必事先知道它們,也不需要跨語言進行 1:1 映射。它能夠流利地翻譯並且具有強大的功能。

圖 1 編碼器–解碼器示例
來看一個 RNN 例子:將 She loves cute cats 翻譯成 Elle aime les chats mignons。有兩個 RNN:一個充當編碼器,一個充當解碼器。源語句 She loves cute cats 后面跟着一個分隔符“-”和目標語句 Elle aime les chats mignon。這兩個關聯語句被輸入給編碼器用於訓練,並且解碼器將產生目標語句 Elle aime les chats mignon。當然,需要大量類似例子來獲得良好的訓練。

圖 2 NMT的序列模型示例
NUM序列模型,一個深度循環結構示例,用於將源語句“She loves cute cats” 翻譯成目標語句“Elle aimel les chats mignons”。解碼器側,前面時序中產生的單詞作為輸出下一個單詞的輸入,“_”代表語句的結束。
現在有一些可以使用的 RNN 變體,具體介紹其中的一些:
- RNN 可以是單向的或雙向的,后者將捕捉雙向的長時間依賴關系。
- RNN 可以有多個隱藏層,層數的選擇對於優化來說至關重要...更深的網絡可以學到更多知識,另一方面,訓練需要花費很長時間而且可能會過度擬合。
- RNN 可以有多個隱藏層,層數的選擇對於優化來說至關重要...更深的網絡可以學到更多知識,另一方面,訓練需要花費很長時間而且可能會過度擬合。
- RNN 可以具有嵌入層,其將單詞映射到嵌入空間中,在嵌入空間中相似單詞的映射恰好也非常接近。
- RNN 可以使用簡單的重復性單元、LSTM、窺孔 LSTM 或者 GRU。
仍然考慮博士論文“Neural Machine Translation”,可以使用嵌入層將輸入的句子映射到一個嵌入空間。然后,存在兩個連接在一起的 RNN——源語言的編碼器和目標語言的解碼器。如下圖所示,有多個隱藏層和兩個流動方向:前饋垂直方向連接隱藏層,水平方向是將知識從上一步轉移到下一步的循環部分。

圖 3 神經機器翻譯示例
本節使用 NMT(Neural Machine Translation,神經機器翻譯),這是一個在 TensorFlow 上在線可得的翻譯演示包。
NMT 可通過https://github.com/tensorflow/nmt/ 獲取,具體代碼可通過 GitHub 獲取。
具體實現過程
- 從 GitHub 克隆 NMT:
- 下載一個訓練數據集。在這個例子中,使用訓練集將越南語翻譯成英語,其他數據集可以在https://nlp.stanford.edu/projects/nmt/上獲得,如德語和捷克語:
- 參考https://github.com/tensorflow/nmt/,這里將定義第一個嵌入層,嵌入層將輸入、詞匯量尺寸 V 和期望的輸出尺寸嵌入到空間中。詞匯量尺寸 V 中只有最頻繁的單詞才考慮被嵌入,所有其他單詞則被打上 unknown 標簽。在本例中,輸入是 time-major,這意味着 max time 是第一個輸入參數(https://www.tensorflow.org/api_docs/python/tf/nn/dynamic_rnn):
- 仍然參考 https://github.com/tensorflow/nmt/,這里定義一個簡單的編碼器,它使用 tf.nn.rnn_cell.BasicLSTMCell(num_units) 作為基本的 RNN 單元。雖然很簡單,但要注意給定基本 RNN 單元,我們利用 tf.nn.dynamic_rnn 構建了 RNN 的(見https://www.tensorflow.org/api_docs/python/tf/nn/dynamic_rnn):
- 定義解碼器。首先要有一個基本的 RNN 單元:tf.nn.rnn_cell.BasicLSTMCell,以此來創建一個基本的采樣解碼器 tf.contrib.seq2seq.BasicDecoder,將結果輸入到解碼器 tf.contrib.seq2seq.dynamic_decode 中進行動態解碼。
- 網絡的最后一個階段是 softmax dense 階段,將最高隱藏狀態轉換為 logit 向量:
- 定義在訓練階段使用的交叉熵函數和損失:
- 定義反向傳播所需的步驟,並使用適當的優化器(本例中使用 Adam)。請注意,梯度已被剪裁,Adam 使用預定義的學習率:
- 運行代碼並理解不同的執行步驟。首先,創建訓練圖,然后開始迭代訓練。評價指標是 BLEU(bilingual evaluation understudy),這個指標是評估將一種自然語言機器翻譯為另一種自然語言的文本質量的標准,質量被認為是算法的結果和人工操作結果的一致性。正如你所看到的,指標值隨着時間而增長:
解讀分析
所有上述代碼已經在 https://github.com/tensorflow/nmt/blob/master/nmt/model.py 上給出。關鍵是將兩個 RNN 打包在一起,第一個是嵌入空間的編碼器,將相似的單詞映射得很接近,編碼器理解訓練樣例的語義,並產生一個張量作為輸出。然后通過將編碼器的最后一個隱藏層連接到解碼器的初始層可以簡單地將該張量傳遞給解碼器。
請注意,學習能夠進行是因為損失函數基於交叉熵,且labels=decoder_outputs。
如下圖所示,代碼學習如何翻譯,並通過BLEU指標的迭代跟蹤進度:
下面我們將源語言翻譯成目標語言。這個想法非常簡單:一個源語句作為兩個組合的 RNN(編碼器+解碼器)的輸入。一旦句子結束,解碼器將發出 logit 值,采用貪婪策略輸出與最大值相關的單詞。
例如,單詞 moi 作為來自解碼器的第一個標記被輸出,因為這個單詞具有最大的 logit 值。此后,單詞 suis 輸出,等等:

解碼器的輸出有多種策略:
- 貪婪:輸出對應最大logit值的單詞。
- 采樣:通過對眾多logit值采樣輸出單詞。
- 集束搜索:有多個預測,因此創建一個可能結果的擴展樹。
翻譯實現過程
- 制定解碼器采樣的貪婪策略。這很簡單,因為可以使用 tf.contrib.seq2seq.GreedyEmbeddingHelper 中定義的庫,由於不知道目標句子的准確長度,因此這里使用啟發式方法將其限制為源語句長度的兩倍。
- 現在可以運行網絡,輸入一個從未見過的語句(inference_input_file=/tmp/my_infer_file),並讓網絡轉換結果(inference_output_file=/tmp/nmt_model/output_infer):
兩個 RNN 封裝在一起形成編碼器–解碼器 RNN 網絡。解碼器發出的 logits 被貪婪策略轉換成目標語言的單詞。作為一個例子,下面顯示了一個從越南語到英語的自動翻譯的例子: