いまさらだけどTensorflowでDQN（完全版）を実装する

# はじめに
この記事は[前回の記事](http://qiita.com/yuishihara/items/0e530e9c0a17a7fa0111)の続きです。
前回は素のTensorflow(2016年7月時点)だけで実装できる、[なんちゃってDQN(不完全版)]()を紹介しましたが、ここでは[Mnihらの2015年の論文][1]で実際に実装された方法を忠実に再現する方法について書いています。

# 不完全版と今回の完全版の違い
1. Optimizerを通常のRMSPropから[A.Gravesが導入したRMSProp][2]に変更
2. Loss clippingを行う

特に1は問題で、素のTensorflowには実装されていないため、独自で実装する必要があります。
ここでは、Tensorflowでの実装方法とそこから得られる結果を紹介します。

# A.GravesによるRMSPropの実装
Tensorflowで新しいOptimizerを作るには、TensorflowのPythonコードとCppのコードに手を加える必要があります。
具体的には下記のファイルを修正、もしくはファイルを追加していきます。
基本的には、既存のRMSPropの実装されているところを見つけて、コピペして、修正していけば実装自体はそこまで難しくはありません

(Python)
1.tensorflow/contrib/layers/python/layers/optimizers.py
2.tensorflow/python/training/training_ops.py
3.tensorflow/python/training/rmspropgraves.py(新規作成)
4.tensorflow/python/training/rmspropgraves_test.py(必須ではないが、テストはしたほうがいいので用意する)

(Cpp側)
1.tensorflow/core/kernels/training_ops.cc
2.tensorflow/core/kernels/training_ops_gpu.cu.cc
3.tensorflow/core/ops/training_ops.cc

実装済のコードは[GitHub][3]に上げたので、具体的な実装はそちらを見てみてください。

# Tensorflowのコードからのビルド
[Tensorflowの公式ページ][4]に書いてありますが、少しハマったので具体的な手順を載せて起きます。
環境はUbuntu14.04として、進めます。

## まずTensorflowのコードを落とす
GitHubからとってきます。
それから、masterブランチは不安定で、ビルドに失敗することがあったので、リリースブランチをcheckoutする(ここではr0.9)。

```bash
$ git clone https://github.com/tensorflow/tensorflow
$ cd tensorflow
$ git checkout --track origin/r0.9
```

## Bazelのインストール
Tensorflowはbazelを使ってビルドを行うので、まずはそれをインストールします。
[Bazelの公式ページに書いてある方法][5]でインストールします。
Tensoflowのページは混乱をきたすので、軽くスルーしたほうがいいです。

## 必要なpythonのライブラリを入れる
これは公式どおり実行すればOK。

```bash
$ sudo apt-get install python-numpy swig python-dev python-wheel
```

## Configureする
tensorflowのルートディレクトリ（GitHubで落としたリポジトリのルートと同じ）でConfigureを実行して、環境の設定をする。

```bash
$ ./configure
(略)
```

途中いろいろ聞かれるが、GPU環境のセットアップをする場合は、基本yとEnter連打で行けるはず。
GPU環境ではなく、CPUのみの環境の場合は、

```bash
Do you wish to build TensorFlow with GPU support? [y/N] n
```

として、GPUのサポートを無効にする。

## ビルド&インストールする
ここまで来るとあと少しですが、公式に従うと、よくわからないtrainerをビルドさせられるので、注意が必要です。
下記で、pipインストールするためのwhlを作成、インストールします。

```bash
(CPU環境の場合)
$ bazel build -c opt //tensorflow/tools/pip_package:build_pip_package

(GPU環境の場合)
$ bazel build -c opt --config=cuda //tensorflow/tools/pip_package:build_pip_package

$ bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg
$ sudo pip install /tmp/tensorflow_pkg/(名前はどのブランチをビルドしたかとかで変わります)
```

# DQN(完全版)を実装する
コードは今回も[GitHub][6]に上げているので、興味ある方はぜひそちらを見てください。
ここでは、ポイントとなるLossClippingの実装について説明します。

LossClippingは誤差が1を超えたら傾きを1に固定する操作なのですが、今回は傾き1の直線は、微分すると1になるという性質を利用して、
1を超えたら、誤差が線形になるような関数を作ることでClippingを実現します。
具体的には下記のようにします。ここの実装は[こちら][7]を参考に少し変更を加えています。

```py
def clipped_loss(self, data, target, q_value_filter):
  filtered_qs = self.filtered_q_values(data, q_value_filter)
  error = tf.abs(target - filtered_qs)
  quadratic = tf.clip_by_value(error, 0.0, 1.0)
  linear = error - quadratic
  return tf.reduce_sum(0.5 * tf.square(quadratic) + linear)
```

他のネット上に見られる実装と違うのは、平均をとるのではなく、和をとっているところです。
Natureの論文を見ても、平均を取らなければいけないような明確な記述は見つけらず、平均を取ることが手元で実験する限りではメリットがなく、むしろ収束を著しく遅らせるだけでした。

平均を取るような実装にした場合は、実質的に学習率を小さくするような効果がでるので、学習がかなり進んだときに、さらなる高得点につながるかもしれませんが、DQNでAtariのゲームをする限りでは、個人的には効果がないのではと思います。

# 結果
ReplayMemoryを100万から40万にした以外、パラメータは同じにして、150万イテレーション回したあたりの結果を載せておきます。

<blockquote class="twitter-video" data-lang="en"><p lang="en" dir="ltr">DQN with tensorflow 2 <a href="https://t.co/JRwM0MsTDG">pic.twitter.com/JRwM0MsTDG</a></p>&mdash; EpsilonCode (@epsilon_code) <a href="https://twitter.com/epsilon_code/status/762108198304165888">August 7, 2016</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

不完全版の時と学習のパラメータやイテレーションの回数が違う動画になってしまっていて、不完全版でも実は同じくらいの性能出るのでは？
と思われるかもしれませんが、やってみるとそんなことはなく、この設定の方が圧倒的に性能が良かったです。
コードはGitHubにありますので、ぜひ試してみてください。

# 参考文献
[V.Mnih et al. Human-level control through deep reinforcement learning][1]
[A.Graves Generating Sequences With Recurrent Neural Networks][2]
[DQNをKerasとTensorFlowとOpenAI Gymで実装する][6]

[1]:http://www.nature.com/nature/journal/v518/n7540/abs/nature14236.html?lang=en
[2]:https://arxiv.org/pdf/1308.0850.pdf
[3]:https://github.com/yuishihara/tensorflow/tree/rmspropgraves
[4]:https://www.tensorflow.org/versions/r0.10/get_started/os_setup
[5]:http://bazel.io/docs/install.html
[6]:https://github.com/yuishihara/DQN-tensorflow/tree/dqn_nature_ver
[7]:https://elix-tech.github.io/ja/2016/06/29/dqn-ja.html