TensorFlow - Recurrent Neural Networks

今回はリカレントニューラルネットワークのチュートリアルを見ていきます。リカレントニューラルネットワーク（RNN）はネットワーク内にループを持たせ、過去の入力を考慮して現在の入力解析を行うことができるニューラルネットワークで、1980年代に提案されました。動画や音声、文章などの時系列データを対象とすることができ、機械翻訳や音声認識、身体動作パターンの学習などによるロボット制御、チャットボットなどの対話システムなどに利用されています。AppleのSiriやMicrosoftのりんなにも時系列データを利用する例として挙げられるかと思います（RNNを使用しているかは分かりませんが）。ナイトライダーのK.I.T.T.（キット）もRNNを搭載しているかもしれません:)

ゲームでのRNNの利用もいろいろと考えられます。例えば、対話のみでみても、RPGのNPCの会話をRNNベースにするだけでこれまでと全く異なるゲーム体験になるかと思います。映画「her／世界でひとつの彼女」では主人公が人工知能OS「サマンサ」に夢中になってしまいますが、それと比較して、ゲームのAIが人工的に感じられてしまう要因の１つは、時系列の変化に対応できずにいる点が大きいかと思います。

# RNN
RNNは非常に多くの種類が存在します。

- Hopfield network
- Fully recurrent network
- Elman network
- Jordan network
- Echo state network (ESN)
- Long short term memory network (LSTM)
- Bi-directional RNN (BRNN)

この中でシンプルとされるのがElman Networkです。RNNはtの時の状態がt+1の状態に影響します。ニューラルネットワークでは入力層と出力層の2つのレイヤーが存在しますが、時系列データを扱う場合は、t＋1の状態では入力層はt, t+1の２つのイメージになります。Elman Networkでは、入力層、隠れ層、出力層のフローを持ち、tの隠れ層はt+1でも利用されます。つまり、t+1の状態では隠れ層(t)はもう一度同じ隠れ層として再利用されます。

このモデルには、時間を遡るように誤差が伝搬される問題があります。この修正を行うのがBack propagation through time(BPTT)です。モデルの修正を行うのはBPと呼ばれていますが、BPTTでは過去の時間を考慮した修正を行います。BPTTでは時間軸に展開した計算グラフ上で、単純に誤差逆伝搬を行います。これで勾配を計算し、勾配ベースの手法を用いてパラメータを最適化します。重みが時系列の数だけ掛けられるため、BPTTの勾配が爆発したり、消失する問題が発生します。爆発に対しては、最大の制限や勾配の正規化などで対処可能ですが、消失については、消失する前に古いデータを切り取るなどの対応が必要となります。つまり、長すぎる時系列データにはElmanは不向きです。

# チュートリアル
ここでTensorFlowの話に切り替わりますが、TensorFlowにもリカレントニューラルネットワークのチュートリアルが用意されており、このチュートリアルではLTSMが利用されています。このLSTMはElmanで問題となる誤差の消失を減衰させないように伝搬させていくことを目的としています。そのために、LSTMではエラーが重みをかけずに伝搬するようにします。なので、減衰の要因そのものが取り除かれます。

さて、コードですが、LSTMの利用にあたっては、LSTMセルがモデルのコアとなります。LSTMセルでは、一度に１つの単語の処理を行い、文が可能な継続の確率を計算します。基本的な疑似コードは下記の通りです。ここでもMNISTでお馴染みのソフトマックス回帰が使用されています。

```lang: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 Backpropagation
次にBack propagationを打ち切るための実装です。LSTMでは誤差の減衰はなくとも、打ち切りは必要になります。下記の疑似コードではnum_stepsで打ち切られるよう実装が変更されています。

```lang: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
```

なお、GitHubのコードではLSTMのマルチレイヤーが使用されています。第一層の出力は第二層の入力として使用されます。

```lang: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
```

#所感
GoogleがTensorFlowを公開した背景として、もっとも需要なのはデータにあるためとよく言われていますが、どのようにTensorFlowに利用するデータを収集していくかは悩みの種です。オープンソース化が一気に進んだ今となっては、機械学習の導入するタイミングの早さよりも、こうしたデータを活用できるよう蓄積してきた企業のほうが強みを握っているのかもしれません。

#参考
[リカレントニューラルネットワークの概要と動作原理](http://wbawakate.jp/wp-content/uploads/2015/03/RNN%E3%83%95%E3%82%9A%E3%83%AC%E3%82%BB%E3%82%99%E3%83%B3.pdf)
[Recurrent Neural Networks](http://www.slideshare.net/beam2d/pfi-seminar-20141030rnn)
[ニューラルネットワークで時系列データの予測を行う](http://qiita.com/icoxfog417/items/2791ee878deee0d0fd9c)