Tensorflow 官方 Sequence-to -Sequence Models 學習
序
Tensoflow 這份教學是利用 seq2seq model 並加上 attention 機制[1-4],來實做英文對法文的機器翻譯。
操作方式很簡單,將官方提供的程式碼整份 clone 下來,cd 到 models/tutorials/rnn/translate
目錄下
|
機器就會自動開始下載資料集、前處理、並且開始訓練了。重複執行也沒有關係,程式會先檢查是否有已下載的資料集,已前處理完成的資料,甚至是已訓練好的模型,並且自動從當下的狀態開始繼續訓練。如果不帶參數,單純執行 python translate.py
,那麼會預設使用 /tmp/
作為存放的目錄。
translate.py
顯然,translate.py
是這裡面的主程式,接下來來看看這隻程式做了什麼事情。
|
這段程式碼顯示,如果不下其他指令,那麼會自動執行訓練,否則是執行測試,或是 decode,也就是預測法文的模式。
我們先將 train()
的主要功能拆成以下幾個區塊,並分開來討論。
|
1.模型初始化
train()
首先會透過 create_model()
函數來建立 seq2seq model。該函數會呼叫seq2seq_model.py
,並初始化一個 seq2seqModel class。
|
可以看到,這個 class 的輸入都是一些常見訓練 RNN 的參數。
bucket 預設是 [(5, 10), (10, 15), (20, 25), (40, 50)]
,舉例來說,對 (5,10) 這個 bucket 而言:
- 英文輸入:
I go .
- 分詞:
["I", "go", "."]
- 編碼:
[PAD PAD "." "go" "I"]
那麼我們會把輸入編碼到長度5,
- 法文輸入:
Je vais .
- 分詞:
["Je", "vais", "."]
- 編碼:
[GO "Je" "vais" "." EOS PAD PAD PAD PAD PAD]
並且把輸出編碼到長度 10。
Bucket 是工程上使用的一種方式。理論上 RNN 可以輸出任意長度的句子,但這樣勢必會因為每句話的長度不同,而產生許多無用的 graph。使用 Bucket 可以減少產生大量,並可能會有不少重複的 graph。若有一長度為 ( 6, 16 ) 的 (英文, 法文) 句子,那麼則會被分配到 (20, 25) 這個 bucket。並且英文會被 padding 至長度 20,法文會被 padding 至長度 25。
2.讀入資料
在 create_model()
之後,接下來是 read_data()
函數。
|
這個函數預設會讀入英/法文已被編碼為數字的檔案giga-fren.release2.fixed.en.ids40000
、giga-fren.release2.fixed.fr.ids40000
長相如下:
|
max_size=30
會忽略長度超過30的句子,若設0或是None的話,會全部讀進來。逐行處理之後,回傳一個長度為 4 的 data_set。
為什麼長度為 4 呢?因為我們的 bucket 預設是長度 4 的 list:[(5, 10), (10, 15), (20, 25), (40, 50)]
。因此 data_set 的長度也為 4。
其中 data_set[0]
,由於 bucket_size 是 (5,10) ,因此存放著例如 [[12106], [4951, 2]]
這樣長度為 (1,2) 的 tokenized 編碼結果。
3.取得 batch
|
get_batch()
是定義在 seq2seq_model.py
之下的一個方法,需要的參數除了方法本身的
data
:即read_data()
回傳的 data_set。bucket_id
:因為對於不同的 bucket 有不同的 graph,假設我們有一個 minibatch ,若是跟這一批資料最接近的 bucket id 是 2,那我們在訓練的時候只要 minimizeloss[2]
即可。
之外,還需要一個 class 本身的 property: batch_size
。
|
以上面這個例子來說,假設我們把 batch_size
設為 8,那麼回傳的結果中每個 array 的長度皆為 8,並且因為 bucket 是 (5, 10),所以 encoder_inputs
長度為 5,decoder_inputs
長度為 10。
下面的回傳結果可以理解成 batch_size=8
,英文輸入序列:[ 0, 0, 0, 0, 12106]
,法文輸入序列:[1, 4951, 2, 0, 0, 0, 0, 0, 0, 0]
。其中 0 代表 PAD_ID,1 代表 GO_ID,2 代表 EOS_ID。並且 weight:[1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
,代表 1, 4951 是有對應到輸出的。
|
4.訓練
上面得到了 get_batch()
回傳的三個值,之後就是進入訓練的本身了
|
step()
同樣是定義在 seq2seq_model.py
之下的一個方法,主要的功能是把輸入與輸出等建立 placeholder 與轉為 feed_dict,以進入訓練的主程序。
以下這段程式會建立一個 bucket 版本的 seq2seq 模型,需要輸入先前提及的 encoder_inputs、decoder_inputs、target_weights。另外的 targets 表示的是訓練目標,其實就是 decoder_input 的現在位置+1。具體的參數說明可以參考官方文件。
|
上面這段程式碼中的 seq2seq 參數 seq2seq_f(x, y, False)
也是定義在 seq2seq_model.py
裡面的。指的是將 x: encoder_input 與 y: decoder_input 輸入,回傳的就是這個 seq2seq model 的 output 與 state。False 這個參數則是 seq2seq_f()
裡面自行定義作為 do_decode or not 的 Boolean。我們把相關的程式碼列出來如下,可以看到多為 tensorflow 之中對於 RNN 的設定。其中比較特別的是 sampled_softmax_loss
以及 seq2seq_f
。
|
sampled_softmax_loss
是用在當有大量的輸出類別必須被 predict 的時候,舉例來說,像是英翻法這樣的翻譯工作,法文的詞典(target_vocab_size) size 有 40000 之多,這時候我們採用 sampled_softmax_loss 可以快速有效地建立一個 softmax classifier。其中的參數num_sampled
指的是 sampling 的數目,在這邊是512。num_classes
指的就是實際的 class 數目,在這邊就是以法文詞典的數目來代表。要注意的是 num_sampled
不可以大於 num_classes
就是了。細節可參考官方文件說明。
seq2seq_f()
直接呼叫了 tf.contrib.legacy_seq2seq.embedding_attention_seq2seq()
。這個 embedding_attention_seq2seq 是一個帶有 embedding + sequence to sequence 並帶有 attention 機制的模型。encoder_input 首先進入一個 embedding layer,轉為 word vector,之後進入一個 encoder RNN。這個 encoder RNN 的每一個 time step 會被記錄下來,作為 attention 機制的參考。接下來,decoder_input 會進入另一個新建立的 embedding layer,在同樣轉為 word vector 之後,進入一個 attention deocder RNN。這個 deocder 是由 encoder 的最後一個 time step 的 state 進行初始化,其後每一個輸入就是 decoder_input 經過 embedding 之後的 word vector,並且具有對 encoder output 專注的 attention 機制。
在 tf.contrib.legacy_seq2seq.embedding_attention_seq2seq()
之中的參數 feed_previous
,當他為 False 的時候 decoder 會使用前面給的 decoer_input 作為輸入,也就是一般在訓練階段的作法。當值為 True 的時候,前面給的 decoder_input 只有第一個值(通常是 GO symbol,代表一個句子的開始) 會作為 decoder 的輸入,而 decoder 的下一個 input,則是 decoder 的前一個 output,也就是只給 deocder 第一個 input,後面讓他自由發揮的意思。這也是一般在 decode/predict 時候的作法。官方文件。
以上是這些程式碼比較核心的部分,後面大概就是利用tf.train.GradientDescentOptimizer
進行訓練,clip gradient 並 print 一些 epoch、loss、perplexity 等資訊。
perplexity
最後面簡單介紹一下 perplexity,這個指標就如同 precision recall 等指標一樣,用來評估一個模型的好壞,只不過在語言模型裡面,我們使用 perplexity 這個指標。perplexity 越小,代表模型的校能越好。translate.py
之中對於 perplexity 的定義是:
|
而數學定義:
$$perplexity=e^{-l}, l=\frac{1}{M}\sum_{i=1}^{m}\log\left(p(s_i) \right )$$
正是math.exp(float(loss))
。
[1] Learning Phrase Representations using RNN Encoder-Decoder for Statistical Machine Translation
[2] Sequence to Sequence Learning with Neural Networks
[3] Neural Machine Translation by Jointly Learning to Align and Translate
[4] On Using Very Large Target Vocabulary for Neural Machine Translation