Titan X (Pascal) 搭載のUbuntu 16.04で深層学習用の環境を構築する

(この記事は，以前WordPressで書いた記事 [Deep learning on Ubuntu 16.04 with Titan X Pascal (Part 1)](http://masahirodll.com/misc/?p=464), [Deep learning on Ubuntu 16.04 with Titan X Pascal (Part 2)](http://masahirodll.com/misc/?p=51 )のQiita版です．）

「コンシューマ向け最強のデュープラーニング用マシンが欲しいんや！」

Ubuntu 16.04 + Titan X (Pascal)でやっている記事が意外と少ないみたいなので備忘録がてら書き残します．

マシンの調達等を除けば，半日〜１日ぐらいでセットアップできると思います．

*(注意：すでにNVIDIAドライバがインストールされている計算機がある場合は「6. Dockerのインストール」から始めてもOKです．）*

## 0. はじめに
デュープラーニングで使われているライブラリ群は今まさに急激な進歩を遂げています．これゆえ，頻繁に更新が行われてぐんぐん性能が向上していったり便利になったりするのは素晴らしいことなのですが，ライブラリ同士の相性の問題で動かなくなったりすることもしばしばあります．また，複数人で同じマシンを使う場合や，ちょっとライブラリを試してみたいといった時に，環境がごちゃごちゃになって収拾がつかなくなってしまう恐れもあります．

このため，近年急速に注目を集めているDocker上で簡単に深層学習環境が構築できるシステムを作ってみました．計算力が必要な時は，AWSやAzureなどのクラウドプラットフォームから用意しておいたDockerのイメージをダウンロードすればすぐに同じ手元と同じ環境をクラウド上に再現することができますし，万が一計算機がぶっ壊れた時も環境構築にかかる手間が大幅に削減できます．

Docker上でもGPU使えるの？と思う方もいるかもしれませんが，2016年にNVIDIA謹製のDockerのプラグインが登場し，これを使って簡単にGPU対応のコンテナを作ることができるようになりました．

とっても簡単に運用できるので是非お試しください．

## 1. 用意するハードウェア
### 1.1 PC
深層学習用マシンでは基本的にGPUを24時間酷使するので，信頼性の高い電源が使われているゲーミングマシンをベースに組むとよいでしょう．今回は定評のある[DellのAlienware Area-51 Surpremacy](http://www.dell.com/jp/p/alienware-area51-r2/pd?oc=caaaw5114jp&model_id=alienware-area51-r2)を使いましたが，最近のNVIDIA GPUが乗るようなPCであれば大丈夫です．

ちなみに，Alienwareでは入力に使うUSBデバイス（キーボード）が接続されていないと起動しないというよくわからない仕様になっていますので，キーボードを接続したまま運用したほうがいいです．じゃないとsshとかで気持ちよくsudo rebootとかやると，そのまま帰ってこなくなってしまいます．

### 1.2 GPU
深層学習を気持ちよく回すためには性能の良いGPUが必須です．Alienwareにも元々NVIDIA GTX1080が乗っているのですが，Pascalアーキテクチャのデスクトップ向けGPUであるNVIDIA Titan X (Pascal)に換装しました．押しも押されぬ現行最高スペックのシングルGPUで，GTX1080に比べてメモリが8GBから12GBになったことで学習できるニューラルネットの規模が大幅に向上しています．日本国内ではamazonのみが販売しています．お値段20万ほどです．

### 1.3 USBメモリ
Ubuntuをインストールするライブメディアに使用します．Ubuntuを丸々コピーするので，4GB以上あるものが望ましいです．

## 2. ライブUSBメモリの準備
Ubuntuをクリーンインストールするために，USBでUbuntuのインストールメディアを用意します．

深層学習用の計算機にはLinuxがインストールされていることがほとんどです．おそらくTensorflowが基本的にはLinuxにのみ対応していることや，各種機械学習系ライブラリがpythonで提供されているためだと思います．一応Tensorflow0.10からWindowsにも対応しているらしいですが，tensorflowのgithubをみる限り，Windows(GPU)環境で自動テストされていないみたいです．また，Linuxのなかでもインターネット上のリソースも豊富であることや扱いやすいことから，Ubuntuが選ばれています．

Ubuntuのクリーンインストールは，Ubuntuのイメージを焼き付けたUSBメモリから起動することで行いますので，あらかじめUSBメモリにUbuntu 16.04.01 LTS (64bit) のイメージを焼きつけます．

（注意：以下の作業はWindowsで行うことを想定していますが，他のOSでも同様にライブメディアを作成することができます．）

（この部分は，別の記事（[UNetbootin その5 - WindowsでUNetbootinを利用するには・UbuntuのライブUSBメモリーを作成する](https://www.amazon.co.jp/NVIDIA-Pascal-GPUアーキテクチャー採用グラフィックカード-TITAN-X/dp/B01M0C8QXE)）でも丁寧に解説されています．）

### 2.1  USBメモリのフォーマット
ライブメディアを作るために，まずはUSBメモリをフォーマットします．フォーマットすると中身が全て書き換えられますので，必ず中のファイルを退避させておいてください．

エクスプローラー上でUSBメモリドライブを右クリックし，「フォーマット」を選択します．ファイルシステムを「FAT32」に設定した上で，開始ボタンを押すとフォーマット完了です．

### 2.2  Ubuntuのイメージのダウンロード
[Ubuntuの公式サイト](https://www.ubuntu.com/download/desktop)から Ubuntu 16.04.1 LTSのイメージ（ubuntu-16.04.1-desktop-amd64.iso）をダウンロードします．

### 2.3 UbuntuイメージをUSBメモリに焼き付ける
ダウンロードしたイメージをUSBメモリに焼き付けるために，UNetbootinというソフトをインストールします．[公式サイト](https://unetbootin.github.io/)からWindows版をダウンロードします．
UNetbootinを起動したら，「ディスクイメージ」を選択し，先ほどダウンロードしておいた.isoファイルを選択してください．
一番下のタイプには「USBドライブ」，ドライブにはUSBメモリのドライブ文字を選択してOKをおすと，あとは自動でライブUSBメモリができます．（少し時間がかかります．）

（注意：ウイルスバスターなどのウイルス対策ソフトを使っている場合は，この工程で失敗することがあります．一時的にオフにして作業をしてください．）

## 3. GPUの換装
グラフィックボードを換装（あるいは増設）します．必ず電源ケーブルを引き抜いた状態でおこなってください．PCI Express x16のスロットが2つある場合は，上にある方を優先して使います．

また，（おそらく）NVIDIAのグラフィックボードのドライバは共存できませんので，異なるバージョンのNVIDIAグラフィックボードは共存させないほうが良いと思います．（よく調べていませんので，work aroundがあるかもしれません．）

## 4. Ubuntuのインストール
まずはUbuntuをインストールしましょう．「Ubuntu クリーンインストール」等の検索ワードでたくさんの親切な記事がヒットしますので，ここでは簡潔に説明します．

### 4.1  ブートドライブの順番を変更
事前に作成しておいたライブUSBメモリをPCに挿した状態で，F2を押しながら起動し，UEFIのセッティング画面に入ります．（UEFIによって異なるボタンが割り当てられていることがあります．）

ブートドライブの順番を，USBドライブを一番上に移動し，変更を保存後再起動します．

### 4.2  Ubuntuのインストール
うまくいって入れば，再起動後にUbuntuのブートローダ（GRUB）が起動します．

Try ubuntu without installを選択し，後は順序通り設定を進めてインストールします．クリーンインストールの場合，特に詰まる部分もないと思います．

## 5.  NVIDIAドライバのインストール
*ここから先は構築するマシンがインターネットに接続されていることが必要です．*

「4. Ubuntuのインストール」まででUbuntuがインストールされた状態になりますが，このままでは深層学習にGPUを活用することができません．

デフォルトの状態だと，NVIDIAの純正ドライバをリバースエンジニアリングして作成されたnouveauというドライバが動いています．通常の使用だとこれでも問題ないのですが，今回はCUDAを使うためにNVIDIAのプロプライエタリなドライバをインストールします．

ただ問題なのが，Ubuntu 16 ではUEFIでセキュアブートが有効になっているとカーネルレベルでも署名がないドライバのロードができないようになっているため，インストールに失敗することがあります．そこで，カーネルレベルで署名なしのドライバがインストールできるようにするか，セキュアブートそのものをオフにしてしまうことによってインストールできるようにします．

ここでは，本当はセキュリティ的に問題があるのですが，セキュアブートをオフにすることにします．前者の方法は[Qiitaの記事](https://www.amazon.co.jp/NVIDIA-Pascal-GPUアーキテクチャー採用グラフィックカード-TITAN-X/dp/B01M0C8QXE)にまとめている方がいらっしゃるので，そちらを参考にしてください．

ちなみに，nouveauをオフにしないとnvidiaドライバのインストールに失敗するという報告（例えば，[Qiitaの記事](http://qiita.com/ttomoaki/items/5a180f61647750eb8d70)）がありますが，自分がやった時は明示的にオフにしなくても大丈夫でした．上の記事の方も，nouveauをオフにしなくてもうまくいったと報告しています．

（別件でUbuntu 14でも同様にnvidiaのドライバをインストールしたのですが，こちらもUEFIでセキュアブートをオフにしないとX windowが起動しませんでした．）

### 5.1 UEFIのセキュアブートをオフにする

上述した通り，セキュリティレベルを下げてドライバがインストール可能な状態にします．

[Secure Boot]のようなメニューがありますので，[Enabled]から[Disabled]に変更します．

必ず変更を保存してから，再起動します．

### 5.2 ドライバのインストール

X windowが起動したままだとNVIDIAドライバのインストールできないので，ログイン画面でalt+ctrl+F1を押してコンソール画面（tty1）に切り替えます．

まず最初に，グラフィックドライバを提供しているubuntuリポジトリを登録します．

```
$ sudo add-apt-repository ppa:graphics-drivers/ppa
$ sudo apt-get update
```

[NVIDIA ドライバダウンロードページ](http://www.nvidia.co.jp/Download/index.aspx?lang=jp) で自分の環境に対応するNVIDIAドライバのバージョンを確認します．（インストール時点のバージョンは 375.39 ）

apt-cacheで検索するとより最新のドライバがヒットすることもありますが，ここでは確実に行くために上のURLで出てきたバージョンのドライバを使います．

```
$ sudo apt-get install nvidia-375
```

再起動します．

```
$ sudo reboot
```

ドライバが正しくインストールされたことを以下のコマンドで確認します．

```
$ nvidia-smi
```

すると，下記のようにドライバがインストールされていることが確認できます．

```
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 375.39 Driver Version: 375.39 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
|===============================+======================+======================|
| 0 TITAN X (Pascal) Off | 0000:02:00.0 On | N/A |
| 23% 30C P8 8W / 250W | 63MiB / 12186MiB | 0% Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes: GPU Memory |
| GPU PID Type Process name Usage |
|=============================================================================|
| 0 4733 G /usr/lib/xorg/Xorg 60MiB |
+-----------------------------------------------------------------------------+
```

以上で，NVIDIA ドライバのインストールが完了です．

## 6. Docker のインストール
[Docker 公式ドキュメント](https://docs.docker.com/engine/installation/linux/ubuntu/)が非常に親切にまとめられていて，この通りやるとまずつまずくことはないと思いますのでここでは割愛します．

やることはすぐ終わるのですが，少し手順が複雑なので公式ドキュメントを参照してください．上記のインストールが終わったら，Dockerを使いやすくするため，[Linuxインストール後のステップ](https://docs.docker.com/engine/installation/linux/linux-postinstall/)も行ってください．（この作業をすると，dockerのコマンドを叩く時に毎回sudoする必要が無くなり，起動時に自動的にdockerが走るように設定できます．）

## 7. NVIDIA-Dockerのインストール
このままだとdockerからGPUを活用することができませんので，DockerのプラグインであるNVIDIA-dockerを入れます．（2017/2/18時点でのコマンドですので，バージョンアップ等によって異なる場合があります．github:NVIDIA/nvidia-dockerを確認してください．）

### 7.1 パッケージのダウンロード

一度 docker daemonを再起動しておきましょう．

```
$ sudo service docker restart
```

続いてパッケージをダウンロードします．

```
$ wget -P /tmp https://github.com/NVIDIA/nvidia-docker/releases/download/v1.0.0/nvidia-docker_1.0.0-1_amd64.deb
```

セキュリティの設定によっては，証明書関連でエラーが出ることがありますので，その際はかわりに以下を実行してください．

```
$ wget --no-check-certificate -P /tmp https://github.com/NVIDIA/nvidia-docker/releases/download/v1.0.0/nvidia-docker_1.0.0-1_amd64.deb
```

（なぜかwgetの際にホスト名が解決できないというエラーが出たのですが，上のコマンドを何度も叩いているうちにダウンロードできました．奥が深いです．）

### 7.2 パッケージのインストール
パッケージをインストールします．

```
$ sudo dpkg -i /tmp/nvidia-docker*.deb && rm /tmp/nvidia-docker*.deb
```

### 7.3 動作確認
NVIDIA ドライバをインストールした時と同様に，nvidia-smiで動作確認してみましょう．

```
$ nvidia-docker run --rm nvidia/cuda nvidia-smi
```

実行した結果，以下のようにプロファイルが表示されればインストール成功です．

```
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 375.39 Driver Version: 375.39 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
|===============================+======================+======================|
| 0 TITAN X (Pascal) Off | 0000:02:00.0 On | N/A |
| 23% 28C P8 9W / 250W | 63MiB / 12187MiB | 0% Default |
+-------------------------------+----------------------+----------------------+

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

## 8. Tensorflowを動かしてみる．
やっとDocker上でGPUを使うことができるようになりました．後は好きなようにDockerイメージを作ってrunさせるだけです．

ここでは，TensorFlowの開発チームが提供しているビルド済みイメージがありますので，それを使ってTensorFlowを動かしてみます．

```
$ nvidia-docker run -it -p 8888:8888 gcr.io/tensorflow/tensorflow:latest-gpu
```

すると，

```
gcr.io/tensorflow/tensorflow:latest-gpu
[I 01:46:58.799 NotebookApp] Writing notebook server cookie secret to /root/.local/share/jupyter/runtime/notebook_cookie_secret
[W 01:46:58.815 NotebookApp] WARNING: The notebook server is listening on all IP addresses and not using encryption. This is not recommended.
[I 01:46:58.819 NotebookApp] Serving notebooks from local directory: /notebooks
[I 01:46:58.819 NotebookApp] 0 active kernels
[I 01:46:58.819 NotebookApp] The Jupyter Notebook is running at: http://[all ip addresses on your system]:8888/?token=********************************
[I 01:46:58.819 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
[C 01:46:58.819 NotebookApp]

Copy/paste this URL into your browser when you connect for the first time,
 to login with a token:
 http://localhost:8888/?token=********************************
```

となり，jupyter server が起動したことが確認できます．

jupyterの初回起動時には認証が要求されるようなので，ここで出て来たURLをクリックするかブラウザにコピペすると，jupyter notebookが起動できます．

あらかじめリポジトリで用意してくれているtensorflowのチュートリアルがありますので，それをいじってみてtensorflowがきちんと動いていることを確認して見るとよいでしょう．

## 9. おわりに
ここまでで，Ubuntu上にインストールしたdockerからtensorflowのイメージをrunさせる方法までを紹介しました．

実際に運用するとなると，複数人で共用できるサーバーとして使いたくなることがあると思います．しかし，tensorflowが配布しているビルド済みイメージはjupyterのポートを8888に設定していますので，Dockerホスト場で複数コンテナ動かそうとすると，ポートがコンフリクトしてうまくいきません．

他にも運用していく上で不便なことが出てくると思いますので，後日別の記事でこの計算機を共用の機械学習用サーバーに進化させる方法を紹介します．