近日,谷歌官方在 Github開放了一份神經機器翻譯教程,該教程從基本概念實現開始,首先搭建了一個簡單的NMT模型,隨后更進一步引進注意力機制和多層 LSTM加強系統的性能,最后谷歌根據 GNMT提供了更進一步改進的技巧和細節,這些技巧能令該NMT系統達到極其高的精度。機器之心對該教程進行簡要的描述,跟詳細和精確的內容請查看項目原網站。
GitHub 鏈接:https://github.com/tensorflow/nmt
機器翻譯,即跨語言間的自動翻譯,是機器學習社區最活躍的研究領域。在機器翻譯的眾多方法中,序列到序列(sequence-to-sequence,seq2seq)模型[1, 2] 近期獲得巨大成功。由于其可以使用深度神經網絡獲取句義,該模型成為谷歌翻譯等多數商業翻譯系統事實上的標準模型。但是,盡管有關OpenNMT 或 tf-seq2seq 等 seq2seq模型的資料已經非常豐富,但能夠指導人們快速構建高質量翻譯系統的知識和技能仍然略顯不足。
今天,我們很高興能夠發布最新的TensorFlow 神經機器翻譯教程,幫助讀者全面了解 seq2seq模型,并介紹如何從頭開始構建有競爭力的翻譯模型。該教程盡可能簡單地呈現該過程,教程首先介紹神經機器翻譯的背景知識,并講述構建 vanilla系統的具體代碼。然后介紹注意力機制 [3, 4] 這一幫助 NMT 系統處理長句的重要工具。最后,本教程將講解如何復制谷歌NMT(GNMT)系統 [5] 的關鍵特征,以實現在多個 GPU 上訓練模型。
本教程包括具體的基準測試結果,用戶可自行復制。我們的模型提供強大的開源基準,其性能不亞于 GNMT 的結果 [5]。我們在 WMT 2014 的英語轉德語翻譯任務中取得了 BLEU 得分 24.4 的成績。
本教程還包括其他的基準測試結果(英語轉越南語、德語轉英語)。
另外,本教程將開放全動態的 seq2seq API(隨 TensorFlow 1.2 發布),該 API 使 seq2seq 模型的構建過程干凈、簡單、易讀:
使用 tf.contrib.data 中最新輸入的管道對動態調整的輸入序列進行預處理。
使用批量填充和序列長度 bucketing,提高訓練速度和推理速度。
使用通用結構和訓練時間表訓練 seq2seq 模型,包括多種注意力機制和固定抽樣。
使用 in-graph 集束搜索在 seq2seq 模型中進行推理。
優化 seq2seq 模型,以實現在多 GPU 設置中的模型訓練。
下文我們將簡要地介紹該 Github 教程項目。
引言
序列到序列(seq2seq)模型(Sutskeveret al., 2014, Cho et al.,2014)在機器翻譯、語音識別和文本摘要等任務上取得了巨大的成功。本教程致力于幫助讀者全面掌握 seq2seq模型,并且展示了如何從頭開始構建一個強大的 seq2seq 模型。我們該教程會注重神經機器翻(NMT)任務,神經機器翻譯是 seq2seq模型很好的試驗臺,并且已經獲得了廣泛的成功。我們使用的代碼是極其輕量、高質量、可投入生產并且結合了最新研究思路的實現。我們通過以下方式實現這一目標:
使用最新的解碼器/attention wrapper API、TensorFlow 1.2 數據迭代器。
結合了我們在構建循環型和 seq2seq 型模型的專業知識。
提供了可構建最好 NMT 模型的技巧,同時還復制了谷歌的 NMT(GNMT)系統。
我們相信提供所有人都很容易復制的基準是非常重要的。因此,我們基于以下公開的數據集提供了全部的試驗結果和預訓練模型:
小規模數據集:TED 演講的英語-越南語平行語料庫(133K 個句子對),該數據集由 IWSLT Evaluation Campaign 提供。
大規模數據集:德語-英語平行語料庫(4.5M 個句子對),該數據集由 WMT Evaluation Campaign 提供。
我們首先需要了解用于NMT 任務的 seq2seq 模型的基本知識,并需要理解如何構建和訓練一個 vanilla NMT
模型。第二部分將更進一步詳細地解釋如何構建帶注意力機制的強大神經機器翻譯模型。然后我們會討論構建更好神經機器翻譯模型(翻譯速度和質量)可能的技巧,例如TensorFlow 最好的實踐方法(batching, bucketing)、雙向循環神經網絡和集束搜索等。
基礎
關于神經機器翻譯
以詞組為基礎的傳統翻譯系統將源語言句子拆分成多個詞塊,然后進行詞對詞的翻譯。這使得翻譯輸出結果流暢性大打折扣,遠遠不如人類譯文。我們會通讀整個源語言句子、了解句子含義,然后輸出翻譯結果。神經機器翻譯(NMT)竟然可以模仿人類的翻譯過程!
圖 1. 編碼器-解碼器結構——神經機器翻譯的通用方法實例。編碼器將源語言句子轉換成「意義」向量,然后通過解碼器輸出翻譯結果。
具體來說,神經機器翻譯系統首先使用編碼器讀取源語言句子,構建一個「思想」向量,即代表句義的一串數字;然后使用解碼器處理該容器,并輸出翻譯結果,如圖1所示。這就是我們通常所說的編碼器-解碼器結構。神經機器翻譯用這種方法解決以詞組為基礎的傳統翻譯系統遇到的翻譯問題:神經機器翻譯能夠捕捉語言中的長距離依賴結構,如詞性一致、句法結構等,然后輸出流利度更高的翻譯結果,正如谷歌神經機器翻譯系統已經做到的那樣。
NMT模型在具體的結構中會發生變化。對于序列數據而言,最好的選擇是循環神經網絡(RNN),這也被大多數 NMT模型采用。通常情況下,編碼器和解碼器都可使用循環神經網絡。但是,循環神經網絡模型在下列情況下發生變化:(a)方向性(directionality),單向或雙向;(b)深度,單層或多層;(c)類型,通常是vanilla RNN、長短期記憶(Long Short-term Memory,LSTM),或門控循環單元(gated recurrentunit,GRU)。
感興趣的讀者可打開該網址(https://colah.github.io/posts/2015-08-Understanding-LSTMs/),獲取 RNN 和 LSTM 的更多信息。
本教程中,我們將以單向的深度多層RNN(deep multi-layer RNN)為例,它使用 LSTM 作為循環單元。模型實例如圖 2所示。我們在該實例中構建了一個模型,將源語言句子「I am a student」翻譯成目標語言「Je suis étudiant」。該 NMT模型包括兩個循環神經網絡:編碼器 RNN,在不預測的情況下將輸入的源語言單詞進行編碼;解碼器,在預測下一個單詞的條件下處理目標句子。
若想參考更多信息,請查看 Luong(2016)(https://github.com/lmthang/thesis)。
圖 2. 神經機器翻譯——一個深度循環結構實例:將源語言句子「I am a student」翻譯成目標語言句子「Je suis étudiant」。此處,「」代表解碼過程開始,「」代表解碼過程結束。
安裝該教程
為了安裝該教程,我們需要先安裝 TensorFlow。本教程需要最新的 TensorFlow 教程(目前為 1.2.1 版本)。為了安裝 TensorFlow,請按照以下安裝指導:https://www.tensorflow.org/install/。
在安裝 TensorFlow 之后,我們需要運行以下命令安裝本教程的源代碼:
git clone https://github.com/tensorflow/nmt/
訓練-如何構建我們第一個 NMT 系統
我們首先需要了解構建一個 NMT 模型具體代碼的核心,我們會在圖 2 中更詳細地講解。我們后面會介紹數據準備和全部的代碼,這一部分是指 model.py 文件。
在網絡的底層,編碼器和解碼器 RNN 接收到以下輸入:首先是原句子,然后是從編碼到解碼模式的過渡邊界符號「」,最后是目標語句。對于訓練來說,我們將為系統提供以下張量,它們是以時間為主(time-major)的格式,并包括了單詞索引:
encoder_inputs [max_encoder_time, batch_size]:源輸入詞。
decoder_inputs [max_decoder_time, batch_size]:目標輸入詞。
decoder_outputs [max_decoder_time, batch_size]:目標輸出詞,這些是 decoder_inputs 按一個時間步向左移動,并且在右邊有句子結束符。
為了更高的效率,我們一次用多個句子(batch_size)進行訓練。測試略有不同,我們會在后面討論。
1.嵌入
給定單詞的分類屬性,模型首先必須查找詞來源和目標嵌入以檢索相應的詞表征。為了令該嵌入層能夠運行,我們首先需要為每一種語言選定一個詞匯表。通常,選定詞匯表大小V,那么頻率最高的 V個詞將視為唯一的。而所有其他的詞將轉換并打上「unknown」標志,因此所有的詞將有相同的嵌入。我們通常在訓練期間嵌入權重,并且每種語言都有一套。
# Embedding
embedding_encoder = variable_scope.get_variable(
????????????????? "embedding_encoder", [src_vocab_size, embedding_size], ...)# Look up embedding:# ? encoder_inputs: [max_time, batch_size]# ? encoder_emp_inp: [max_time, batch_size, embedding_size]
encoder_emb_inp = embedding_ops.embedding_lookup(
????????????????? embedding_encoder, encoder_inputs)
我們同樣可以構建 embedding_decoder 和 decoder_emb_inp。注意我們可以選擇預訓練的詞表征如 word2vec 或 Glove vectors 初始化嵌入權重。通常給定大量的訓練數據,我們能從頭學習這些嵌入權重。
2.編碼器
一旦可以檢索到,詞嵌入就能作為輸入饋送到主神經網絡中。該網絡有兩個多層循環神經網絡組成,一個是原語言的編碼器,另一個是目標語言的解碼器。這兩個RNN 原則上可以共享相同的權重,然而在實踐中,我們通常使用兩組不同的循環神經網絡參數(這些模型在擬合大型訓練數據集上做得更好)。解碼器RNN 使用零向量作為它的初始狀態,并且可以使用如下代碼構建:
# Build RNN cell
encoder_cell = tf.nn.rnn_cell.BasicLSTMCell(num_units)
# Run Dynamic RNN# ? encoder_outpus: [max_time, batch_size, num_units]# ? encoder_state: [batch_size, num_units]
encoder_outputs, encoder_state = tf.nn.dynamic_rnn(
?????????????????? encoder_cell, encoder_emb_inp,
?????????????????? sequence_length=source_seqence_length, time_major=True)
注意語句有不同的長度以避免浪費計算力,因此我們會通過
source_seqence_length 告訴 dynamic_rnn 精確的句子長度。因為我們的輸入是以時間為主(time
major)的,我們需要設定 time_major=True。現在我們暫時只需要構建單層
LSTM、encoder_cell。我們后面會詳細描述怎樣構建多層 LSTM、添加 dropout 并使用注意力機制。
3.解碼器
decoder 也需要訪問源信息,一種簡單的方式是用編碼器最后的隱藏態 encoder_state 對其進行初始化。在圖 2 中,我們將源詞「student」中的隱藏態傳遞到了解碼器。
# Build RNN cell
decoder_cell = tf.nn.rnn_cell.BasicLSTMCell(num_units)
# Helper
helper = tf.contrib.seq2seq.TrainingHelper(
decoder_emb_inp, decoder_lengths, time_major=True)# Decoder
decoder = tf.contrib.seq2seq.BasicDecoder(
????????????????????? decoder_cell, helper, encoder_state,
???????????????????? output_layer=projection_layer)# Dynamic decoding
outputs, _ = tf.contrib.seq2seq.dynamic_decode(decoder, ...)
logits = outputs.rnn_output
此處代碼的核心是 BasicDecoder、獲取 decoder_cell(類似于 encoder_cell) 的 decoder、helper 以及之前作為輸入的 encoder_state。
通過分離 decoders 和 helpers,我們能重復使用不同的代碼庫,例如 TrainingHelper 可由 GreedyEmbeddingHelper 進行替換,來做貪婪解碼。
最后,我們從未提到過的 projection_layer 是一個密集矩陣,將頂部的隱藏態轉變為維度 V 的邏輯向量。我們在圖 2 的上部展示了此過程。
projection_layer = layers_core.Dense(
??????????????????????? tgt_vocab_size, use_bias=False)
4.損失
給出以上的 logits,可計算訓練損失:
crossent = tf.nn.sparse_softmax_cross_entropy_with_logits( labels=decoder_outputs, logits=logits)
train_loss = (tf.reduce_sum(crossent * target_weights) / batch_size)
以上代碼中,target_weights 是一個與 decoder_outputs 大小一樣的 0-1 矩陣。該矩陣將目標序列長度以外的其他位置填充為標量值 0。
我們需要指出來的是,訓練損失可以由
batch_size 分割,因此我們的超參數 batch_size 是「不變量」。也有些人將訓練損失按照 batch_size *num_time_steps分割,這樣可以減少短句所造成的誤差。更巧妙的,我們的超參數(應用于前面的方法)不能用于后面的方法。例如,如果兩種方法都是用學習率為 1.0的隨機梯度下降,后面的方法將更有效地利用一個較小的學習率,即 1 / num_time_steps。
5.梯度計算和優化
現在是時候定義我們的 NMT 模型的前向傳播了。計算反向傳播只需要寫幾行代碼:
# Calculate and clip gradients
parameters = tf.trainable_variables()
gradients = tf.gradients(train_loss, params)
clipped_gradients, _ = tf.clip_by_global_norm(
? ? ? ? ? ? ? ? ?? gradients, max_gradient_norm)
訓練
RNN 的一個重要步驟是梯度截斷(gradient clipping)。這里,我們使用全局范數進行截斷操作。最大值
max_gradient_norm 通常設置為 5 或 1。最后一步是選擇優化器。Adam優化器是最常見的選擇。我們還要選擇一個學習率,learning_rate 的值通常在 0.0001 和 0.001之間,且可設置為隨著訓練進程逐漸減小。
# Optimization
optimizer = tf.train.AdamOptimizer(learning_rate)
update_step = optimizer.apply_gradients( zip(clipped_gradients, params))
在我們的實驗中,我們使用標準的隨機梯度下降(tf.train.GradientDescentOptimizer),并采用了遞減的學習率方案,因此也就有更好的性能。
開始訓練 NMT 模型
讓我們開始訓練第一個 NMT 模型,將越南語翻譯為英語。代碼的入口是** **nmt.py。
我們將使用小規模的 Ted 演講并行語料庫(133k 的訓練樣本)進行訓練。所有的數據都可從以下鏈接找到:https://nlp.stanford.edu/projects/nmt/。
我們將使用 tst2012 作為開發數據集,tst 2013 作為測試數據集。運行以下命令行下載數據訓練 NMT 模型:
nmt/scripts/download_iwslt15.sh /tmp/nmt_data
運行以下命令行開始訓練:
mkdir /tmp/nmt_model
python -m nmt.nmt \
--src=vi --tgt=en \
--vocab_prefix=/tmp/nmt_data/vocab ?\
--train_prefix=/tmp/nmt_data/train \
--dev_prefix=/tmp/nmt_data/tst2012 ?\
--test_prefix=/tmp/nmt_data/tst2013 \
--out_dir=/tmp/nmt_model \
--num_train_steps=12000 \
--steps_per_stats=100 \
--num_layers=2 \
--num_units=128 \
--dropout=0.2 \
--metrics=bleu
以上命令行訓練一個
2 層的 LSTM seq2seq 模型,帶有 128-dim 的隱藏單元和 12 個 epochs 的嵌入。我們使用 0.2(或然率為
0.8)的 dropout 值。如果沒誤差,在我們訓練中隨著降低混淆度,我們應該能看到類似于以下的 logs。
# First evaluation, global step 0
eval dev: perplexity 17193.66
eval test: perplexity 17193.27
# Start epoch 0, step 0, lr 1, Tue Apr 25 23:17:41 2017
sample train data:
src_reverse: ?i?u ?ó , d? nhiên , là cau chuy?n trích ra t? h?c thuy?t c?a Karl Marx .
ref: That , of course , was the distilled from the theories of Karl Marx .
epoch 0 step 100 lr 1 step-time 0.89s wps 5.78K ppl 1568.62 bleu 0.00
epoch 0 step 200 lr 1 step-time 0.94s wps 5.91K ppl 524.11 bleu 0.00
epoch 0 step 300 lr 1 step-time 0.96s wps 5.80K ppl 340.05 bleu 0.00
epoch 0 step 400 lr 1 step-time 1.02s wps 6.06K ppl 277.61 bleu 0.00
epoch 0 step 500 lr 1 step-time 0.95s wps 5.89K ppl 205.85 bleu 0.00
更多細節,請查看:train.py。
我們可以使用 Tensorboard 在訓練過程中查看模型的總結:
tensorboard --port 22222 --logdir /tmp/nmt_model/
通過以下簡單的變化,就能逆向完成英語到越南語的翻譯。
--src=en --tgt=vi
推理——如何生成翻譯
當你訓練你的NMT模型時(并且一旦你已經訓練了模型),可以在給定之前不可見的源語句的情況下獲得翻譯。這一過程被稱作推理。訓練與推理之間有一個明確的區分(測試):在推理時,我們只訪問源語句,即encoder_inputs。解碼的方式有很多種,包括 greedy 解碼、采樣解碼和束搜索解碼(beam-search)。下面我們討論一下greedy 解碼策略。
其想法簡單,我們將在圖 3 中作說明:
在訓練獲取 encoder_state 的過程中,我們依然以相同方式編碼源語句,并且 encoder_state 用于初始化解碼器。
一旦解碼器接收到開始符 (在我們的代碼中指 tgt_sos_id),就開始解碼處理(翻譯)。
最大的單詞,其 id 與最大的 logit 值相關聯,正如被發出的詞(這是 greedy 行為)。例如在圖 3 中,單詞 moi 在第一個解碼步中具有最高的翻譯概率。接著我們把這一單詞作為輸入饋送至下一個時間步。
這一過程會持續到這句話的終止符「</ s>」,然后輸出(在我們的代碼中是 tgt_eos_id)。
圖 3. Greedy 解碼——一個實例:已訓練的 NMT 模型如何使用 greedy 搜索為源語句 Je suis étudiant 生成翻譯。
推理與訓練的區別在于步驟 3。推理不總是饋送作為輸入的正確目標詞,而是使用被模型預測的單詞。下面是實現 greedy 解碼的代碼。它與訓練解碼器非常相似。
# Helper
helper = tf.contrib.seq2seq.GreedyEmbeddingHelper(
embedding_decoder,
tf.fill([batch_size], tgt_sos_id), tgt_eos_id)
# Decoder
decoder = tf.contrib.seq2seq.BasicDecoder(
??????????????? decoder_cell, helper, encoder_state,
??????????????? output_layer=projection_layer)# Dynamic decoding
outputs, _ = tf.contrib.seq2seq.dynamic_decode(
??????????????? decoder, maximum_iterations=maximum_iterations)
translations = outputs.sample_id
我們在本文中使用了 GreedyEmbeddingHelper 而不是 TrainingHelper。由于無法提前知道目標語句的長度,我們使用 maximum_iterations 限制翻譯的長度。一個啟發是解碼最多兩倍的源語句長度。
maximum_iterations = tf.round(tf.reduce_max(source_sequence_length) * 2)
我們已經訓練了一個模型,現在可以創建一個推理文件并翻譯一些語句:
cat > /tmp/my_infer_file.vi# (copy and paste some sentences from /tmp/nmt_data/tst2013.vi)
python -m nmt.nmt \
--model_dir=/tmp/nmt_model \
--inference_input_file=/tmp/my_infer_file.vi \
--inference_output_file=/tmp/nmt_model/output_infer
cat /tmp/nmt_model/output_infer # To view the inference as output
注意上述指令也可在模型被訓練時運行,只要存在一個訓練檢查點。詳見 inference.py。
中級
在訓練了一些最基本的序列到序列模型之后,我們現在更進一步。為了打造當前最優的神經機器翻譯系統,我們需要更多的秘訣:注意力機制。該機制由
Bahdanau 等人在 2015 年首次提出(https://arxiv.org/abs/1409.0473),稍后 Luong等人和其他人完善了它,其核心思想是當我們翻譯時通過「注意」相關的源內容,建立直接的短連接。注意力機制的一個很好副產品是源語句和目標語句之間的一個易于可視化的對齊矩陣(如圖
4 所示)。
圖 4. 注意力可視化——源語句與目標語句之間對齊的實例。圖片來自 2015 年 Bahdanau 等人的論文。
請記住在vanilla序列到序列模型中,當開始編碼處理時,我們把最后的源狀態從編碼器傳遞到解碼器。這對短、中長度的語句效果很好;對于長句子,單一固定大小的隱狀態成為了信息瓶頸。注意力機制沒有擯棄源RNN中計算的所有隱狀態,而是提出了允許解碼器窺探它們的方法(把它們看作是源信息的動態存儲)。如此,注意力機制提升了長句的翻譯質量。現在,注意力機制實至名歸,已成功應用于其他諸多任務(比如語音識別)。
注意力機制背景
我們現在描述一下注意力機制的實例(Luonget al., 2015),它已經被應用到幾個最新型的系統當中了,包括開源工具,比如OpenNMT(http://opennmt.net/about/)和此教程中的 TF seq2seq API。我們還將會提供注意力機制相關變體的內容。
圖 5. 注意力機制——基于注意力的 NMT 系統(Luong et al., 2015 中有具體描述)。
我們重點詳解注意力計算過程中的第一步。為了更加清晰,我們沒有展示圖(2)中的嵌入層和投影層。
如圖 5 所示,注意力計算發生在解碼步驟中的每一步。它包含下列步驟:
1. 當前目標隱蔽狀態和所有源狀態(source state)進行比較,以導出權重(weight),見圖 4。
2. 基于注意力權重,我們計算了一個背景向量(context vector),作為源狀態的平均權值。
3. 將背景向量與當前目標隱蔽態進行結合以生成最終的注意力向量。
4. 此注意力向量將作為下一時序步驟的輸入。前三個步驟可以由下列公式總結:
這里,函數score 用于將目標隱蔽狀態 ht 和每一個源狀態 hs進行比較,結果會被標準化成生成式注意力權重(一個源位置的分布)。其實有很多種關于評分函數(scoring function)的選擇;比較流行的評分函數包括公式(4)中給出的乘法與加法形式。一旦被計算,注意力向量 at 就會用于推導 softmax logit 和損失。這與 vanilla seq2seq 模型頂層的目標隱蔽態相似。函數 f 也可以利用其它形式。
注意力機制的多種實現方法可由以下鏈接獲得:https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/seq2seq/python/ops/attention_wrapper.py。
注意力機制中有什么相關注意事項呢?
上述公式表明注意力機制有很多種變體。這些變體依賴于評分函數(scoring function)和注意力函數(attention function)的形式,也依賴于前一狀態 ht-1,而不依賴于開始建議的評分函數 ht(Bahdanau et al.,2015)。實際上我們發現的只有一些選擇上的注意事項。一,注意力的基本形式,例如,目標和源之間的直接聯系需要被呈現。二,把注意力向量輸入給下一時間步驟,以把之前的注意力決策告知給網絡(Luong
et al., 2015)。最后,評分函數的選擇經常可以造成不同的性能表現。
Attention Wrapper API
在我們的Attention Wrapper API 的實現中,借鑒了 Weston et al., 2015 在 onmemory network工作中的術語。相比于擁有可讀、可寫的記憶,此教程中的 attention 機制僅是可讀的記憶。特別是對隱藏態(或者隱藏態的變體,例如$$W\overline{h}_s$$ in Luong's scoring style or $$W_2\overline{h}_s$$ )的設定,被認為是「記憶」。在每個時間步下,我們使用現有的目標隱藏態作為「query」決定讀取哪一部分記憶。通常情況下,query需要與單個記憶條相對應的 keys進行對比。在上面對注意機制的演示中,我們偶然使用一套源隱藏態(或者其變體,例如$$W_1h_t$$)作為「key」。你們可以從這種記憶網絡術語中獲取靈感,找到其他形式的attention。
由于 attention wrapper,就不再需要擴展我們帶有 attention 的 vanilla seq2seq 代碼。這部分文件為 attention_model.py。
首先,我們需要定義一種注意機制,例如采用 Luong et al., 2015 的研究。
# attention_states: [batch_size, max_time, num_units]
attention_states = tf.transpose(encoder_outputs, [1, 0, 2])
# Create an attention mechanism
attention_mechanism = tf.contrib.seq2seq.LuongAttention(
num_units, attention_states,
memory_sequence_length=source_sequence_length)
在之前的Encoder 部分,encoder_outputs 是一套頂層的掩藏態源,形式為 [max_time, batch_size,
num_units](因為我們使用 dynamic_rnn with time_major設定)。在注意機制上,我們需要保證通過的「memory」是批次為主的,所以需要調換 attention_states。我們通過source_sequence_length 保證注意機制的權重有適當的規范化(只在 non-padding的位置)。定義完注意機制之后,我們使用 AttentionWrapper 來包裹解碼單元。
decoder_cell = tf.contrib.seq2seq.AttentionWrapper(
?????????????????? decoder_cell, attention_mechanism,
?????????????????? attention_layer_size=num_units)
剩下的代碼基本和編碼器一轉樣 (https://github.com/tensorflow/nmt#decoder)!
上手—打造一個基于注意力的 NMT 模型
為了使注意力發揮作用,我們需要用到luong、scaled_luong、bahdanau 或 normed_bahdanau其中的一個作為訓練期間注意力標志(attentionflag)的值。該標志指定了我們將要使用的注意力機制。除此之外,我們需要為注意力模型創建一個新目錄,因此無需重新使用之前訓練的基本 NMT模型。
運行以下指令開始訓練:
mkdir /tmp/nmt_attention_model
python -m nmt.nmt \
--attention=scaled_luong \
--src=vi --tgt=en \
--vocab_prefix=/tmp/nmt_data/vocab ?\
--train_prefix=/tmp/nmt_data/train \
--dev_prefix=/tmp/nmt_data/tst2012 ?\
--test_prefix=/tmp/nmt_data/tst2013 \
--out_dir=/tmp/nmt_attention_model \
--num_train_steps=12000 \
--steps_per_stats=100 \
--num_layers=2 \
--num_units=128 \
--dropout=0.2 \
--metrics=bleu
訓練之后,我們可以使用帶有新 model_dir 的相同推理指令進行推理:
python -m nmt.nmt \
--model_dir=/tmp/nmt_attention_model \
--inference_input_file=/tmp/my_infer_file.vi \
--inference_output_file=/tmp/nmt_attention_model/output_infer
基準
IWSLT 英語-越南語
訓練:133k 的樣本,dev=tst2012,test=tst2013
訓練速度:在英偉達 K40m 上是 0.37s 的時間步、15.3k 的 wps,在 Titan X 上是 0.17 s 的時間步,32.2k 的 wps。
WMT 德語-英語
訓練:4.5M 的樣本量,dev=newstest2013,test=newtest2015
訓練速度:在英偉達 K40m 上是 2.1s 的時間步,3.4k 的 wps,在英偉達 Titan X 上是 0.7s 的時間步,8.7k 的 wps。
為了查看 GNMT 注意的加速度,我們只在 K40m 上做了基準測試:
WMT 英語-德語 全對比
第二行是我們 GNMT 注意模型:模型 1(4 層),模型 2(8 層)。
其他資源
若想深入了解神經機器翻譯和序列-序列模型,我們非常推薦以下資源:
Neural Machine Translation and Sequence-to-sequence Models: A Tutorial:https://arxiv.org/abs/1703.01619
Neural Machine Translation - Tutorial ACL 2016:https://sites.google.com/site/acl16nmt/
Thang Luong's Thesis on Neural Machine Translation:https://github.com/lmthang/thesis
用于構建 seq2seq 模型的工具很多:
Stanford NMT https://nlp.stanford.edu/projects/nmt/ [Matlab]
tf-seq2seq https://github.com/google/seq2seq [TensorFlow]
Nemantus https://github.com/rsennrich/nematus [Theano]
OpenNMT http://opennmt.net/ [Torch]
參考內容
[1]Sequence to sequence learning with neural networks(https://papers.nips.cc/paper/5346-sequence-to-sequence-learning-with-neural-networks.pdf),Ilya Sutskever, Oriol Vinyals, and Quoc V. Le. NIPS, 2014.
[2]Learning phrase representations using RNN encoder-decoder forstatistical machine translation(http://aclweb.org/anthology/D/D14/D14-1179.pdf), Kyunghyun Cho, Bart Van Merrienboer, Caglar Gulcehre, Dzmitry Bahdanau, Fethi Bougares,Holger Schwenk, and Yoshua Bengio. EMNLP 2014.
[3] Neural machine translation by jointly learning to align and translate(https://arxiv.org/pdf/1409.0473.pdf), Dzmitry Bahdanau, Kyunghyun Cho,and Yoshua Bengio. ICLR, 2015.
[4] Effective approaches to attention-based neural machine translation(https://arxiv.org/pdf/1508.04025.pdf), Minh-Thang Luong, Hieu Pham, and Christopher D Manning. EMNLP, 2015.
[5] Google‘s Neural Machine Translation System: Bridging the Gap between Human and Machine Translation (http://arxiv.org/abs/1609.08144),2016.
機器之心原文參考:https://www.jiqizhixin.com/articles/2017-07-13-6