TensorFlow : Tutorials : Sequence-to-Sequence モデル (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
更新日時 : 09/15/2017; 10/10/2016, 09/04/2016
作成日時 : 02/24/2016
* 本ページは、TensorFlow 本家サイトの Tutorials – Sequence-to-Sequence Models を翻訳した上で
適宜、補足説明したものです:
* 本ページは、TensorFlow の本家サイトの Tutorials – Sequence-to-Sequence Models を翻訳した上で
適宜、補足説明したものです:
https://www.tensorflow.org/versions/master/tutorials/seq2seq/index.html#sequence-to-sequence-models
* サンプルコードの動作確認はしておりますが、適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
既に RNN チュートリアル で議論したように、リカレント・ニューラルネットワークは言語のモデル化を学習できます。(もしまだ読んでいないのであれば、これを進める前に一読してください。) これは興味深い質問を引き起こします: ある入力に対して生成された単語を条件として、意味のあるレスポンスを生成できるでしょうか? 例えば、ニューラルネットワークを英語からフランス語に翻訳するために訓練することができるでしょうか? 答えは yes であることが判明しています。
このチュートリアルはそのようなシステムをどのように構築して訓練するかを端から端まで貴方に示します。貴方が pip パッケージを通してインストール済みで、tensorflow git レポジトリをクローンし、そして git ツリーのルートにいることを仮定しています。
そして翻訳プログラムを実行することで開始できます:
cd tensorflow/models/rnn/translate python translate.py --data_dir [your_data_directory]
それは WMT’15 Web サイト から 英語フランス語翻訳データをダウンロードして訓練のための準備をして、そして訓練します。それは約 20 GB のディスク容量を取り、そしてダウンロードと準備(詳細は後で)にしばらくの時間がかかりますので、貴方はこのチュートリアルを読みながら始めて実行したままにできます。
このチュートリアルは models/rnn からの次のファイルを参照します。
ファイル | What’s in it? (内容) |
seq2seq.py | sequence-to-sequence モデルを構築するライブラリ。 |
translate/seq2seq_model.py | ニューラル翻訳 sequence-to-sequence モデル。 |
translate/data_utils.py | 翻訳データを準備するためのヘルパー関数。 |
translate/translate.py | 翻訳モデルを訓練して実行するバイナリ。 |
Sequence-to-Sequence 基本
基本的な sequence-to-sequence モデルは、Cho et al., 2014 (pdf)、で紹介されているように、2つのリカレント・ニューラルネットワーク (RNN) から成ります: 入力を扱う エンコーダ と出力を生成する デコーダ です。この基本的なアーキテクチャは下に描かれます。

上の図の各ボックスは RNN のセルを表しています、より一般的には GRU セルまたは LSTM セルです (それらの説明については RNN チュートリアル を参照)。エンコーダとデコーダは重みを共有することができますがあるいは、より一般的に、異なるパラメータセットを使用することもできます。多層セルは sequence-to-sequence モデルでも成功的に使用されています。例えば翻訳のために Sutskever et al., 2014 (pdf)。
上に描かれた基本モデルでは、デコーダに渡される唯一のものとして、全ての入力は固定サイズの状態ベクタにエンコードされなければなりません、デコーダにより直接的な入力へのアクセスを可能にするため、attention メカニズムが Bahdanau et al., 2014 (pdf) で紹介されました。私たちは attention メカニズムの詳細には踏み込みませんが(論文参照)、それはデコーダに全てのデコーディング・ステップで入力をピークすることを許可するとだけ言っておけば十分でしょう。LSTM セルと attention メカニズムによる多層 sequence-to-sequence ネットワークはこのように見えます。

TensorFlow seq2seq ライブラリ
上で見たように、多くの異なる sequence-to-sequence があります。これらのモデルの各々は異なる RNN セルを使用することができますが、これら全てはエンコーダ入力とデコーダ入力を受容します。これは TensorFlow seq2seq ライブラリ (models/rnn/seq2seq.py
) のインターフェイスへの興味を起こさせます。基本的な RNN encoder-decoder sequence-to-sequence モデルは次のように動作します。
outputs, states = basic_rnn_seq2seq(encoder_inputs, decoder_inputs, cell)
上の呼び出しで、encoder_inputs
はエンコーダへの入力を表すテンソルのリストです、すなわち、上の最初の図の文字 A, B, C に相当します。同様に、decoder_inputs
はデコーダへの入力を表すテンソルで、最初の図の GO, W, X, Y, Z に相当します。
クラスのインスタンスで、どのセルがモデル内部で使用されるかを決定します。
GRUCell
または LSTMCell
のような既存のセルを使用できますし、あるいは貴方自身のものを書くこともできます。更に、rnn_cell
は多層セルを構築し、セル入力または出力にドロップアウトを追加し、あるいは他の変換を行なうためのラッパーを提供します。例としては RNN チュートリアル を見てください。
basic_rnn_seq2seq
への呼び出しは2つの引数を返します: outputs
と states
です。両方とも decoder_inputs
と同じ長さのテンソルのリストです。もちろん、outputs
は各時間ステップのデコーダの出力に相当します。上の最初の図ではそれは W, X, Y, Z, EOS でした。返された states
は全ての時間ステップにおけるデコーダの内部状態を表します。
sequence-to-sequence モデルの多くのアプリケーションでは、時刻 t におけるデコーダの出力はフィードバックされ、時刻 t+1 におけるデコーダの入力になります。テスト時、シークエンスをデコードする時、このようにシークエンスが構築されます。訓練時には、他方、デコーダが前にミスをしていた場合でも一般に全ての時間ステップで正しい入力をデコーダに提供します。seq2seq.py
の関数は feed_previous
引数を使って両方のモードをサポートします。例えば、埋め込み RNN モデルの次の用法を解析しましょう。
outputs, states = embedding_rnn_seq2seq( encoder_inputs, decoder_inputs, cell, num_encoder_symbols, num_decoder_symbols, output_projection=None, feed_previous=False)
embedding_rnn_seq2seq では、全ての入力は(both encoder_inputs と decoder_inputs 両方について)離散した値を表す整数テンソルです。これらは密な表現に埋め込まれます(埋め込みについての詳細は ベクタ表現チュートリアル を参照)、しかしこれらの埋め込みを構築するには、次のように現れる離散シンボルの最大値を指定する必要があります: エンコーダ側の num_encoder_symbols とデコーダ側の num_decoder_symbols です。
上記の呼び出しでは、feed_previous を False に設定しました。これはデコーダが decoder_inputs テンソルを提供されたものとして使用することを意味しています。もし feed_previous を True に設定した場合には、デコーダは decoder_inputs の最初の要素だけを使います。このリストからの他の全てのテンソルは無視され、代わりにエンコードの前の出力が使用されます。これは、私たちの翻訳モデルで翻訳をデコードするために使われますが、モデルを自身の誤りに対して堅固にするために、訓練時にもまた使うことができます。Bengio et al., 2015 (pdf) と同様です。
上で使われているもう一つの重要な引数は output_projection です。指定されない場合は、埋め込みモデルの出力は、各々の生成されたシンボルのロジットを表す、バッチ・サイズ x num_decoder_symbols の shape のテンソルになります。大規模出力語彙でモデルを訓練する時、すなわち num_decoder_symbols が大きい時、これらの大規模なテンソルをストアするのは実践的ではありません。代わりに、より小さな出力テンソルを返すのが良いです。これらは output_projection を使って後で大規模出力テンソルに射影されます。これは Jean et. al., 2014 (pdf) に記述されているように、私たちの seq2seq モデルを sampled softmax 損失とともに使うことを可能にします。
basic_rnn_seq2seq と embedding_rnn_seq2seq に加えて、seq2seq.py には2、3の更なる sequence-to-sequence モデルがありますので、見てください。それらは全て同様のインターフェイスを持ちますので、詳細は説明しません。下の翻訳モデルのためには embedding_attention_seq2seq を使います。
ニューラル翻訳モデル
sequence-to-sequence モデルの核が models/rnn/seq2seq.py の関数で構築される一方で、言及するに値する2、3のトリックがまだあります。それは models/rnn/translate/seq2seq_model.py における翻訳モデルで使われます。
Sampled softmax と出力射影 (projection)
上で既に述べたような理由で、大規模出力語彙を処理するために sampled softmax を使いたいです。それからデコードするために、出力射影を追跡する必要があります。sampled softmax 損失と出力射影の両者は seq2seq_model.py の次のコードにより構築されます。
if num_samples > 0 and num_samples < self.target_vocab_size: w = tf.get_variable("proj_w", [size, self.target_vocab_size]) w_t = tf.transpose(w) b = tf.get_variable("proj_b", [self.target_vocab_size]) output_projection = (w, b) def sampled_loss(inputs, labels): labels = tf.reshape(labels, [-1, 1]) return tf.nn.sampled_softmax_loss(w_t, b, inputs, labels, num_samples, self.target_vocab_size)
最初に、サンプルの数 (デフォルト 512) がターゲット語彙サイズよりも小さい場合にのみ sampled softmax を構築することに注意してください。512 より小さい語彙のためには標準 softmax 損失だけを使うのはより良い考えでしょう。
そして、見て取れるように、出力射影を構築します。それはペアで、重み行列とバイアスベクタから成ります。使用された場合、rnn セルは shape batch-size x size の shape のベクタを返します、batch-size x target_vocab_size ではありません。ロジットを取り出すためには、seq2seq_model.py の 124-126 行目で行なわれているように、重み行列で乗算してバイアスを加算する必要があります。
if output_projection is not None: self.outputs[b] = [tf.matmul(output, output_projection[0]) + output_projection[1] for ...]
Bucketing とパディング
sampled softmax に加えて、私たちの翻訳モデルはまた bucketing を使います、これは異なる長さの文を効率的に処理するための方法です。まず問題をクリアにしましょう。英語からフランス語に翻訳する時、入力として異なる長さ L1 の英語の文があるでしょう、そして出力として異なる長さの L2 のフランス語の文があります。英語の文は encoder_inputs として渡され、フランス語の文は(GO シンボルで prefix される)decoder_inputs として現れますから、原理上は英語とフランス語の文の長さの全てのペア (L1, L2+1) のために seq2seq モデルを作成すべきです。これは多くの非常に類似したサブグラフから成る巨大なグラフという結果になるでしょう。一方で、特殊な PAD シンボルで全ての文をパディングすることもできます。そうすればパディングされた長さのため、一つの seq2seq モデルだけが必要になります。しかしより短い文では私たちのモデルは非効率的となり、必要のない、多くの PAD シンボルをエンコードしてデコードすることになります。長さの全てのペアのためのグラフを構築することと単一の長さにパディングすることの間の妥協として、幾つかのバケツを使いそして個々の文をその長さを超えてバケツの長さにパディングします。
buckets = [(5, 10), (10, 15), (20, 25), (40, 50)]
このことは、もし入力が 3 トークンの英語文であり、そして対応する出力が 6 トークンのフランス語文である場合、これらは最初のバケツに入れられエンコーダ入力のために長さ 5 に、そしてデコーダ入力のためには長さ 10 にパディングされることを意味しています。もし8 トークンの英語文があり対応するフランス語文が 18 トークンである場合には、(10, 15) バケツにはフィットしませんので、(20, 25) バケツが使用されます。すなわち、英語文は 20 にパディングされ、フランス語文は 25 にパディングされます。
デコーダ入力を構築する時、特殊な GO シンボルを入力データに追加することを思い出してください。
これは seq2seq_model.py の get_batch() 関数で行なわれます、これはまた入力英文の反転もします。入力の反転はニューラル翻訳モデルに対して結果を改善することが Sutskever et al., 2014 (pdf) で示されています。すべてを理解するために、"I go." という文があり、入力として ["I", "go", "."] としてトークン分割されそして出力は "Je vais." という文で、["Je", "vais", "."] とトークン分割されていることを想像してください。それは (5, 10) バケツに入れられます、[PAD PAD "." "go" "I"] を表すエンコーダ入力と デコーダ入力 [GO "Je" "vais" "." EOS PAD PAD PAD PAD PAD] と共に。
Let's Run It
上述のモデルを訓練するためには、大規模な英仏コーパスが必要です。私たちは訓練のために WMT'15 Website からの10^9-英仏 コーパスを使用して、開発セットとして同じサイトから 2013 ニューステストを使用します。次のコマンドが実行された時、両方のデータセットが data_dir にダウンロードされて訓練が始まり、train_dir にチェックポイントが保存されます。
python translate.py --data_dir [your_data_directory] --train_dir [checkpoints_directory] --en_vocab_size=40000 --fr_vocab_size=40000
訓練コーパスを準備するためには約 18 GB のディスク空間を必要とし数時間かかります。それはアンパックされ、語彙ファイルは data_dir で作成され、そしてコーパスはトークン分割されて整数 id に変換されます。語彙サイズを決定するパラメータに注意してください。上の例では、40 K のもっとも一般的なもの以外の全ての単語は未知の単語を表す UNK トークンに変換されます。もし語彙サイズを変更するのであれば、バイナリはコーパスをトークン-id に再度マップし直します。
データが準備された後、訓練が始まります。translate(.py) のデフォルト・パラメータは非常に大きな値に設定されています。長時間かけて訓練されたラージ・モデルは良い結果を与えますが、時間がかかり過ぎたり貴方の GPU のメモリを消費し過ぎたりします。次の例のようにより小さなモデルを訓練するように要求することもできます。
python translate.py --data_dir [your_data_directory] --train_dir [checkpoints_directory] --size=256 --num_layers=2 --steps_per_checkpoint=50
上のコマンドは 2 層(デフォルトは 3 層)、各層は 256 ユニット(デフォルトは 1024)のモデルを訓練して、50 ステップ毎にチェックポイントを保存します(デフォルトは 200)。貴方はこれらのパラメータと遊ぶことでどの程度の大きさのモデルが貴方の GPU メモリに適合することができるか見つけられます。訓練の間、steps_per_checkpoint ステップ毎にバイナリは新しいステップから統計情報をプリントアウトします。デフォルト・パラメータ(サイズ 1024 の 3 層)では、最初のメッセージはこのようなものです。
global step 200 learning rate 0.5000 step-time 1.39 perplexity 1720.62 eval: bucket 0 perplexity 184.97 eval: bucket 1 perplexity 248.81 eval: bucket 2 perplexity 341.64 eval: bucket 3 perplexity 469.04 global step 400 learning rate 0.5000 step-time 1.38 perplexity 379.89 eval: bucket 0 perplexity 151.32 eval: bucket 1 perplexity 190.36 eval: bucket 2 perplexity 227.46 eval: bucket 3 perplexity 238.66
各ステップがちょうど 1.4 秒以下かかること、各バケツのための訓練セット上の perplexity と開発セット上の perplexity が見て取れます。約 30K ステップ後、短い文(バケツ 0 と 1)の perplexity が一桁に入ることを見ます。訓練コーパスは ~ 22M 文を含みますので、一つの epoch(訓練データを一巡すること) はバッチサイズ 64 で約 340K ステップかかります。この時点でモデルは --decode オプションで英語文をフランス語に翻訳するために使用することができます。
python translate.py --decode --data_dir [your_data_directory] --train_dir [checkpoints_directory] Reading model parameters from /tmp/translate.ckpt-340000 > Who is the president of the United States? Qui est le président des États-Unis ?
What Next?
上の例は貴方自身の英仏翻訳機をどのようにビルドするかを示しています、端から端まで。それを実行してモデルがどのように遂行するかを貴方自身で見てください。それは reasonable な品質を持つ一方で、デフォルト・パラメータはベストな翻訳モデルを提示してはいません。貴方が改良できる2、3のことがここにあります。
まず最初に、私たちは非常にプリミティブな tokenizer (トークン解析器) - data_utils の basic_tokenizer 関数を使用しています。より良い tokenizer は WMT'15 Web サイト で見つかります。その tokenizer、そしてより大規模な語彙を使用することは、翻訳を改良します。
また、翻訳モデルのデフォルト・パラメータはチューニングされていません。学習率、減衰を変更してみる、あるいはモデルの重みを違う方法で初期化してみることができます。また seq2seq_model.py のデフォルト GradientDescentOptimizer をより進んだもの、AdagradOptimizer のようなものに変更することもできます。これらのことにトライしてそれらがどのように結果を改善するか見てみましょう。
最後に、上で提示されたモデルは翻訳だけではなく、任意の sequence-to-sequence タスクに使用できます。例えば解析木を生成するためにシークエンスをツリーに変換することを望む場合、Vinyals & Kaiser et al., 2014 (pdf) で示されてるように、上と同じモデルが最高水準の結果を与えることができます。従って貴方自身の翻訳器をビルドできるだけでなく、解析器、チャットボット、あるいは思いつく任意のプログラムをビルドすることもできます。 Experiment!
以上