趣味でディープラーニングで遊ぶために GPU マシンを使いたい。
GPU は本当にすごくて、自分の MacBook Air で 2 時間かかるような学習が GPU を使うと 5 分足らずで終わる。CPU だけでディープラーニングするのは馬鹿馬鹿しいという気持ちになる。
しかし自宅に GPU マシンを組んだとしても四六時中計算し続けるわけでもないからもったいないし、ここはクラウドサービスを活用して安く済ませたい。
さらに、最近では Docker コンテナ内から GPU を利用することができる NVIDIA Docker という Docker プラグインがあり、これを利用することで GPU マシンの環境を汚すことなく好きなだけ学習環境を立ち上げることができる。
今回は Amazon EC2 の GPU インスタンスと NVIDIA Docker を使って趣味用のディープラーニング環境を作ったので、その作り方について書いていく。
書かないこと
以下の事項は詳しく説明しないので、各自調べて欲しい。
- AWS, Amazon EC2 の使い方
- Linux の基本操作
- Docker の基本操作
- ディープラーニングとその関連ツールについて
インスタンスのスペック
今回は AWS を使ったが、GCP1 や Azure2 にも GPU インスタンスはあるので好きなのを使えばいいと思う。
今回利用するインスタンスのスペックは以下の通り。
- AMI: Ubuntu Server 16.04 LTS3
- インスタンスタイプ: p2.xlarge
- GPU x 1 (NVIDIA Tesla K804)
- $1.542/h (東京リージョンの場合)
- ストレージ: SSD (gp2) x 16GB
- Docker イメージを扱うので 8GB だとおそらく足りない
AWS の GPU インスタンスには現在 G2 と P2 の二種類があり、G2 はグラフィック用途向け / P2 は汎用計算向け となっている。今回はディープラーニングに使いたいので P2 インスタンスの中で一番安い p2.xlarge を利用する。
GPU インスタンスは総じて料金が高く、p2.xlarge を起動しっぱなしにすると一ヶ月で10万円以上にもなってしまうので、使うときだけ起動して使い終わったら停止するのを忘れないように気をつけたほうがよい。
ドライバインストール済み AMI もある、が
GPU ドライバインストール済み AMI が NVIDIA 公式から提供されている5のでこちらを利用してもいいかもしれない。
しかし現時点では Amazon Linux / Ubuntu 14.04 / Windows Server という選択肢しかなく、今回は Ubuntu 16.04 が使いたかったため普通の Ubuntu AMI にドライバを入れて利用することにした。
Docker をインストールする
Ubuntu への Docker のインストール方法は公式ドキュメントにまとまっているので割愛する。
Get Docker for Ubuntu | Docker Documentation
作業ユーザを docker グループに追加する
Docker のソケットファイルにアクセスするためには root もしくは docker グループのユーザである必要があるので、作業ユーザを docker グループに追加する。(ログインし直すと反映される)
# usermod -aG docker $USER
自動起動設定 & 起動
Systemd のいつものやつ。
# systemctl enable docker
# systemctl start docker
GPU のドライバをインストールする
NVIDIA の公式サイトからドライバを検索できる。6
NVIDIA DRIVERS Tesla Driver for Ubuntu 16.04
AWS の P2 インスタンスで使われている Tesla K80 の Ubuntu 16.04 用のドライバは 375 というやつっぽい。
そのままこれをダウンロードしてインストールしてもいいが、Ubuntu の場合は apt からでもインストールすることができる。
# apt install nvidia-375
インストールできたかどうか確認する
nvidia-smi
コマンド7を使って確認できる。
$ nvidia-smi
Wed Jun 7 01:35:15 2017
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 375.66 Driver Version: 375.66 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
|===============================+======================+======================|
| 0 Tesla K80 Off | 0000:00:1E.0 Off | 0 |
| N/A 33C P8 32W / 149W | 0MiB / 11439MiB | 0% Default |
+-------------------------------+----------------------+----------------------+
+-----------------------------------------------------------------------------+
| Processes: GPU Memory |
| GPU PID Type Process name Usage |
|=============================================================================|
| No running processes found |
+-----------------------------------------------------------------------------+
Tesla K80
がひとつ認識できているので、無事ドライバをインストールできたと思われる。
NVIDIA Docker をインストールする
NVIDIA Docker の概要については、リポジトリの README にある画像を見るとなんとなくわかる。
NVIDIA/nvidia-docker: Build and run Docker containers leveraging NVIDIA GPUs
Installation · NVIDIA/nvidia-docker Wiki を読むと nvidia-modprobe が必要とあるので、まずはこれをインストールする。8
# apt install nvidia-modprobe
続いて NVIDIA Docker 本体のインストール。
バイナリからインストールする方法とソースをビルドする方法があるらしいが、Ubuntu 16.04 ではバイナリが利用できそうなのでバイナリからインストールする。
$ wget https://github.com/NVIDIA/nvidia-docker/releases/download/v1.0.1/nvidia-docker_1.0.1-1_amd64.deb
# dpkg -i nvidia-docker_1.0.1-1_amd64.deb
コンテナ内から GPU を使ってディープラーニングしてみる
以上までで GPU インスタンスへの NVIDIA Docker の導入が完了したので、ここからは実際にコンテナを立てて利用してみる。
今回は Keras (バックエンドは TensorFlow) が動かせるイメージを作成し、そのイメージのコンテナ内で Keras のサンプルコードを実行する。
Keras が使えるイメージをビルドする
以下の Dockerfile を作成してビルドする。
FROM nvidia/cuda:8.0-cudnn6-runtime
RUN apt update && apt install -y python3-pip
RUN pip3 install tensorflow-gpu keras
$ docker build . -t keras-tensorflow-gpu
[2017.10.03 追記] TensorFlow 1.3 では cuDNN 6 に対応したようなので Dockerfile も 8.0-cudnn6-runtime を使うように修正した。
ちなみに nvidia/cuda
イメージを使わずに自前で cuDNN をインストールしようとすると、パッケージをダウンロードするために NVIDIA Developer Program のメンバー登録 (無料) が必要になる9ので、cuDNN 入りの nvidia/cuda
イメージを使うと少しだけ楽。
ビルドしたイメージでコンテナを起動する
通常の docker
コマンドではなく nvidia-docker
コマンドを使って起動する。10
コマンドのオプションは通常の docker
コマンドと変わらない。
$ nvidia-docker run -it keras-tensorflow-gpu
実行コマンドを省略しているが、省略すると(このイメージの場合は) bash が実行されるので、Bash シェルでコンテナにログインすることができる。
以下、(container)#
はコンテナにログインした状態で実行したコマンドという意味で使う。
(container)# nvidia-smi
を実行すると、ホストと同じようにコンテナ内でも GPU が認識されていることがわかる。
Keras のサンプルコードを実行する
MNIST データセットを CNN で学習させる Keras のサンプルコード11を wget で落としてきて実行してみる。
(container)# apt install wget
(container)# wget https://raw.githubusercontent.com/fchollet/keras/master/examples/mnist_cnn.py
(container)# python3 mnist_cnn.py
Using TensorFlow backend.
Downloading data from https://s3.amazonaws.com/img-datasets/mnist.npz
x_train shape: (60000, 28, 28, 1)
60000 train samples
10000 test samples
Train on 60000 samples, validate on 10000 samples
Epoch 1/12
2017-06-07 08:21:24.737577: W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE4.1 instructions, but these are available on your machine and could speed up CPU computations.
2017-06-07 08:21:24.737613: W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE4.2 instructions, but these are available on your machine and could speed up CPU computations.
2017-06-07 08:21:24.737627: W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use AVX instructions, but these are available on your machine and could speed up CPU computations.
2017-06-07 08:21:24.737635: W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use AVX2 instructions, but these are available on your machine and could speed up CPU computations.
2017-06-07 08:21:24.737648: W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use FMA instructions, but these are available on your machine and could speed up CPU computations.
2017-06-07 08:21:24.923105: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:901] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2017-06-07 08:21:24.923615: I tensorflow/core/common_runtime/gpu/gpu_device.cc:887] Found device 0 with properties:
name: Tesla K80
major: 3 minor: 7 memoryClockRate (GHz) 0.8235
pciBusID 0000:00:1e.0
Total memory: 11.17GiB
Free memory: 11.11GiB
2017-06-07 08:21:24.923646: I tensorflow/core/common_runtime/gpu/gpu_device.cc:908] DMA: 0
2017-06-07 08:21:24.923660: I tensorflow/core/common_runtime/gpu/gpu_device.cc:918] 0: Y
2017-06-07 08:21:24.923677: I tensorflow/core/common_runtime/gpu/gpu_device.cc:977] Creating TensorFlow device (/gpu:0) -> (device: 0, name: Tesla K80, pci bus id: 0000:00:1e.0)
60000/60000 [==============================] - 11s - loss: 0.3182 - acc: 0.9025 - val_loss: 0.0727 - val_acc: 0.9770
Epoch 2/12
60000/60000 [==============================] - 9s - loss: 0.1139 - acc: 0.9662 - val_loss: 0.0516 - val_acc: 0.9830
Epoch 3/12
60000/60000 [==============================] - 9s - loss: 0.0877 - acc: 0.9743 - val_loss: 0.0474 - val_acc: 0.9849
Epoch 4/12
60000/60000 [==============================] - 9s - loss: 0.0717 - acc: 0.9784 - val_loss: 0.0424 - val_acc: 0.9852
Epoch 5/12
60000/60000 [==============================] - 9s - loss: 0.0625 - acc: 0.9814 - val_loss: 0.0368 - val_acc: 0.9875
Epoch 6/12
60000/60000 [==============================] - 9s - loss: 0.0572 - acc: 0.9825 - val_loss: 0.0347 - val_acc: 0.9873
Epoch 7/12
60000/60000 [==============================] - 9s - loss: 0.0511 - acc: 0.9850 - val_loss: 0.0340 - val_acc: 0.9876
Epoch 8/12
60000/60000 [==============================] - 9s - loss: 0.0473 - acc: 0.9860 - val_loss: 0.0312 - val_acc: 0.9881
Epoch 9/12
60000/60000 [==============================] - 9s - loss: 0.0441 - acc: 0.9871 - val_loss: 0.0324 - val_acc: 0.9889
Epoch 10/12
60000/60000 [==============================] - 9s - loss: 0.0425 - acc: 0.9872 - val_loss: 0.0302 - val_acc: 0.9890
Epoch 11/12
60000/60000 [==============================] - 9s - loss: 0.0382 - acc: 0.9886 - val_loss: 0.0282 - val_acc: 0.9891
Epoch 12/12
60000/60000 [==============================] - 9s - loss: 0.0376 - acc: 0.9884 - val_loss: 0.0269 - val_acc: 0.9906
Test loss: 0.0268516085245
Test accuracy: 0.9906
手元の Mac では 1 epoch あたり 500 秒以上かかっていたのが 9 秒で終わっていて GPU の威力を感じる。
コスト感
土日のどちらかをディープラーニングで遊んだとして、
8時間 × 4日 × $1.542/h = $49.344/mon
p2.xlarge インスタンスの料金がこれくらいで、加えて EBS (SSD) 16GB が約 $2 かかるので、ひと月で $50 くらい。
まぁ趣味としては全然高くないんじゃないだろうか。
GPU を使ってもなお計算に一週間かかる、みたいなことをやるんだったら自前で GPU マシンを組んだほうが良いかもしれない。
まとめ
- GPU があると快適にディープラーニングで遊べる
- GPU 付きのクラウドサーバを必要なときだけ立ち上げるようにすれば低コストで遊べる
- NVIDIA Docker を使えば環境が汚れるのを気にせずに好き放題遊べる
- 遊んだあとはインスタンス止めるの忘れないで!!!
-
Azure N-Series: General availability on December 1 | Blog | Microsoft Azure ↩
-
個人的な印象だが、ディープラーニングでは Ubuntu で動かすのがデフォというツールが多い気がしていて、特に理由がなければ Amazon Linux とかよりも Ubuntu を使ったほうがハマらなさそう ↩
-
この NVIDIA Tesla K80 という GPU は普通に買うと 70万円以上するのでこの価格帯で利用できるのすごい ↩
-
nvidia-smi
コマンドは NVIDIA デバイスの管理インタフェースとのこと: nvidia-smiコマンドの詳細ついて - Qiita ↩ -
ドライバを apt からではなく公式サイトからダウンロードしてインストールした場合は、既に nvidia-modprobe が入っているかもしれない。 ↩
-
NVIDIA Docker がやっている事としては、ホスト OS の GPU 情報を収集して、ホストのドライバをコンテナ側にマウントするようなオプションを追加して
docker run
コマンドに流している、ということらしい: nvidia-dockerは何をしているのか - Qiita ↩