Translate

2016年9月27日火曜日

TensorFlowサイトのチュートリアル「Sequence-to-Sequence Models」を翻訳してみた

TensorFlowもいれたので、そろそろ応用してみたいなあ..
とおもい
TensorFlowサイトのチュートリアルにある

Sequence-to-Sequence Models
https://www.tensorflow.org/versions/r0.10/tutorials/seq2seq/index.html

を翻訳してみた。

拙い英語力なので参照される場合は、At your own risk でお願いします。

-----

Sequence-to-Sequence モデル


既に RNNチュートリアル (もし迄読んでいない場合は、このチュートリアルを始める前にこちらへ進んでください)で論じられているように、言語のモデル化を学習することができます。ここには興味深い問題があがっています:いくらかのインプットを条件にして、意味のあるレスポンスを生成することができますか?たとえば、ニューラルネットワークを英語からフランス語への翻訳を訓練することができますか?答えはyesであることがわかります。


このチュートリアルでは、いかにしてそのようなシステムをエンドtoエンドでビルドおよび訓練するかについて紹介します。TensorFlow git リポジトリにクローンのルートにあるpipパッケージを通じて既にインストールされている前提とします。

翻訳プログラムは次のコマンドで開始できます:

cd tensorflow/models/rnn/translate
python translate.py --data_dir [データディレクトリパス]
訓練の準備および訓練のために WMT'15ウェブサイト から英語-フランス語翻訳データをダウンロードします。ディスク容量約20GBが必要となりますので、ダウンロードして準備します(詳細は 後述 )。このチュートリアルを読んでいる間、開始することができます。

このチュートリアルのリファレンスはmodels/rnnにあります。

ファイル内容
seq2seq.pysequence-to-sequence モデルを構築するためのライブラリ
translate/seq2seq_model.pyニューラル翻訳 sequence-to-sequence モデル
translate/data_utils.py翻訳データ準備のためのヘルパー関数
translate/translate.py翻訳モデルの訓練および実行用バイナリ


訳者注:上記ファイルは、tensorflowをインストールした環境には入っています。Dockerコンテナを使っている場合は/usr/local/lib/python2.7/dist-packages/tensorflow/models/rnn/にあります。

Sequence-to-Sequence の基礎

Cho氏が2014年に寄稿した論文PDF )に記載されている、基本的なsequence-to-sequenceモデルは、インプットデータを処理するエンコーダとアウトプットを生成するデコーダの2つのRNNで構成されています。基礎的なアーキテクチャは次の図の通りです。



上図の四角はRNNセルを表しています。またこれらの四角は、最も一般的にはGPUセルもしくはLSTMセル(説明は RNNチュートリアル を参照のこと)を表しています。エンコーダおよびデコーダは重みを共有するかもしくは、より一般的には、異なるパラメータセットを使用します。多層セルが sequence-to-sequence モデルにおいてもうまく使われています。例: 翻訳向け Sutskeverほか 2014年 ( PDF )

ここで表されている基本的なモデルにおいて、あらゆるインプットは、デコーダに引き継がれる唯一のものである、固定サイズのベクトルにエンコード されなければなりません。インプットへの直接アクセスをデコーダに与えるために、注意メカニズム(attention mechanism)は、 Bahdanauほか、2014 ( PDF )にて導入されました。注意メカニズム(attention mechanism)の詳細については記述しませんが(論文を参照のこと)、デコーダに対しあらゆるデコーデング手順におけるインプットを覗くことを許す ために、詳細について十分理解しておいてください。LTSMセルや注意メカニズム(attention mechanism)による多層 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_input は、エンコーダへのインプット(最初に登場した絵の ABC )をあらわすtensorflowのリストです。同様に、 decoder_input は、エンコーダへのインプット(最初に登場した絵の WXYZ )をあらわすテンソルです。
セル引数は、どのセルがモデル内部で使用されるかを示す models.rnn.rnn_cell.RNNCell クラスのインスタンスです。 GRUCellLSTMCell のような既存のセルもしくは独自に記述したものを使用することができます。さらに、 rnn_cell は多層セルの構築や、セルインプットもしくはアウトプットへのドロップアウト、もしくはその他の翻訳実行のためのラッパーを提供します。例は RNN チュートリアル を参照してください。

basic_rnn_seq2seq へのセルは二つの変数( outputsstates )を返します。両方の変数は decode_inputs と同じ長さのテンソルのリストです。当然のことながら、 outputs は、タイムステップごとのデコーダのアウトプットと一致します。そして、 outputs は、最初の絵の WXYZEOS と一致します。返却値 states は、それぞれのタイムステップにおけるデコーダの内部の状態をあらわします。
たくさんのsequence-to-sequence モデルのアプリケーションでは、時刻 tのデコーダのアウトプットは、フィードバックされ時刻 t+1 のデコーダのインプットとなります。テストの際にsequenceをデコーディングする場合、これはシーケンス構築方法となります。訓練中、他方では、た とえデコーダが前に間違えていたとしても、タイムステップごとに正しいインプットをデコーダへ提供することは一般的です。 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 モデルにおいて、全てのインプット( encoder_inputs と 
decoder_inputs の両方 )は離散値をあらわす整数テンソルです。それらには密度表現が
埋め込まれています(埋め込みについての詳細は ベクトル表現チュートリアル を参照
のこと)が、これらの埋め込みを構築するために別々のシンボルの最大値、エンコーダ
側の num_encoder_symbols とデコーダ側の num_decoder_symbols を指定する必要があり
ます。

 
上記の起動により、 feed_previousFalse がセットされます。これは、デコーダが decoder_inputs テンソルが提供されてるものとして使用することを意味しています。もし feed_previousTrue がセットされているのであれば、デコーダは decoder_inputs の最初の要素のみ使用します。このリストのその他のテンソルはすべて無視され、前のエンコーダのアウトプットをかわりに使用します。これは翻訳モデルの翻訳デコーディングのために使用されますが、訓練中にも使用され、自身の間違いにより強力な Bengioほか,2015年 ( PDF )に類似したモデルを構築します。

上のリストで使用されているもう一つの重要な変数は、 output_projection です。もしこれが指定されていない場合は、埋め込みモデルのアウトプットは、生成されたシンボルごとの ロジット を表すので、 num_decoder_symbols によるバッチサイズ型をしたテンソルとなります。長いアウトプット語彙を使ったモデルの訓練時(たとえば num_decoder_symbols が長い場合)、これらの長いテンソルとしては実用的ではありません。その代わりよいこととして、より小さなアウトプットテンソルを返します。そして後で output_projection を使った長いアウトプットテンソルへ反映されます。これは、 Jeanほか 2014年 ( PDF )にて説明されている、sampled softmax 損失を seq2seq モデルにて使用することを許可します。

basic_rnn_seq2seq embedding_rnn_seq2seqに加えて、 seq2seq.py 内にはいくつかの sequence-to-sequence モデルが存在しますので、確認してみてください。それらは類似したインタフェースを持っているので、ここでは詳細を説明しません。ここでは翻訳モデルとして、 embedding_attention_seq2seq を、以下のように使います。


ニューラル翻訳モデル

sequence-to-sequence モデルのコアは models/rnn/seq2seq.py 内の関数によって構築され
ており、そこには models/rnn/translate/seq2seq_model.py 内の翻訳モデルが使用される
ことに言及する価値があるいくつかのトリックがまだ存在します。
 

sampled softmax と output projection


ひとつめ(samples softmax)は既に説明していますが、ここではsampled 
softmaxを長いアウトプット語彙を操作するために使用したいと考えています。
デコードするためには、output projection の結果を追う必要があります。
sampled softmax 損失 と output projection 両方とも、 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)がターゲット語彙サイズより小さいのであれば、sample softmax のみ構築します。語彙が512より小さい場合、標準softmax損失を使うことは良いアイディアであるかもしれません。
次に、output projection を構築します。それは重み行列とバイアスベクトルから構成されるペアです。使用する場合、rnn セルは target_vocab_size によるバッチサイズよりももしろ、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 ...]
 

バケッティングとパディング(埋め込み)

 
sampled softmax に加えて、翻訳モデルもまたバケッティングを利用しています。バケッティングは、異なる長さの文を効果的に操作するための手法のひとつです。まず最初に問 題をはっきりさせましょう。英語からフランス語へ翻訳する場合、インプット上の異なる長さ L1 の英文と、アウトプット上の異なる長さ L2 のフランス語文を持っています。英文は encoder_inputs に渡されると、フランス語文が decoder_inputs (GOシンボルが先頭文字につく)としてかえってきます。

こ のため原則英文とフランス語文の長さの各ペア( L1, L2 + 1)のための seq2seq モデルを作成しなくてはなりません。結果としてこれは類似する大量のサブグラフを含む巨大なグラフとなります。他方では、ちょうどそれぞれの文に特別な PADシンボルをつめることができます。そうすれば我々は、詰められた長さの、seq2seq モデルを1つだけ必要するだけです。ただし短文では、役に立たないたくさんのPADシンボルをデコードするため、能率が悪くなります。

それぞれの長さのペアのためのグラフ構築と単一の長さに詰めることの妥協策として、大量のバケットを使って各文をバケットの長さに詰めます。 translate.py 内の以下の箇所でデフォルトのバケットを使っています。

buckets = [(5, 10), (10, 15), (20, 25), (40, 50)]
これは、3トークンの英文でこのアウトプットが6トークンのフランス語文であったとすると、最初のバケットに格納して、エンコーダインプットのため に長さ5になるまで、デコーダインプットのために長さ10になるまで詰めることを意味しています。もし英文が8トークンでフランス語文が18トークンとな る場合、例えば英文を20まで、フランス文を25までつめるとするなら、(10, 15) のバケットには適合せず、(20, 25)のバケットが使用されます。

デコーダインプットを作成する際にインプットデータにGOシンボルを前につめることを思い出してください。これは seq2seq_model.py内の get_batch() 関数で処理されます、そしてこの関数でインプット英文もまた反転します。インプットの反転は、 Sutskeverほか、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 ]となります。
 
 

動かしてみよう


これまで説明していたモデルを訓練するために、長い英語-フランス語コーパスが必要に
なります。 WMT15 Website から 10^9-Fench-English コーパスを訓練のために使い、
同じサイトから 2013 new test を開発セットとして使います。以下のコマンドを実行する
と、両方のデータセットは data_dirにダウンロードされ、訓練が始まり、チェックポイント
train_dir に保存されます。

python translate.py
  --data_dir [your_data_directory] --train_dir [checkpoints_directory]
  --en_vocab_size=40000 --fr_vocab_size=40000
約18GBの空き容量を消費し、訓練コーパス準備に数時間かかります。解凍され、語彙ファイルが data_dir 内に作成され、そしてコーパスがトークン化され整数idに変換されます。語彙サイズを定義するパラメータに注意してください。上の実行例では、40Kの最も一般的な用語を除く単語はすべて未知の単語を意味する UNK トークンに変換されます。そして、もし語彙サイズを変更したいのであれば、バイナリは再度コーパスをid型にリマップします。

データが準備できたら、訓練が始まります。翻訳のデフォルトパラメータは極めて長い値がセットされています。長いモデルが良い結果を与えるまで長 い時間かかります。長くかかりすぎるか、もしくはGPUを使ってとても多くのメモリを消費するかになります。以下の例では、小さなモデルを訓練することを 指示できます。

python translate.py
  --data_dir [your_data_directory] --train_dir [checkpoints_directory]
  --size=256 --num_layers=2 --steps_per_checkpoint=50
 
上記コマンドは、各レイヤが256個のユニット(デフォルトは1024個)の2層のモデル(デフォルトは3層)を訓練し、そして50ステップ(デ フォルトは200ステップ)ごとにチェックポイントとしてセーブします。あなたのGPUのメモリがどのくらいの大きさのモデルが適合することができるかを 発見するためにこれらのパラメータを操作することができます。

訓練中、それぞれの steps_per_checkpoints ステップでこれまでのステップの統計結果を表示します。デフォルトパラメータ(サイズが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秒未満で、それぞれのバケットに訓練セットの予測性能と開発セットの
予測性能を確認することができます。30Kステップ後、短文(バケット0と1)の予測性能が
一桁になっていることが確認できます。訓練コーパスが~22Mの文を含んでおり、
1 epoch(訓練データを1回通す)は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 ?
 
 

次は何?

 
ここまでで、どうやって独自のEnglish-to-French翻訳機を隅から隅まで構築するかを紹介しました。実行してどのようにモデルが振る 舞うのかを自分自身で確認しました。合理的な品質をもつほど、デフォルトパラメータではベストな翻訳モデルを提供しませんでした。ここにあたなが改善する ことがのこっています。

まず第一に、とても原始的なトークナイザである data_utils 内の basic_tokenizer を使いました。より良いトークナイザは、 WMT'15 ウェブサイト で見つけることができました。そのトークナイザと多くの語彙を使って、翻訳を発展させるべきです。

また、翻訳モデルのデフォルトパラメータは未調整です。モデルの学習率、劣化、もしくは重みの初期値うぃ異なった方法で変更してみることが可能です。また seq2seq_model.py内のデフォルト GradientDescentOptimizer を、 AdagradOptimizer のようなより先進的なものに変更することも可能です。これらを試して、結果の向上方法を確認してみましよう!

最後に、ここで紹介されたモデルを、翻訳のためでなくほかの sequnece-to-sequence 作業に使うことが可能です。たとえあなたがsequenceをtreeにかえたとしても、 Vinyals、Kaiserほか 2014年 ( PDF )にて試行されたように、分析木の生成において同じモデルが最新技術の結果をあたることができます。独自の翻訳機をビルドするだけでなく、構文解析、チャットボット、もしくはあなたが考えるどんなプログラムをビルドすることができます。実験してみてください!
-------- 


やっぱりサンプルは、最新の翻訳モデルではないのか..

基礎的なモデルだけチュートリアルやサンプルコードで紹介して、
Google翻訳につかっているモデルは..
 
..そりゃ、当然教えてはくれないわなあ..


ただ、日本語は単語間に隙間がないから、
このモデルをそのまま使うわけには行かないよな..

書籍「はじめてのディープラーニング」にかいてあった
バイドゥのBi-Directed RNNモデルか..


というか、RNNもきちっと理解してないしな..

RNN、調べるか..

0 件のコメント:

TensorFlowにおけるフィーチャ列はこうしてね的なブログ記事を翻訳してみた

TensorFlow 1.4.0 になってイロイロガチャガチャしている。 一番ビックリしたのがTutorialのSequence-to-Sequence。 イキナリブログ記事のリンクだけになっていた.. Sequence-to-Sequence Models ht...