Tensorflow + GPU 環境を nvidia-docker を使って楽に作る (on CentOS 7.2)

※Ubuntu版はこちらをご参照ください。
[Tensorflow + GPU 環境を nvidia-docker を使って楽に作る (on Ubuntu 17.04)](http://qiita.com/ttomoaki/items/b3be56d4df5f80c555af)

# 背景
以前自作GPUマシン上でDeepLearning用の環境を構築したのですが、

1. GPUのドライバインストール
1. Cuda/Cudnnのインストール
1. DeepLearningライブラリのインストール
1. 動作テスト

とステップが多く、ハマると結構インストールに苦労します。
また、Cuda/Cudnn、各ライブラリ共にバージョンアップが頻繁であったり、色々とモジュールを入れているうちに依存関係が壊れてしまったり何かと環境を作り直したくなることも多いです。
簡単に環境を作り直せるようにしたいと思い、NVIDIAが提供しているnvidia-dockerを用いた環境構築の方法を試してみました。


# 目的
GPUマシン上でのDeepLearning用環境の構築は何かと手間が多いので最大限楽したい。
クラウドのサーバ上や自作PC上でさくっとTensorflow on GPU環境を構築したい。

# 使うもの

- GPU搭載のサーバ（今回はIBM Softlayer）
    - OS : CentOS7.2
    - CPU : 2.4GHz Intel Xeon-Haswell x 2
    - GPU : NVIDIA GRID K2 x 1
- nvidia-docker (https://github.com/NVIDIA/nvidia-docker)
- docker (https://www.docker.com/)
- Tensorflow (https://github.com/tensorflow/tensorflow)

※2016/9/30追記
Tensorflowのバージョン0.10.0でも動くことを確認しました。
 - Tensorflow version : 0.10.0
 - GPU ： NVIDIA Corporation GK210GL [Tesla K80]
 - Driver : http://jp.download.nvidia.com/XFree86/Linux-x86_64/361.93.02/NVIDIA-Linux-x86_64-361.93.02.run
   (BETA版と書かれていたけど動きました) 

# nvidia-dockerとは
dockerのラッパーとして実装されているツールで、ホストOS上にNVIDIAのドライバがインストールされていれば、CudaToolkitを含んだコンテナからGPUを利用出来るようになります。
参考）http://www.nvidia.co.jp/object/docker-container-jp.html

# 導入手順
以下の手順で導入できます。

1. ドライバインストール（ホストOS上）
1. Dockerインストール（ホストOS上）
1. nvidia-dockerインストール（ホストOS上）
1. Tensorflow + GPU のイメージ作成＆実行

## 1. ドライバインストール
nouveauグラフィックスドライバを無効化した後、NVIDIAのドライバをインストールします。
（nouveauを無効化しないとドライバインストールが失敗するため）

```bash
## nouveau ドライバーを無効にする
sudo su -

# nouveauが読み込まれていることを確認
lsmod | grep nouveau
---
nouveau              1403757  0
video                  24400  1 nouveau
mxm_wmi                13021  1 nouveau
i2c_algo_bit           13413  1 nouveau
drm_kms_helper        125008  1 nouveau
ttm                    93441  1 nouveau
drm                   349210  3 ttm,drm_kms_helper,nouveau
i2c_core               40582  5 drm,i2c_i801,drm_kms_helper,i2c_algo_bit,nouveau
wmi                    19070  2 mxm_wmi,nouveau
---
  
# nouveauなしの起動イメージ生成
mv /boot/initramfs-$(uname -r).img /boot/initramfs-$(uname -r)-nouveau.img
dracut --omit-drivers nouveau /boot/initramfs-$(uname -r).img $(uname -r)
  
# 依存からロードされないように設定変更
echo 'blacklist nouveau' >> /etc/modprobe.d/modprobe.conf
echo 'blacklist nouveau' >> /etc/modprobe.d/nouveau_blacklist.conf
  
# 再起動
reboot now

# nouveauが読み込まれていないことを確認
lsmod | grep nouveau
  
## nvidia drive のインストール  
yum -y install pciutils gcc kernel-devel tmux git

# GPUの型番を確認
lspci | grep -i nvidia
---
83:00.0 VGA compatible controller: NVIDIA Corporation GK104GL [GRID K2] (rev a1)
84:00.0 VGA compatible controller: NVIDIA Corporation GK104GL [GRID K2] (rev a1)
---

# http://www.nvidia.co.jp/Download/index.aspx?lang=jp
# ここからGPUに合った最新のドライバを探す
# GRID -> GRID Series -> Linux 64bit
cd /tmp/
wget http://us.download.nvidia.com/XFree86/Linux-x86_64/367.27/NVIDIA-Linux-x86_64-367.27.run
sh NVIDIA-Linux-x86_64-367.27.run
# 誘導にしたがってインストールする
```

## 2. Dockerインストール
[公式のインストールマニュアル](https://docs.docker.com/engine/installation/linux/centos/)に従ってインストールします。

```bash
sudo tee /etc/yum.repos.d/docker.repo <<-'EOF'
[dockerrepo]
name=Docker Repository
baseurl=https://yum.dockerproject.org/repo/main/centos/7/
enabled=1
gpgcheck=1
gpgkey=https://yum.dockerproject.org/gpg
EOF
  
sudo yum install docker-engine
sudo service docker start
sudo docker run hello-world
# Hello from Docker! のメッセージを確認

# 自動起動設定
sudo chkconfig docker on

# 一応docker-composeもインストール
sudo sh -c 'curl -L https://github.com/docker/compose/releases/download/1.10.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose'
sudo chmod +x /usr/local/bin/docker-compose
```

## 3. nvidia-dockerインストール
[公式のQuickStart](https://github.com/NVIDIA/nvidia-docker)に従ってインストールします。

```bash
### nvidia-docker install
# Install nvidia-docker and nvidia-docker-plugin
wget -P /tmp https://github.com/NVIDIA/nvidia-docker/releases/download/v1.0.0-rc.3/nvidia-docker-1.0.0.rc.3-1.x86_64.rpm
sudo rpm -i /tmp/nvidia-docker*.rpm && rm /tmp/nvidia-docker*.rpm
sudo systemctl start nvidia-docker
sudo systemctl enable nvidia-docker
 
# Test nvidia-smi
nvidia-docker run --rm nvidia/cuda nvidia-smi
# 以下の様な出力を確認
---
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 367.27                 Driver Version: 367.27                    |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GRID K2             Off  | 0000:83:00.0     Off |                  Off |
| N/A   31C    P8    18W / 117W |   3794MiB /  4036MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
|   1  GRID K2             Off  | 0000:84:00.0     Off |                  Off |
| N/A   28C    P8    17W / 117W |     38MiB /  4036MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
+-----------------------------------------------------------------------------+
---
```

## 4. Tensorflow + GPU のイメージ作成＆実行
https://github.com/tensorflow/tensorflow/tree/master/tensorflow/tools/docker
Tensorflowが提供しているDockerhubのイメージを使うと以下の様なエラーが出るので (2016/7/20時点)、

```
F tensorflow/stream_executor/cuda/cuda_dnn.cc:204] could not find cudnnCreate in cudnn DSO; dlerror: /usr/local/lib/python2.7/dist-packages/tensorflow/python/_pywrap_tensorflow.so: undefined symbol: cudnnCreate
[I 16:47:11.347 NotebookApp] KernelRestarter: restarting kernel (1/5)
```

githubのmasterにあるDockerファイルからイメージをビルドします。

```bash
## run tensorflow docker

# ※これうまく動かなかった
#nvidia-docker run -it -p 8888:8888 gcr.io/tensorflow/tensorflow:latest-gpu
  
# TensorflowのmasterにあるDockerfileからビルドする
git clone https://github.com/tensorflow/tensorflow.git
cd tensorflow/tensorflow/tools/docker
docker build -t $USER/tensorflow-suffix -f Dockerfile.gpu .
nvidia-docker run -it -p 8888:8888 $USER/tensorflow-suffix
```

Dockerを起動後
http://${HOST_IP}:8888/
にアクセスすればjupyterの画面が確認できます。

![jupyter_home.png](https://qiita-image-store.s3.amazonaws.com/0/39236/85f7665b-fb97-6dd6-118d-9a9bc82856d3.png)

3_mnist_from_scratch.ipynb のnotebookを起動して Cell -> Run All でサンプルが動くことを確認します。結果を確認。

![3_mnist_from_scratch_result.png](https://qiita-image-store.s3.amazonaws.com/0/39236/74058b74-aa5b-c862-b6cc-1877aca6825b.png)

jupyterのログでGPUを使って計算されていることを確認します。

```
I tensorflow/stream_executor/dso_loader.cc:108] successfully opened CUDA library libcublas.so locally
I tensorflow/stream_executor/dso_loader.cc:108] successfully opened CUDA library libcudnn.so locally
I tensorflow/stream_executor/dso_loader.cc:108] successfully opened CUDA library libcufft.so locally
I tensorflow/stream_executor/dso_loader.cc:108] successfully opened CUDA library libcuda.so locally
I tensorflow/stream_executor/dso_loader.cc:108] successfully opened CUDA library libcurand.so locally
I tensorflow/core/common_runtime/gpu/gpu_init.cc:102] Found device 0 with properties:
name: GRID K2
major: 3 minor: 0 memoryClockRate (GHz) 0.745
pciBusID 0000:83:00.0
Total memory: 3.94GiB
Free memory: 3.91GiB
W tensorflow/stream_executor/cuda/cuda_driver.cc:572] creating context when one is currently active; existing: 0x6f991e0
I tensorflow/core/common_runtime/gpu/gpu_init.cc:102] Found device 1 with properties:
name: GRID K2
major: 3 minor: 0 memoryClockRate (GHz) 0.745
pciBusID 0000:84:00.0
Total memory: 3.94GiB
Free memory: 3.91GiB
I tensorflow/core/common_runtime/gpu/gpu_init.cc:126] DMA: 0 1
I tensorflow/core/common_runtime/gpu/gpu_init.cc:136] 0:   Y Y
I tensorflow/core/common_runtime/gpu/gpu_init.cc:136] 1:   Y Y
I tensorflow/core/common_runtime/gpu/gpu_device.cc:806] Creating TensorFlow device (/gpu:0) -> (device: 0, name: GRID K2, pci bus id: 0000:83:$0.0)
I tensorflow/core/common_runtime/gpu/gpu_device.cc:806] Creating TensorFlow device (/gpu:1) -> (device: 1, name: GRID K2, pci bus id: 0000:84:$0.0)
W tensorflow/core/common_runtime/bfc_allocator.cc:213] Ran out of memory trying to allocate 2.92GiB. The caller indicates that this is not a failure, but may mean that there could be performance gains if more memory is available.
W tensorflow/core/common_runtime/bfc_allocator.cc:213] Ran out of memory trying to allocate 2.59GiB. The caller indicates that this is not a failure, but may mean that there could be performance gains if more memory is available.
W tensorflow/core/common_runtime/bfc_allocator.cc:213] Ran out of memory trying to allocate 5.84GiB. The caller indicates that this is not a failure, but may mean that there could be performance gains if more memory is available.
W tensorflow/core/common_runtime/bfc_allocator.cc:213] Ran out of memory trying to allocate 5.19GiB. The caller indicates that this is not a failure, but may mean that there could be performance gains if more memory is available.
W tensorflow/core/common_runtime/bfc_allocator.cc:213] Ran out of memory trying to allocate 5.20GiB. The caller indicates that this is not a failure, but may mean that there could be performance gains if more memory is available.
```

以上です。

# Reference
[nvidia-dockerでtensorflowを動かす - Qiita](http://qiita.com/ymfj/items/6d2658240aaa02663453)
[Ubuntu14.04.3でnvidia-docker使ってCaffeをインストールしてみた - Qiita](http://qiita.com/daxanya1/items/f04c7f75a6d2ecb92b23)