AWSのGPUインスタンスでTensorFlowを動かす

以下の環境で試したので、インストール時の作業記録を残しておきます。

- Mac OS X CPUのみ
- [Amazon web service(AWS)](http://aws.amazon.com/jp/) GPUあり

なお、TensoFlowはWindowsで動作しません。TensorFlowが採用しているGoogle製のビルドツールBazelが、LinuxとMacのみの対応です。手元にMacかLinuxマシンが無ければ、AWSでUbuntu環境を用意するのが簡単だと思います。

# Mac OS X
CPUのみで動作するパッケージをインストールしました。最もお手軽です。
**スペック**
- MacBook Pro (Retina, 15-inch, Mid 2014)
- CPU: 2.2 GHz Intel Core i7
- メモリ: 16 GB 1600 MHz DDR3

公式[ドキュメント](https://www.tensorflow.org/versions/master/get_started/os_setup.html#pip_install)通りpipでパッケージを導入。

```bash
$ sudo easy_install --upgrade six
$ sudo pip install --upgrade https://storage.googleapis.com/tensorflow/mac/tensorflow-0.6.0-py2-none-any.whl
```

テストとして、[CIFAR-10 dataset](https://www.cs.toronto.edu/~kriz/cifar.html)を学習してみます。

```bash
$ git clone https://github.com/tensorflow/tensorflow.git
$ cd tensorflow/tensorflow/models/image/cifar10/
$ python cifar10_train.py
```

実行すると、データセットがダウンロードされ学習が開始されます。
途中の経過がターミナルに出力されるので、学習が安定した100stepごろを見ると、1つのバッチ学習に0.540秒かかりました。

```bash
2015-12-31 15:00:08.397460: step 100, loss = 4.49 (237.0 examples/sec; 0.540 sec/batch)
```

---

# Amazon web service(AWS)
AWS EC2の[G2インスタンス](https://aws.amazon.com/jp/ec2/instance-types/)を使用して、環境構築します。

**注意: TensorFlowはCuda compute capability 3.5以下の場合は特別な対応が必要です。**
Cuda compute capabilityはGPUのアーキテクチャのようなもので、GPUによって決まっています。AWS G2インスタンスに搭載されているGRID K520はCuda compute capability3.0なので、そのままではTensorFlowのGPU計算を実行できません。[ここ](https://github.com/tensorflow/tensorflow/issues/25)で3.0への対応が議論されています。

料金の安かったオレゴン（米国）のインスタンスを使用しました。
以下の料金や情報は2015年12月30日時点の情報です。

|   モデル       | GPU    | vCPU  |  メモリ(GiB)  |  SSDストレージ(GB)  |  [料金](https://aws.amazon.com/jp/ec2/pricing/) - オレゴン(米国)  |
| ------------- |:------:|:-----:|:------------:|:-----------------:|:------------:|
| g2.2xlarge    | [GRID K520](http://www.nvidia.co.jp/object/cloud-gaming-gpu-boards-jp.html) x 1      | 8     | 15           | 1 x 60            | $0.65 /1 時間 |
| g2.8xlarge    | [GRID K520](http://www.nvidia.co.jp/object/cloud-gaming-gpu-boards-jp.html) x 4      | 32    | 60           | 2 x 120           | $2.6 /1 時間  |


# インストール
Linuxインスタンスへの接続は[ここ](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/AccessingInstancesLinux.html)を参照。
[こちら](https://gist.github.com/erikbern/78ba519b97b440e10640)を参考に進めました。
まずは必要なソフトウェアをインストール。

```bash
$ sudo apt-get update
$ sudo apt-get upgrade -y # “install package maintainers version”を選択
$ sudo apt-get install -y build-essential python-pip python-dev git python-numpy swig python-dev default-jdk zip zlib1g-dev ipython
```

NVIDIAドライバとの衝突をさけるため、Nouveauのブラックリストを追加。

```bash
$ echo -e "blacklist nouveau\nblacklist lbm-nouveau\noptions nouveau modeset=0\nalias nouveau off\nalias lbm-nouveau off\n" | sudo tee /etc/modprobe.d/blacklist-nouveau.conf
$ echo options nouveau modeset=0 | sudo tee -a /etc/modprobe.d/nouveau-kms.conf
$ sudo update-initramfs -u
$ sudo reboot
```

リブートされるので、再度ログインして、以下を実行。ここはなんで必要なのか理解していない。

```bash
$ sudo apt-get install -y linux-image-extra-virtual
$ sudo reboot
# Install latest Linux headers
$ sudo apt-get install -y linux-source linux-headers-`uname -r`
```

次に、CUDAとcuDNNをインストールします。[公式ドキュメントのここ](https://www.tensorflow.org/versions/master/get_started/os_setup.html#install_cuda)も参考に作業を進めます。なお、インストールするバージョンは必ず下記のものである必要があります。
- CUDA Toolkit 7.0
- cuDNN Toolkit 6.5

まずは、CUDAをインストール。

```bash
# Install CUDA 7.0
$ wget http://developer.download.nvidia.com/compute/cuda/7_0/Prod/local_installers/cuda_7.0.28_linux.run
chmod +x cuda_7.0.28_linux.run
$ ./cuda_7.0.28_linux.run -extract=`pwd`/nvidia_installers
$ cd nvidia_installers
$ sudo ./NVIDIA-Linux-x86_64-346.46.run
$ sudo modprobe nvidia
$ sudo ./cuda-linux64-rel-7.0.28-19326674.run
```

次にcuDNNをインストールします。cuDNNはDeep neural networkの学習をGPUで高速化することに特化したライブラリです。[この記事](http://news.mynavi.jp/articles/2015/04/10/gtc2015_cudnn/)が参考になります。cuDNNを入手するには、NVIDIAのデベロッパーアカウントへの登録が必要です。wgetで入手できないので、一度ローカル環境で[ここ](https://developer.nvidia.com/rdp/cudnn-archive)からダウンロードします。
ダウンロードしたファイルをローカルからSCPで転送します。
以下はLinuxからの転送例。xxxxx.amazonaws.comはAMIのパブリックDNS。

```bash
# ローカルで作業
# SCPでAMIへ転送
$ scp -i /path/my-key-pair.pem cudnn-6.5-linux-x64-v2.tgz ubuntu@xxxxx.amazonaws.com:~
```

転送が終わったら解凍し、cudaのディレクトリにコピーする。

```bash
# AMIで作業
$ cd
$ tar -xzf cudnn-6.5-linux-x64-v2.tgz
$ sudo cp cudnn-6.5-linux-x64-v2/libcudnn* /usr/local/cuda/lib64
$ sudo cp cudnn-6.5-linux-x64-v2/cudnn.h /usr/local/cuda/include/
```

パスを通します。

```bash
$ vi .bashrc # viやnanoなどで以下の2行を.bashrcに追加
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/cuda/lib64"
export CUDA_HOME=/usr/local/cuda
$ source .bashrc # .bashrcの設定を反映
```

AMIで利用出来るディスク領域はあまり大きくありません。この後、Bazelでビルドする際や、学習用データをダウンロードする場合に、十分に大きなディスク容量が必要になります。
インスタンス作成時に割り当てられるエフェメラルストレージ(/mnt/以下)には十分なディスク領域があるので、シンボリックリンクを作っておきます。
なお、もう使用しないnvidia_installersやcudnn-6.5-linux-x64-v2.tgzは削除しても構いません。

```bash
$ df
Filesystem     1K-blocks    Used Available Use% Mounted on
udev             7687184      12   7687172   1% /dev
tmpfs            1540096     344   1539752   1% /run
/dev/xvda1       8115168 5874536   1805356  77% /
none                   4       0         4   0% /sys/fs/cgroup
none                5120       0      5120   0% /run/lock
none             7700472       0   7700472   0% /run/shm
none              102400       0    102400   0% /run/user
/dev/xvdb       66946696   53144  63486192   1% /mnt
# /mnt/tmp/へのシンボリックリンクを作成
$ sudo mkdir /mnt/tmp
$ sudo chmod 777 /mnt/tmp
$ sudo rm -rf /tmp
$ sudo ln -s /mnt/tmp /tmp
```

**注意: インスタンス停止時に、/mnt/以下の内容は全て削除されます。AMIイメージに残す必要があるデータは/tmp/に保存しないでください。**
後述する公開AMIにはインスタンスの作成時やリスタート時に、エフェメラルストレージにtmpを作成するシェルスクリプト(create_tmp_on_ephemeral_storage.sh)を用意しました。

ビルドツールであるBazelをインストールします。

```bash
$ cd /mnt/tmp
$ git clone https://github.com/bazelbuild/bazel.git
$ cd bazel
$ git checkout tags/0.1.0
$ ./compile.sh
$ sudo cp output/bazel /usr/bin
```

次にTensorFlowをインストールします。
"./configure"は、[ここ](https://github.com/tensorflow/tensorflow/issues/25)で議論されている通り、"TF_UNOFFICIAL_SETTING=1 ./configure"のようにオプションをつけて実行します。これにより、Cuda compute capability 3.0にも対応可能なアンオフィシャル設定になります。

```bash
$ cd /mnt/tmp
$ git clone --recurse-submodules https://github.com/tensorflow/tensorflow
$ cd tensorflow
$ TF_UNOFFICIAL_SETTING=1 ./configure
```

コンフィグレーション中に、以下のような質問があります。デフォルトでは、Cuda compute capability 3.5と5.2のみの対応ですので、以下のように3.0を追加します。

```
Please specify a list of comma-separated Cuda compute capabilities you want to build with.
You can find the compute capability of your device at: https://developer.nvidia.com/cuda-gpus.
Please note that each additional compute capability significantly increases your build time and binary size.
[Default is: "3.5,5.2"]: 3.0,3.5,5.2   #3.0を追加
```

TensorFlowをビルド。

```bash
$ bazel build -c opt --config=cuda //tensorflow/cc:tutorials_example_trainer
```

次にTensorFlowのPythonパッケージをビルド、インストールします。
"/tmp/tensorflow_pkg/tensorflow-0.6.0-cp27-none-linux_x86_64.whl"の部分は、実際に生成されたバージョンのファイル名に合わせてください。

```bash
$ 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/tensorflow-0.6.0-cp27-none-linux_x86_64.whl
```

これで、インストール完了です。

## テスト
まずはg2.2xlargeインスタンスで試します。

```bash
$ cd tensorflow/models/image/cifar10/
$ python cifar10_multi_gpu_train.py
```

結果は以下のとおり。

```
2016-01-01 09:08:55.345446: step 100, loss = 4.49 (285.9 examples/sec; 0.448 sec/batch)
```

さらに、g2.8xlargeで試します。
以下のように、cifar10_multi_gpu_train.pyの63行目あたりでGPUの数を4に設定します。これを行わない場合、見かけはGPUを4つ使用しますが、並列化が正常に行われないためか高速化されません。

```python
tf.app.flags.DEFINE_integer('num_gpus', 4,
                            """How many GPUs to use.""")
```

実行結果。かなり高速されました。

```
2016-01-01 09:33:24.481037: step 100, loss = 4.49 (718.2 examples/sec; 0.178 sec/batch)
```


## コミュニティAMI
AWS オレゴン(米国)のコミュニティAMIに今回作成したイメージを公開しています。
**ubuntu14.04_tensorflow0.6.0_gpu - ami-69475f08**
インスタンスの作成後、create_tmp_on_ephemeral_storage.shを実行して、エフェメラルストレージ上の/tmpディレクトリを作成してください。

```bash
$ ./create_tmp_on_ephemeral_storage.sh
```