NVIDIA Dockerで簡単にGPU対応のTensorFlow入りコンテナを作る方法

2016年5月8日日曜日

こんにちは。
小説家になろうの作品群を読んでいたら連休が終わりそうです。
連休の終わりのアンミラ帰りに衝撃作TensorFlowでGPUが使えないとそのアンサーエントリであるTensorFlow on DockerでGPUを使えるようにする方法を読み、書いてみたエントリです。先月購入した最低限Maxwell的なグラフィックカード(図1)絡みで見つけたNVIDIA Docker、なかなか良いものなのでもっと多くの方に知ってもらいたいなと思い。
図1 NVIDIA GTX 750Ti搭載グラフィックカード
NVIDIA Docker + TensorFlowGPUNVIDIA Docker + CaffeUbuntu14.04.3nvidia-docker使CaffeCaffe*1TensorFlowNVIDIAcuDNN
[*1] このQiitaエントリが書かれたのは2016年1月で、NVIDIA Dockerがイマイチこなれていなかった頃です。当時と今では公式イメージのバリエーションやコマンド利用時の注意点などに若干差異がありますが原則は変わりません。

NVIDIA Docker

NVIDIA DockerNVIDIADockerDockerNVIDIA GPUDockerCUDA Toolkit(+cuDNN)CUDA Toolkit
NVIDIA DockercuDNNDockerNVIDIA(!)cuDNN

NVIDIA Docker



CUDA Toolkit

CUDA Toolkit(7.07.5)

CUDA Toolkit 6.57.5





NVIDIA GPUDocker



EULA()

EULADockerJava

Docker HubNVIDIA DockerNVIDIADocker HubEULA+(*2)



[*2] その派生イメージをDocker Hubへ突っ込んで良いかどうかは別問題かと思いますので、ここでは議論しません。issueを立てて開発チームへ確認すべき案件でしょう。

NVIDIA Docker





(2016/05/08)1.0.0-rc*3

GitHubissues



DockerNVIDIA Docker

docker-machinecomposeswarm使GPUGPU()

NVIDIA Docker(Docker使CUDA/cuDNN)



[*3] 個人的には、cuDNN 5のRCが取れて正式リリースされるタイミングでNVIDIA Dockerも1.0.0正式版をリリースするのだろうと踏んでいます。

NVIDIAUbuntu10



Ubuntu 14.04(Docker)

DockerNVIDIA GPU

DockerDocker(docker-engine)NVIDIA Docker

cuDNNTensorFlow()

GPU

MNISTTensorFlow+GPU(cuDNN)



Docker



: GeForce GTX 750Ti

NVIDIA: 361.42

OS: Ubuntu 14.04.4 LTS

Docker engine: 1.10.3-cs3

NVIDIA Docker

CUDA: 7.5

cuDNN: 4(4.0.7)

TensorFlow: 0.8.0(GPU)

Python: 2.7.6



Ubuntu 14.04(Docker)

Ubuntu 14.04
UNetbootinUSBHDDUbuntu

OSXNVIDIAXXGRUBbootNouveau*4
/etc/default/grub
GRUB_CMDLINE_LINUX_DEFAULT="text nouveau.modeset=0"
今年こそはUbuntuデスクトップ元年になるでしょうか。このタイミングであわせてOpenSSH Serverをインストールし、以後の作業はSSH経由でおこないます。
[*4] Nouveauドライバの無効化についてはAutodeskのヘルプページが分かりやすいので、困ったら参照してください。

Dockerホスト側へNVIDIAの最新ドライバをインストールする

前段階できちんとNouveauドライバを無効化しておけば、
$ sudo ./NVIDIA-Linux-x86_64-361.42.run
としていくつかの質問に答えるだけですんなりドライバのインストールが完了するはずです。

Dockerホスト側へDocker(docker-engine)とNVIDIA Dockerをインストールし、動作確認をする

Docker(docker-engine)をインストールする

NVIDIA DockerはDocker(docker-engine)のプラグインとして構築されているので、まずDocker本体をインストールする必要があります。
1.9.0以上が必要なので、今回はhttps://docs.docker.com/docker-trusted-registry/cs-engine/install/#install-on-ubuntu-14-04-ltsの手順に従ってインストールしました。
docker infoの結果は次の通りです。
$ docker info
Containers: 0
 Running: 0
 Paused: 0
 Stopped: 0
Images: 0
Server Version: 1.10.3-cs3
Storage Driver: aufs
 Root Dir: /var/lib/docker/aufs
 Backing Filesystem: extfs
 Dirs: 0
 Dirperm1 Supported: true
Execution Driver: native-0.2
Logging Driver: json-file
Plugins:
 Volume: local
 Network: bridge null host
Kernel Version: 4.2.0-27-generic
Operating System: Ubuntu 14.04.4 LTS
OSType: linux
Architecture: x86_64
CPUs: 4
Total Memory: 7.734 GiB
Name: muo-desktop
ID: 7MD3:N656:PHZ6:LROY:ML3K:J4OF:4RGW:YRV4:HCCK:YQI4:ZMSZ:SI5I
WARNING: No swap limit support

NVIDIA Dockerをインストールする

公式の手順通り次のコマンドにてインストールします。
$ wget -P /tmp https://github.com/NVIDIA/nvidia-docker/releases/download/v1.0.0-rc/nvidia-docker_1.0.0.rc-1_amd64.deb
$ sudo dpkg -i /tmp/nvidia-docker_1.0.0.rc-1_amd64.deb && rm /tmp/nvidia-docker*.deb
Docker(docker-engine)を正しくインストールできていない場合にはエラーが発生するかもしれません。
正常にインストールできれば、次のようなターミナル出力で終了するはずです。
Preparing to unpack .../nvidia-docker_1.0.0.rc-1_amd64.deb ...
nvidia-docker stop/waiting
Unpacking nvidia-docker (1.0.0~rc-1) over (1.0.0~rc-1) ...
nvidia-docker (1.0.0~rc-1) を設定しています ...
Adding system user `nvidia-docker' (UID 117) ...
Adding new user `nvidia-docker' (UID 117) with group `nogroup' ...
Not creating home directory `/var/lib/nvidia-docker'.
nvidia-docker start/running, process 18051
Processing triggers for ureadahead (0.100.0-16) ...
muo@muo-desktop:~$
このあたりで一旦ホストOSを再起動しておきます。

NVIDIA Dockerの簡単な動作確認をおこなう

これまた公式の手順通り
$ nvidia-docker run --rm nvidia/cuda nvidia-smi
としてみます。
$ nvidia-docker run --rm nvidia/cuda nvidia-smi
Sun May  8 07:00:24 2016
+------------------------------------------------------+
| NVIDIA-SMI 361.42     Driver Version: 361.42         |
|-------------------------------+----------------------+----------------------+
| 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 750 Ti  Off  | 0000:01:00.0     Off |                  N/A |
| 22%   33C    P8     1W /  38W |      7MiB /  2046MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+
GPUOK
*5

CUDA

deviceQuery

NVIDIA DockerNVIDIA DockerGPU
https://github.com/NVIDIA/nvidia-docker/blob/master/samples/ubuntu/deviceQuery/Dockerfile
Dockerfilenvidia/cuda(~/docker/deviceQuery/Dockerfile)
$ nvidia-docker build -t local:deviceQuery .
でビルドして
$ nvidia-docker run --rm local:deviceQuery
と実行すると、
./deviceQuery Starting...

 CUDA Device Query (Runtime API) version (CUDART static linking)

Detected 1 CUDA Capable device(s)

Device 0: "GeForce GTX 750 Ti"
  CUDA Driver Version / Runtime Version          8.0 / 7.5
  CUDA Capability Major/Minor version number:    5.0
  Total amount of global memory:                 2047 MBytes (2145927168 bytes)
  ( 5) Multiprocessors, (128) CUDA Cores/MP:     640 CUDA Cores
  GPU Max Clock rate:                            1084 MHz (1.08 GHz)
  Memory Clock rate:                             2700 Mhz
  Memory Bus Width:                              128-bit
  L2 Cache Size:                                 2097152 bytes
  Maximum Texture Dimension Size (x,y,z)         1D=(65536), 2D=(65536, 65536), 3D=(4096, 4096, 4096)
  Maximum Layered 1D Texture Size, (num) layers  1D=(16384), 2048 layers
  Maximum Layered 2D Texture Size, (num) layers  2D=(16384, 16384), 2048 layers
  Total amount of constant memory:               65536 bytes
  Total amount of shared memory per block:       49152 bytes
  Total number of registers available per block: 65536
  Warp size:                                     32
  Maximum number of threads per multiprocessor:  2048
  Maximum number of threads per block:           1024
  Max dimension size of a thread block (x,y,z): (1024, 1024, 64)
  Max dimension size of a grid size    (x,y,z): (2147483647, 65535, 65535)
  Maximum memory pitch:                          2147483647 bytes
  Texture alignment:                             512 bytes
  Concurrent copy and kernel execution:          Yes with 1 copy engine(s)
  Run time limit on kernels:                     No
  Integrated GPU sharing Host Memory:            No
  Support host page-locked memory mapping:       Yes
  Alignment requirement for Surfaces:            Yes
  Device has ECC support:                        Disabled
  Device supports Unified Addressing (UVA):      Yes
  Device PCI Domain ID / Bus ID / location ID:   0 / 1 / 0
  Compute Mode:
     < Default (multiple host threads can use ::cudaSetDevice() with device simultaneously) >

deviceQuery, CUDA Driver = CUDART, CUDA Driver Version = 8.0, CUDA Runtime Version = 7.5, NumDevs = 1, Device0 = GeForce GTX 750 Ti
Result = PASS
のような結果が出力されるはずです。手元のグラフィックカードによって結果は大きく異なるはずですが、最後にResult = PASSと出ているとなんだか安心しますね。

その他のサンプル動作確認

実は、前述のdeviceQueryで作成したイメージにはCUDAのサンプルが一式含まれます。
nbodyなどのサブディレクトリでmakeをおこなえばそれぞれ実行できるので、何か詰まった時には試してみると良いかもしれません。
[*5] NVIDIA DockerリポジトリのwikiにCUDA Toolkitバージョンごとの最低ドライババージョンとGPUアーキテクチャが記載されているので、参考にしてください。

cuDNN入りイメージをベースにTensorFlow込みのイメージ(カスタムイメージ)を作成する

お待ちかねの本編というところですが、とても簡単です。
FROM nvidia/cuda:cudnn

RUN apt-get update \
 && apt-get install -y --no-install-recommends python-pip python-dev \
 && pip install --upgrade https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow-0.8.0-cp27-none-linux_x86_64.whl \
 && rm -rf /var/lib/apt/lists/*
上記をDockerfile(今回は~/docker/tensorflow/Dockerfileにしました)へ保存して、Dockerfileのあるディレクトリにて
$ nvidia-docker build -t local:tf .
とコマンドを実行するだけです。
ビルド作業の途中でライブラリのビルドが走るため少々時間がかかりますが、成功すれば以下のようにイメージが生えたことを確認できるはずです。
$ nvidia-docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
local               tf                  7461583755dd        4 minutes ago       1.707 GB
local               nbody               c7559dcc54c7        21 minutes ago      1.467 GB
local               deviceQuery         3bab450c41f8        29 minutes ago      1.464 GB
nvidia/cuda         cudnn               baffdac82a9d        4 days ago          1.354 GB
nvidia/cuda         latest              981203204dd5        4 days ago          1.229 GB
あとは、TensorFlow公式の手順に従って簡単な動作確認をおこないましょう。
$ nvidia-docker run --rm -it local:tf /bin/bash
# python
...
>>> import tensorflow as tf
I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcublas.so locally
I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcudnn.so locally
I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcufft.so locally
I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcuda.so.1 locally
I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcurand.so locally
>>> sess = tf.Session()
I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:900] 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_init.cc:102] Found device 0 with properties:
name: GeForce GTX 750 Ti
major: 5 minor: 0 memoryClockRate (GHz) 1.0845
pciBusID 0000:01:00.0
Total memory: 2.00GiB
Free memory: 1.96GiB
I tensorflow/core/common_runtime/gpu/gpu_init.cc:126] DMA: 0
I tensorflow/core/common_runtime/gpu/gpu_init.cc:136] 0:   Y
I tensorflow/core/common_runtime/gpu/gpu_device.cc:755] Creating TensorFlow device (/gpu:0) -> (device: 0, name: GeForce GTX 750 Ti, pci bus id: 0000:01:00.0)
>>> a = tf.constant(10)
>>> b = tf.constant(32)
>>> print(sess.run(a + b))
42
>>>
動作していますね。

カスタムイメージから生成したコンテナでGPUを利用できることを確認する

MNISTでTensorFlow+GPU(cuDNN)の動作確認

ここでは、定番のMNISTを利用して正しくGPUを利用できていることを確認します。
ホスト側で次のコマンドを実行します。
$ nvidia-docker run --rm local:tf python -m tensorflow.models.image.mnist.convolutional
すると次のようにログが延々と出力されます。
I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcublas.so locally
I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcudnn.so locally
I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcufft.so locally
I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcuda.so.1 locally
I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcurand.so locally
Successfully downloaded train-images-idx3-ubyte.gz 9912422 bytes.
Successfully downloaded train-labels-idx1-ubyte.gz 28881 bytes.
Successfully downloaded t10k-images-idx3-ubyte.gz 1648877 bytes.
Successfully downloaded t10k-labels-idx1-ubyte.gz 4542 bytes.
Extracting data/train-images-idx3-ubyte.gz
Extracting data/train-labels-idx1-ubyte.gz
Extracting data/t10k-images-idx3-ubyte.gz
Extracting data/t10k-labels-idx1-ubyte.gz
I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:900] 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_init.cc:102] Found device 0 with properties:
name: GeForce GTX 750 Ti
major: 5 minor: 0 memoryClockRate (GHz) 1.0845
pciBusID 0000:01:00.0
Total memory: 2.00GiB
Free memory: 1.96GiB
I tensorflow/core/common_runtime/gpu/gpu_init.cc:126] DMA: 0
I tensorflow/core/common_runtime/gpu/gpu_init.cc:136] 0:   Y
I tensorflow/core/common_runtime/gpu/gpu_device.cc:755] Creating TensorFlow device (/gpu:0) -> (device: 0, name: GeForce GTX 750 Ti, pci bus id: 0000:01:00.0)
Initialized!
Step 0 (epoch 0.00), 577.0 ms
Minibatch loss: 12.054, learning rate: 0.010000
Minibatch error: 90.6%
Validation error: 84.6%
Step 100 (epoch 0.12), 15.3 ms
Minibatch loss: 3.294, learning rate: 0.010000
Minibatch error: 4.7%
Validation error: 7.3%
Step 200 (epoch 0.23), 15.1 ms
Minibatch loss: 3.470, learning rate: 0.010000
Minibatch error: 10.9%
Validation error: 4.0%
...
以下略
実行中に他のターミナルからnvidia-smiを実行してGPUの利用状況を確認してみます。
$ nvidia-docker run --rm nvidia/cuda nvidia-smi
Sun May  8 08:07:38 2016
+------------------------------------------------------+
| NVIDIA-SMI 361.42     Driver Version: 361.42         |
|-------------------------------+----------------------+----------------------+
| 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 750 Ti  Off  | 0000:01:00.0     Off |                  N/A |
| 36%   57C    P0    30W /  38W |   1926MiB /  2046MiB |     80%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
+-----------------------------------------------------------------------------+
GPU57
PC1-2GPU
$ nvidia-docker run --rm nvidia/cuda nvidia-smi
Sun May  8 08:11:27 2016
+------------------------------------------------------+
| NVIDIA-SMI 361.42     Driver Version: 361.42         |
|-------------------------------+----------------------+----------------------+
| 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 750 Ti  Off  | 0000:01:00.0     Off |                  N/A |
| 27%   35C    P8     1W /  38W |      7MiB /  2046MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+

Docker

nvidia-dockerDockerDocker Hubnvidia/cuda
()UbuntuCentOS
latestUbuntupull1.2GB
muo@muo-desktop:~$ nvidia-docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nvidia/cuda         latest              981203204dd5        4 days ago          1.229 GB
扱いやすいですね。
Docker Hub上に標準的な置き方をされているため、派生イメージを作りやすい(実際、今回の手順の途中でTensorFlow入りのものを作ったように)のでとても便利です。

トラブルシューティング: MNISTの動作が途中で終了してしまう場合

$ nvidia-docker run --rm local:tf python -m tensorflow.models.image.mnist.convolutional
での実行が
...
Extracting data/train-images-idx3-ubyte.gz
Extracting data/train-labels-idx1-ubyte.gz
Extracting data/t10k-images-idx3-ubyte.gz
Extracting data/t10k-labels-idx1-ubyte.gz
Initialized!
のように元気よくInitialized!と叫んであっさり終わってしまう場合、以下のログエントリが出力されていないか確認してみてください。
I tensorflow/stream_executor/dso_loader.cc:99] Couldn't open CUDA library libcudnn.so. LD_LIBRARY_PATH: /usr/local/nvidia/lib:/usr/local/nvidia/lib64:
I tensorflow/stream_executor/cuda/cuda_dnn.cc:1562] Unable to load cuDNN DSO
cuDNNDocker
DockerfileFROM nvidia/cudaFROM nvidia/cuda:latest(FROM nvidia/cuda:devel)cuDNN
DockerfileTensorFlowDockerFROM nvidia/cuda:cudnn(FROM nvidia/cuda:cudnn-ubuntu14.04)build

DockercuDNN

cudnncuDNN4(4.0.7)3(2016/05/08)cuDNN 5RCNVIDIAcuDNN 5RC

GPUWeb

NVIDIA DockerHTTPdiag
$ curl http://localhost:3476/gpu/info

Driver version:          361.42
Supported CUDA version:  8.0

Device #0
  Model:         GeForce GTX 750 Ti
  UUID:          GPU-9e723e86-d279-8a3d-25a4-010ecfa20551
  Path:          /dev/nvidia0
  Family:        Maxwell
  Arch:          5.0
  Cores:         640
  Power:         38 W
  CPU Affinity:  NUMA node0
  PCI
    Bus ID:     0000:01:00.0
    BAR1:       256 MiB
    Bandwidth:  15760 MB/s
  Memory
    ECC:        false
    Global:     2046 MiB
    Constant:   64 KiB
    Shared:     64 KiB
    L2 Cache:   2048 KiB
    Bandwidth:  86400 MB/s
  Clocks
    Cores:        1293 MHz
    Memory:       2700 MHz
  P2P Available:  None

$ curl http://localhost:3476/gpu/status

Device #0
  Power:        1 / 38 W
  Temperature:  33 °C
  Utilization
    GPU:      0 %
    Memory:   1 %
    Encoder:  0 %
    Decoder:  0 %
  Memory
    Global:  7 / 2046 MiB
    ECC Errors
      L1 Cache:  N/A
      L2 Cache:  N/A
      Global:    N/A
  PCI
    BAR1:  1 / 256 MiB
    Throughput
      RX:  0 MB/s
      TX:  0 MB/s
  Clocks
    Cores:    135 MHz
    Memory:   405 MHz
  Processes:  None
GPUを利用するプロセスを走らせた状態で/gpu/statusへアクセスすると
...
  Clocks
    Cores:   1019 MHz
    Memory:  2700 MHz
  Processes
    - PID:     16759
      Name:    python
      Memory:  1841 MiB

+調

docker-compose + docker-machine ?

docker-machine+docker-compose(swarm)https://github.com/NVIDIA/nvidia-docker/issues/8

NVIDIA Docker使cuDNN+TensorFlow
Windowsdocker-machine+Hyper-VGPUCUDA ToolkitNVIDIA GPULinuxWindows(=)

2 件のコメント:

  1. Maintainer of nvidia-docker here, I had to ask Google to translate for me, but the article sounds very good and well-documented, very nice work!
    Let us know if you have any issue :)

    返信削除
    返信
    1. Wow, thank you for dropping a comment. nvidia-docker rocks!!

      削除