CUBE SUGAR CONTAINER

技術系のこと書きます。

Python: Keras/TensorFlow の学習を GPU で高速化する (Ubuntu 16.04 LTS)

以前、このブログで Keras/TensorFlow の学習スピードを GPU を使って速くする記事を書いた。 ただし、このとき使った OS は Mac OS X (macOS Sierra) だった。

blog.amedama.jp

とはいえ NVIDIA の dGPU を積んだ Mac がどれだけあるんだというと、正直なかなか無いと思う。 実際にやってみるとしたら Linux だよねということで、今回は Ubuntu 16.04 LTS を使う場合について書く。

インストールの手順については次の公式ドキュメントをベースに進める。

Installing TensorFlow on Ubuntu  |  TensorFlow

環境について

今回使った OS のバージョンなどは次の通り。

$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.2 LTS"
$ uname -r
4.4.0-66-generic

また、学習に GPU を使う場合はハードウェアの環境にも制約があるので、ここで記述しておく。 まず、当たり前だけど大前提として CUDA をサポートしている NVIDIA のグラフィックカードが必ず必要になる。 ちなみに TensorFlow のドキュメントを見ると一部 Radeon も対応しているらしいことが書かれていたけど、試してはいない。

さらに、NVIDIA のグラフィックカードなら何でも良いのかというと、そういうわけでもない。 次の NVIDIA 公式ページを見てほしい。 ここには NVIDIA の各 GPU ごとの Compute Capability という数値が載っている。

CUDA GPUs | NVIDIA Developer

Compute Capability という字面だけを見ると、性能を表す数値のような印象を受けるけど、そうではない。 数値はパフォーマンスとは関係がなく、あくまで GPU の世代というか載っている機能のバージョンらしい。 問題は、この数値が少なくとも 3.0 以上ないと現行の CUDA 8.0 がサポートしていない、という点だ。

また、PyPI で配布されている TensorFlow の GPU 版バイナリパッケージは CUDA 8.0 でビルドされている。 なので、もちろん動かす環境にも CUDA 8.0 が必要になる。 以上のことから Compute Capability が 3.0 以上の GPU を用意すべきだ。

ちなみに、ドキュメントを見ると一応 TensorFlow は今のところ CUDA 7.5 もサポートしているらしい。 CUDA 7.5 であれば Compute Capability は 2.0 以上をサポートしている。 ただし、そのときは自分でソースコードからビルドしなければいけない。 それについて、この記事では扱わない。

今回 GPU には GeForce GTX 1050 Ti を用意した。 これの Compute Capability は 6.1 となっている。 具体的なグラフィックカードは次のもの。

MSI GeForce GTX 1050 Ti 4G OC グラフィックスボード VD6191

MSI GeForce GTX 1050 Ti 4G OC グラフィックスボード VD6191

GPU のチョイスについては、現時点 (2017/3) で最もコストパフォーマンスが高くなるものを選んだ。 PassMark というベンチマークのスコアを値段で割った値を考えると GeForce GTX 1050 Ti が一番高くなる。 あとは、CPU に比べると GPU の学習速度はそれこそ一桁か、下手すると二桁は違ってくる。 つまり、GPU が載っていること自体がまずは重要で、性能はその次かなという感じ。 ようするに 60 秒かかった処理が 6 秒になるのは大きく違うけど、6 秒が 3 秒になってどれだけ嬉しいか?っていう。 それもあってハイエンドモデルではなくエントリーモデルを選ぶことにした。

グラフィックカードを刺して GPU を Ubuntu 16.04 LTS が認識することを確認しておこう。

$ lspci | grep -i nvidia
01:00.0 VGA compatible controller: NVIDIA Corporation Device 1c82 (rev a1)
01:00.1 Audio device: NVIDIA Corporation Device 0fb9 (rev a1)

CUDA Toolkit をインストールする

インストールの第一歩として CUDA Toolkit をインストールする。 そのために、まずは NVIDIA 公式サイトを参照する。

developer.nvidia.com

インストール方法は色々とあるけど、ここでは CUDA のリモートリポジトリを APT に登録するやり方を取る。 これなら、後からマイナーアップデートがあったときにも自動で更新できる。 上記ウェブサイトで、次のように操作して deb ファイルのダウンロード URL を取得しよう。

Linux > x86_64 > Ubuntu > 16.04 > deb (network)

上記で得られた URL から deb ファイルをダウンロードしてくる。

$ wget http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/x86_64/cuda-repo-ubuntu1604_8.0.61-1_amd64.deb

deb ファイルをインストールする。 これで CUDA のリポジトリが APT パッケージシステムに登録される。

$ sudo dpkg -i cuda-repo-ubuntu1604_8.0.61-1_amd64.deb
$ sudo apt-get update

これで APT 経由で CUDA をインストールできるようになる。 ちなみに、インストールにはとても長い時間がかかるので覚悟しておこう。 先ほどのインストール方法でローカルインストールを選んだ場合には 1.9GB のファイルを落とすことになる。 かかる時間は、推して知るべし。

$ sudo apt-get -y install cuda

上記が終わると、依存パッケージとして一緒に NVIDIA のグラフィックドライバなんかも入る。

$ dpkg -l | grep nvidia-[0-9]
ii  nvidia-375                            375.26-0ubuntu1                          amd64        NVIDIA binary driver - version 375.26
ii  nvidia-375-dev                        375.26-0ubuntu1                          amd64        NVIDIA binary Xorg driver development files

cuDNN をインストールする

次に cuDNN をインストールする。

cuDNN のインストールには、あらかじめ NVIDIA のサイトでデベロッパー登録が必要になる。 登録を済ませると cuDNN をダウンロードできるようになる。

https://developer.nvidia.com/rdp/cudnn-download

cuDNN の現行のバージョンは 5.1 となっている。 ダウンロードに使うパッケージは CUDA Toolkit のバージョンと組み合わせになっている。 今回であれば v5.1 + CUDA 8.0 のパッケージを選ぼう。

Download cuDNN v5.1 (Jan 20, 2017), for CUDA 8.0 > cuDNN v5.1 Library for Linux

ダウンロードしてきたら、それを CUDA のインストールされたディレクトリに放り込む。

$ sudo tar xvf cudnn-8.0-linux-x64-v5.1.tgz -C /usr/local
cuda/include/cudnn.h
cuda/lib64/libcudnn.so
cuda/lib64/libcudnn.so.5
cuda/lib64/libcudnn.so.5.1.10
cuda/lib64/libcudnn_static.a

libcupti-dev をインストールする

次に The CUDA Profiling Tools Interface もインストールしておく。

$ sudo apt-get -y install libcupti-dev

ここまで終わったら、一旦再起動しておこう。

$ sudo shutdown -r now

Keras/TensorFlow (GPU)

さて、次はいよいよ Keras/TensorFlow をインストールする…と言いたいところだけど、その前に。 システムの Python 実行環境を汚したくないので Python の仮想環境を作れるようにしよう。

ここでは virtualenvwrapper を使う。

$ sudo apt-get -y install virtualenvwrapper

シェルを起動し直す。

$ exec $SHELL

これで Python 仮想環境が作れるようになった。 tensorflow-with-gpu という名前で Python3 の仮想環境を作る。

$ mkvirtualenv tensorflow-with-gpu -p $(which python3)

これで最低限のパッケージが入った、システムから独立した仮想環境ができた。

(tensorflow-with-gpu) $ pip list --format=columns
Package       Version
------------- --------
appdirs       1.4.3
packaging     16.8
pip           9.0.1
pkg-resources 0.0.0
pyparsing     2.2.0
setuptools    34.3.2
six           1.10.0
wheel         0.30.0a0

ここに Keras と GPU 版 TensorFlow をインストールする。

(tensorflow-with-gpu) $ pip install keras tensorflow-gpu

インストールすると、こんな感じ。

(tensorflow-with-gpu) $ pip list --format=columns | grep -i -e keras -e tensorflow
Keras          1.2.2
tensorflow-gpu 1.0.1

ちなみに、以降はシェルのプレフィックスを表記しないけど Python 仮想環境上で実行し続けている。

GPU で学習させる

まずはベンチマーク用のアプリケーションで使うデータセット (MNIST) をダウンロードしよう。 Python の REPL を起動する。

$ python

Keras の mnist パッケージをインポートする。

>>> from keras.datasets import mnist
Using TensorFlow backend.
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcublas.so.8.0 locally
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcudnn.so.5 locally
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcufft.so.8.0 locally
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcuda.so.1 locally
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcurand.so.8.0 locally

load_data() 関数でデータセットのダウンロードが始まる。 これには少し時間がかかる。

>>> mnist.load_data()
Downloading data from https://s3.amazonaws.com/img-datasets/mnist.pkl.gz
...
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0]]], dtype=uint8), array([7, 2, 1, ..., 4, 5, 6], dtype=uint8)))

終わったら REPL から抜ける。

>>> exit()

続いて学習のベンチマークに使うアプリケーションをダウンロードしてくる。

$ curl -O https://raw.githubusercontent.com/fchollet/keras/master/examples/mnist_cnn.py
$ echo 'K.clear_session()' >> mnist_cnn.py

ベンチマーク用のアプリケーションを実行する。 これは MNIST データセットを CNN で認識するものになっている。 初めての実行だと GPU の環境を整えるので学習を始まるまで時間がかかるかも。

$ time python mnist_cnn.py
Using TensorFlow backend.
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcublas.so.8.0 locally
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcudnn.so.5 locally
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcufft.so.8.0 locally
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcuda.so.1 locally
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcurand.so.8.0 locally
X_train shape: (60000, 28, 28, 1)
60000 train samples
10000 test samples
Train on 60000 samples, validate on 10000 samples
Epoch 1/12
W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE3 instructions, but these are available on your machine and could speed up CPU computations.
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.
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.
I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:910] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
I tensorflow/core/common_runtime/gpu/gpu_device.cc:885] Found device 0 with properties:
name: GeForce GTX 1050 Ti
major: 6 minor: 1 memoryClockRate (GHz) 1.455
pciBusID 0000:01:00.0
Total memory: 3.94GiB
Free memory: 3.84GiB
I tensorflow/core/common_runtime/gpu/gpu_device.cc:906] DMA: 0
I tensorflow/core/common_runtime/gpu/gpu_device.cc:916] 0:   Y
I tensorflow/core/common_runtime/gpu/gpu_device.cc:975] Creating TensorFlow device (/gpu:0) -> (device: 0, name: GeForce GTX 1050 Ti, pci bus id: 0000:01:00.0)
60000/60000 [==============================] - 8s - loss: 0.3661 - acc: 0.8871 - val_loss: 0.0864 - val_acc: 0.9726
Epoch 2/12
60000/60000 [==============================] - 6s - loss: 0.1337 - acc: 0.9604 - val_loss: 0.0613 - val_acc: 0.9806
Epoch 3/12
60000/60000 [==============================] - 6s - loss: 0.1046 - acc: 0.9694 - val_loss: 0.0524 - val_acc: 0.9838
Epoch 4/12
60000/60000 [==============================] - 6s - loss: 0.0879 - acc: 0.9737 - val_loss: 0.0428 - val_acc: 0.9858
Epoch 5/12
60000/60000 [==============================] - 6s - loss: 0.0755 - acc: 0.9773 - val_loss: 0.0393 - val_acc: 0.9870
Epoch 6/12
60000/60000 [==============================] - 6s - loss: 0.0683 - acc: 0.9801 - val_loss: 0.0368 - val_acc: 0.9875
Epoch 7/12
60000/60000 [==============================] - 6s - loss: 0.0648 - acc: 0.9806 - val_loss: 0.0367 - val_acc: 0.9888
Epoch 8/12
60000/60000 [==============================] - 6s - loss: 0.0593 - acc: 0.9820 - val_loss: 0.0353 - val_acc: 0.9889
Epoch 9/12
60000/60000 [==============================] - 6s - loss: 0.0548 - acc: 0.9833 - val_loss: 0.0328 - val_acc: 0.9890
Epoch 10/12
60000/60000 [==============================] - 6s - loss: 0.0529 - acc: 0.9845 - val_loss: 0.0316 - val_acc: 0.9897
Epoch 11/12
60000/60000 [==============================] - 6s - loss: 0.0508 - acc: 0.9848 - val_loss: 0.0308 - val_acc: 0.9902
Epoch 12/12
60000/60000 [==============================] - 6s - loss: 0.0484 - acc: 0.9852 - val_loss: 0.0298 - val_acc: 0.9899
Test score: 0.0297728348608
Test accuracy: 0.9899

real  1m28.273s
user  1m39.684s
sys   0m8.740s

全体の実行が一分半ほどで終わった。 一回のエポックは大体 6 秒ほどで終わっている。

ちなみに GPU の仕事っぷりは nvidia-smi コマンドから見られる。

$ nvidia-smi
Sun Mar 12 20:24:56 2017
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 375.26                 Driver Version: 375.26                    |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce GTX 105...  Off  | 0000:01:00.0      On |                  N/A |
| 35%   38C    P0    58W /  75W |   3862MiB /  4037MiB |     77%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|    0      1510    G   /usr/lib/xorg/Xorg                              59MiB |
|    0      3374    C   python                                        3799MiB |
+-----------------------------------------------------------------------------+

毎秒ごとに表示を更新したいときは、こんな感じで。

$ nvidia-smi -l 1

CPU で学習させる

ちなみに対比として CPU を使ったときの例も載せておく。

使った CPU は次の通り。 第一世代 Core i7 なので、なかなか古い。 現行世代なら Core i3 くらいの性能らしい。

$ cat /proc/cpuinfo | grep -i name | head -n 1
model name      : Intel(R) Core(TM) i7 CPU         860  @ 2.80GHz
$ cat /proc/cpuinfo | grep -i name | wc -l
8

余談ながら、当然チップセットやマザーボードも古いので今回使ったグラフィックカードを認識するかドキドキした。

先ほどインストールした GPU 版 TensorFlow をアンインストールして CPU 版 TensorFlow をインストールする。

$ pip uninstall -y tensorflow-gpu
$ pip install tensorflow

先ほどと同じようにベンチマーク用のアプリケーションを実行する。

$ time python mnist_cnn.py
Using TensorFlow backend.
X_train shape: (60000, 28, 28, 1)
60000 train samples
10000 test samples
Train on 60000 samples, validate on 10000 samples
Epoch 1/12
W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE3 instructions, but these are available on your machine and could speed up CPU computations.
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.
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.
60000/60000 [==============================] - 117s - loss: 0.3733 - acc: 0.8858 - val_loss: 0.0885 - val_acc: 0.9723
Epoch 2/12
60000/60000 [==============================] - 117s - loss: 0.1347 - acc: 0.9596 - val_loss: 0.0635 - val_acc: 0.9799
Epoch 3/12
60000/60000 [==============================] - 115s - loss: 0.1031 - acc: 0.9693 - val_loss: 0.0565 - val_acc: 0.9818
Epoch 4/12
60000/60000 [==============================] - 115s - loss: 0.0887 - acc: 0.9740 - val_loss: 0.0448 - val_acc: 0.9852
Epoch 5/12
60000/60000 [==============================] - 114s - loss: 0.0780 - acc: 0.9773 - val_loss: 0.0415 - val_acc: 0.9868
Epoch 6/12
60000/60000 [==============================] - 113s - loss: 0.0707 - acc: 0.9788 - val_loss: 0.0376 - val_acc: 0.9870
Epoch 7/12
60000/60000 [==============================] - 112s - loss: 0.0651 - acc: 0.9808 - val_loss: 0.0352 - val_acc: 0.9893
Epoch 8/12
60000/60000 [==============================] - 112s - loss: 0.0604 - acc: 0.9818 - val_loss: 0.0352 - val_acc: 0.9888
Epoch 9/12
60000/60000 [==============================] - 112s - loss: 0.0555 - acc: 0.9840 - val_loss: 0.0332 - val_acc: 0.9884
Epoch 10/12
60000/60000 [==============================] - 112s - loss: 0.0542 - acc: 0.9835 - val_loss: 0.0322 - val_acc: 0.9895
Epoch 11/12
60000/60000 [==============================] - 111s - loss: 0.0495 - acc: 0.9853 - val_loss: 0.0319 - val_acc: 0.9897
Epoch 12/12
60000/60000 [==============================] - 110s - loss: 0.0477 - acc: 0.9857 - val_loss: 0.0326 - val_acc: 0.9893
Test score: 0.032642004689
Test accuracy: 0.9893

real  22m54.715s
user  123m6.744s
sys   46m53.296s

今度は 23 分ほどかかった。

GPU が 1 分 28 秒で終わったのに対して CPU では 22 分 54 秒かかっている。 つまり、GPU を使うことで学習が 15.6 倍高速化できた。 一回のエポック当たりでは 19.1 倍速くなっている。

まとめ

今回は OS として Ubuntu 16.04 LTS を使って Keras/TensorFlow の学習を GPU で高速化してみた。 CUDA がサポートしている GPU は Compute Capability という数値で表現されている。 この数値が大きいものほど、新しい世代のものになっている。 使う GPU については NVIDIA で、少なくとも Compute Capability が 3.0 以上のものを選ぼう。 もちろん、将来の CUDA で最小要件の Compute Capability は上がっていくはずなので、なるべく大きい方が良い。

いじょう。

MSI GeForce GTX 1050 Ti 4G OC グラフィックスボード VD6191

MSI GeForce GTX 1050 Ti 4G OC グラフィックスボード VD6191