TensorFlowチュートリアル - リカレント・ニューラルネットワーク（翻訳）

TensorFlow のチュートリアル（Recurrent Neural Networks）
https://www.tensorflow.org/versions/master/tutorials/recurrent/index.html#recurrent-neural-networks
の翻訳です。
翻訳の誤りなどあればご指摘お待ちしております。

---

##前置き

リカレント・ニューラルネットワークと、特に LSTM については、[この偉大な記事](http://colah.github.io/posts/2015-08-Understanding-LSTMs/)を参照してください。

##言語モデリング

このチュートリアルでは、言語モデルの挑戦的なタスクにおいて、リカレント・ニューラルネットワークを訓練する方法を示します。問題のゴールは、文に確率を割り当てる、確率モデルに適合することです。すなわち、前の単語の履歴を与えられたテキストにおいて、次の単語を予測します。この目的のために、ペン・ツリー・バンク（PTB）データセットを使用します。このデータセットは、小さく、訓練が比較的高速であり、これらのモデルの品質を測定するための一般的なベンチマークです。

言語モデルは、音声認識、機械翻訳、または画像説明など、多くの興味深い問題への鍵です。それはまた、楽しいです。[ここ](http://karpathy.github.io/2015/05/21/rnn-effectiveness/)を参照してください。

このチュートリアルでは、PTB データセットで非常に良い結果を達成した [Zaremba et al., 2014](http://arxiv.org/abs/1409.2329) の結果を再現します。

##チュートリアル・ファイル

このチュートリアルでは、models/rnn/ptb の、以下のファイルを参照します：

|ファイル 	|目的|
|:--|:--|
|ptb_word_lm.py 	|PTB データセットで言語モデルを訓練するコード|
|reader.py 	|データセットを読み取るコード|

##データのダウンロードと準備

このチュートリアルに必要なデータは、Tomas Mikolov の Web ページの PTB データセットの data/ ディレクトリにあります：
http://www.fit.vutbr.cz/~imikolov/rnnlm/simple-examples.tgz

データセットはすでに前処理され、文末マーカーと、レアな単語のための特殊記号（&lt;unk&gt;）を含む、全部で10000語の異なる単語が含まれています。ニューラルネットワークでの処理を容易にするため、reader.py でそれらの単語すべてを、一意の整数の識別子に変換します。

##モデル

###LSTM

モデルの中心部は LSTM セルで構成されます。LSTM セルは、一度に１つの単語を処理し、文のありうる継続の確率を計算します。ネットワークのメモリ状態をゼロ・ベクトルで初期化し、各単語を読み込んで、メモリ状態を更新します。また、計算上の理由から、サイズ batch_size のミニバッチでデータを処理します。

基本的な擬似コードは以下のようになります：

```py
lstm = rnn_cell.BasicLSTMCell(lstm_size)
# Initial state of the LSTM memory.
state = tf.zeros([batch_size, lstm.state_size])

loss = 0.0
for current_batch_of_words in words_in_dataset:
    # The value of state is updated after processing each batch of words.
    output, state = lstm(current_batch_of_words, state)

    # The LSTM output can be used to make next word predictions
    logits = tf.matmul(output, softmax_w) + softmax_b
    probabilities = tf.nn.softmax(logits)
    loss += loss_function(probabilities, target_words)
```

###打ち切り（truncated）バックプロパゲーション

学習プロセスを扱いやすくするために、バックプロパゲーションの勾配を、固定数（num_steps）の展開済みステップで打ち切るのが一般的です。一度に長さ num_steps の入力を供給し、各反復後に後方へ渡すことによってこれを実施するのが容易です。

打ち切りバックプロパゲーションのグラフを作成するコードの簡易版を、以下に示します：

```py
# Placeholder for the inputs in a given iteration.
words = tf.placeholder(tf.int32, [batch_size, num_steps])

lstm = rnn_cell.BasicLSTMCell(lstm_size)
# Initial state of the LSTM memory.
initial_state = state = tf.zeros([batch_size, lstm.state_size])

for i in range(len(num_steps)):
    # The value of state is updated after processing each batch of words.
    output, state = lstm(words[:, i], state)

    # The rest of the code.
    # ...

final_state = state
```

そして、データセット全体にわたって反復処理を実装する方法を、以下に示します：

```py
# A numpy array holding the state of LSTM after each batch of words.
numpy_state = initial_state.eval()
total_loss = 0.0
for current_batch_of_words in words_in_dataset:
    numpy_state, current_loss = session.run([final_state, loss],
        # Initialize the LSTM state from the previous iteration.
        feed_dict={initial_state: numpy_state, words: current_batch_of_words})
    total_loss += current_loss
```

###入力

単語IDはLSTMに供給される前に、密な表現に埋め込まれます（[ベクトル表現のチュートリアル](https://www.tensorflow.org/versions/master/tutorials/word2vec/index.html)を参照）。これにより、モデルは特定の単語についての知識を効率的に表現することができます。プログラミングは容易です：

```py
# embedding_matrix is a tensor of shape [vocabulary_size, embedding size]
word_embeddings = tf.nn.embedding_lookup(embedding_matrix, word_ids)
```

埋め込み行列はランダムに初期化され、モデルは、ただデータを見ることによって、言葉の意味を区別することを学習をします。

###損失関数

ターゲット単語の負の対数確率の平均を最小化します：

```math
\text{loss} = -\frac{1}{N}\sum_{i=1}^{N} \ln p_{\text{target}_i}
```

実装することは難しくありませんが、関数 sequence_loss_by_example が利用可能なので、ここでは単にこれを使用します。

論文で報告されている一般的な尺度は、単語ごとのパープレキシティの平均（多くの場合、単にパープレキシティと呼ばれる）です。パープレキシティは以下で与えられます。

```math
e^{-\frac{1}{N}\sum_{i=1}^{N} \ln p_{\text{target}_i}} = e^{\text{loss}}
```

訓練プロセスを通じて、この値を監視します。

###複数 LSTM のスタック

モデルにさらに表現力を与えるため、データ処理に複数の LSTM 層を追加することができます。第１層の出力は第２層の入力となります。

実装をシームレスにする、MultiRNNCell というクラスがあります：

```py
lstm = rnn_cell.BasicLSTMCell(lstm_size)
stacked_lstm = rnn_cell.MultiRNNCell([lstm] * number_of_layers)

initial_state = state = stacked_lstm.zero_state(batch_size, tf.float32)
for i in range(len(num_steps)):
    # The value of state is updated after processing each batch of words.
    output, state = stacked_lstm(words[:, i], state)

    # The rest of the code.
    # ...

final_state = state
```

##コードの実行

pip パッケージによりインストールされ、tensorflow の git リポジトリをクローンし、git ツリーのルートにいるとします（ソースからビルドする場合は、bazel を使用して tensorflow/models/rnn/ptb:ptb_word_lm ターゲットをビルド）。

そして、以下を実行します：cd tensorflow/models/rnn/ptb python ptb_word_lm --data_path=/tmp/simple-examples/data/ --model small

チュートリアルのコードには３つのサポートされているモデル構成があります：「small」、「medium」、「large」です。これらの差は LSTM の大きさ、および訓練に使用されるハイパーパラメータの集合です。

モデルが大きいほど、より良い結果が得られるはずです。small モデルはテスト・セットで120以下のパープレキシティに達し、large モデルでは、訓練に数時間かかる場合がありますが、80以下のパープレキシティに到達できるはずです。

##次は？

より良いモデルを作るための、触れていないいくつかのトリックがあります。例えば、以下です：

* 学習率の減少スケジュール
* LSTM 層間のドロップアウト

コードを研究し、モデルをさらに改善するように変更してください。