AWSのGPUインスタンスを利用してTensorflowを試す

## EC2のセットアップ
以下の通りp2.xlargeというGPUインスタンスを利用して、Ubuntuのインスタンスを構築します。
(現状、東京リージョンではp2インスタンスは利用できないため、ここでは)

* AMIの選択: Ubuntu Server 16.04 LTS (HVM), SSD Volume Type - ami-80861296
* インスタンスタイプの選択: p2.xlarge(GPUコンピューティング) $0.90/h
* インスタンスの詳細の設定: デフォルトのまま
* ストレージの追加: 64GiB 汎用SSD
* Add Tags: なし
* セキュリティグループの追加: 新しいセキュリティグループを作成する
  * sg_01: ssh 22, カスタムTCP 9898(jupyter用)
* 確認と作成
  * 新しいキーペアの作成: kp_01.pem

セキュリティグループとキーペアは適当な名前を設定します。(ここでは、```sg_01```、```kp_01```としています。)
また、既存のキーペアを利用しても問題ありません。

キーペアをダウンロードしたら、.sshに移動してパーミションを変更します。

```
$ mv ~/Download/kp_01.pem ~/.ssh/.
$ chmod 600 ~/.ssh/kp_01.pem
```

インスタンスが作成されたら、マネジメントコンソールでPublic DNSを確認し、SSHでログインします。

```
$ ssh -i ~/.ssh/kp_01.pem ubuntu@<Public DNS>
```

以下はEC2上での作業となります。まず、パッケージを更新しておきます。

```
$ sudo apt-get update
$ sudo apt-get upgrade
```

## CUDA
### CUDA 8.0のインストール
URL: https://developer.nvidia.com/cuda-downloads
Installation guide: http://docs.nvidia.com/cuda/cuda-installation-guide-linux/index.html

#### 事前確認
CUDAに対応したGPUを搭載しているか確認

```
$ lspci | grep -i nvidia
00:1e.0 3D controller: NVIDIA Corporation GK210GL [Tesla K80] (rev a1)
```

CUDAに対応したOSか確認

```
$ uname -m && cat /etc/*release
x86_64
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.2 LTS"
NAME="Ubuntu"
VERSION="16.04.2 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.2 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
VERSION_CODENAME=xenial
UBUNTU_CODENAME=xenial
```

gcc(+開発ツール群)のインストール

```
$ sudo apt-get install build-essential
```

動作中のカーネルと同じバージョンのカーネルヘッダーをインストール

```
$ sudo apt-get install linux-headers-$(uname -r)
```

#### インストール
https://developer.nvidia.com/cuda-downloads の「Select Target Platform」で以下の通り選択するとダウンロードリンクとインストール手順が表示されます。
リンク先のURLからwgetでファイルを取得して、インストールします。
(ここでは```cuda-repo-ubuntu1604-8-0-local-ga2_8.0.61-1_amd64-deb```をインストールします。)

```
$ wget https://developer.nvidia.com/compute/cuda/8.0/Prod2/local_installers/cuda-repo-ubuntu1604-8-0-local-ga2_8.0.61-1_amd64-deb
$ sudo dpkg -i cuda-repo-ubuntu1604-8-0-local-ga2_8.0.61-1_amd64-deb
$ sudo apt-get update
$ sudo apt-get install cuda
```

#### 環境変数の設定
~/.bash_profileに以下の通り設定します。

```:~/.bash_profile
export CUDA_HOME="/usr/local/cuda-8.0"
export PATH="${CUDA_HOME}/bin${PATH:+:${PATH}}"
export LD_LIBRARY_PATH="${CUDA_HOME}/lib64${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}"
```

ログインし直して設定を反映します。

```
$ exec $SHELL -l
```

#### 動作確認
サンプルプログラムをビルドして動作確認をします。(実行しなくても問題ありません。)

```
$ cuda-install-samples-8.0.61.sh test
$ cd test/NVIDIA_CUDA-8.0_Samples
$ sed -i "s/nvidia-367/nvidia-375/g" `grep "nvidia-367" -r ./ -l`
$ make
```

※sedの行は、今回のバージョンのサンプルプログラムに含まれるMakefileでドライバの指定に誤りがあったため置換しています。
(参考: https://askubuntu.com/questions/911548/cuda-examples-not-working-after-cuda-8-0-install)

### cuDNN 5.1のインストール
URL: https://developer.nvidia.com/cudnn
ダウンロードにはNVIDIA Developer Programの会員登録が必要です。
認証が必要なため、ファイルをいったんローカルPCにダウンロードし、SCPでEC2にアップロードします。
(ここでは```cudnn-8.0-linux-x64-v5.1.tgz```を利用します。）

ローカルからSCP

```
$ scp -i ~/.ssh/kp_01.pem ~/Downloads/cudnn-8.0-linux-x64-v5.1.tgz ubuntu@<Public DNS>:~/.
```

EC2上でインストール(ファイルの展開と配置のみ)

```
$ tar zxvf cudnn-8.0-linux-x64-v5.1.tgz
$ sudo cp cuda/include/* ${CUDA_HOME}/include/.
$ sudo cp cuda/lib64/* ${CUDA_HOME}/lib64/.
```

### NVIDIA CUDA Profile Tools Interface(libcupti-dev)のインストール
apt-getでインストールできます。

```
$ sudo apt-get install libcupti-dev
```

ただし、今回は実行した時に"*** is not a symbolic link"というエラーが出たため、以下の通り解決しました。
(参考: http://stackoverflow.com/questions/43016255/libegl-so-1-is-not-a-symbolic-link)

```
$ sudo mv /usr/lib/nvidia-375/libEGL.so.1 /usr/lib/nvidia-375/libEGL.so.1.org
$ sudo ln -s /usr/lib/nvidia-375/libEGL.so.375.39 /usr/lib/nvidia-375/libEGL.so.1

$ sudo mv /usr/local/cuda-8.0/targets/x86_64-linux/lib/libcudnn.so.5 /usr/local/cuda-8.0/targets/x86_64-linux/lib/libcudnn.so.5.org
$ sudo ln -s /usr/local/cuda-8.0/targets/x86_64-linux/lib/libcudnn.so.5.1.10 /usr/local/cuda-8.0/targets/x86_64-linux/lib/libcudnn.so.5

$ sudo mv /usr/lib32/nvidia-375/libEGL.so.1 /usr/lib32/nvidia-375/libEGL.so.1.org
$ sudo ln -s /usr/lib32/nvidia-375/libEGL.so.375.39 /usr/lib32/nvidia-375/libEGL.so.1
```

### GPU設定
http://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/accelerated-computing-instances.html
を参考に"GPU 設定の最適化 (P2 インスタンスのみ)"を適用します。

```
$ sudo nvidia-smi -pm 1
$ sudo nvidia-smi --auto-boost-default=0
$ sudo nvidia-smi -ac 2505,875
```

## Python環境
[こちら](http://qiita.com/y__sama/items/5b62d31cb7e6ed50f02c)の記事を参考にして、pyenv + minicondaの環境を作成します。
(「anaconda単独だと実は問題があります。」とのこと)

### pyenv
https://github.com/pyenv/pyenv#installation

git cloneして```~/.bash_profile```の設定をします。

```
$ git clone https://github.com/pyenv/pyenv.git ~/.pyenv
```

```:~/.bash_profile
export PYENV_ROOT="${HOME}/.pyenv"
export PATH="${PYENV_ROOT}/bin:${PATH:+:${PATH}}"
eval "$(pyenv init -)"
```

### miniconda
pyenvで最新のminiconda(ここでは、miniconda3-4.3.11)をインストールします。

```
$ pyenv install -l | grep miniconda
...
(省略)
...
  miniconda3-4.3.11

$ pyenv install miniconda3-4.3.11
```

```:~/.bash_profile
export CONDA_HOME="${PYENV_ROOT}/versions/miniconda3-4.3.11"
export PATH="${CONDA_HOME}/bin${PATH:+:${PATH}}"
```

## Tensorflow
### Install with Anaconda
condaでAnaconda環境を作成して、Tensorflowをインストールします。

```
$ conda create -n tensorflow python=3.5 anaconda
$ source activate tensorflow
(tensorflow)$ pip install --ignore-installed --upgrade https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow_gpu-1.1.0-cp35-cp35m-linux_x86_64.whl
```
## Jupyter notebook
http://jupyter-notebook.readthedocs.io/en/latest/public_server.html
EC2上で起動したJupyter notebookにローカルPCから接続するための設定をします。

* 任意のホスト名でアクセス
* ポート番号をデフォルトから変更(ここでは9999に設定)
* httpsで接続
* パスワードの設定

### サーバ証明書と鍵ファイルの作成

```
(tensorflow)$ mkdir certificate
(tensorflow)$ cd certificate
(tensorflow)$ openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mykey.key -out mycert.pem
```

### パスワードのハッシュ値作成

```
(tensorflow)$ python
>>> from notebook.auth import passwd
>>> passwd()
Enter password: 
Verify password:
'sha1:********'
>>> exit()
```

### jupyterの設定ファイル作成
設定ファイルの雛形を出力

```
(tensorflow)$ jupyter notebook --generate-config
```

以下の設定を追加

```:~/.jupyter/jupyter_notebook_config.py
c.NotebookApp.certfile = '/home/ubuntu/certificate/mycert.pem'
c.NotebookApp.keyfile = '/home/ubuntu/certificate/mykey.key'
c.NotebookApp.ip = '*'
c.NotebookApp.port = 9999
c.NotebookApp.open_browser = False
c.NotebookApp.password='sha1:********'
```

### Jupyter notebook起動

```
(tensorflow)$ jupyter notebook
```

ローカルPCのブラウザで```https://<Public DNS>:9999```にアクセスすると、パスワード入力画面が表示されるので、「パスワードのハッシュ値作成」の際に入力したパスワードを入力してログインします。