機械学習ライブラリがいろいろあって目移りしますね。私もUbuntuにCaffe,Chainer,Tensorflowと一通り入れてみたのですが、それ以外にも欲張ってあれもこれもと入れているうちに、ライブラリの依存関係がおかしくなったり、入れ替えると戻らなくなったりして、5回ぐらいインストールし直していました。
さすがに辛くなってきたのでgoogle先生に尋ねたところ、docker内からGPU使えることがわかったので、備忘録を兼ねてご紹介します。NVIDIA Dockerを使います。
この記事のゴール
- NVIDIA Dockerをインストールして
- Caffe組み込み済みのイメージを作成して
- コンテナの中に入ってMNIST実行するまで
NVIDIA Dockerとは何ぞい?
NVIDIA社の日本語説明ページが参考になりますが、ホストOS側にGPUのドライバがあって、dockerのコンテナ側にCUDA Toolkitを置いて、dockerの中からでもGPUにアクセスできるようにしているようです。
NVIDIA Dockerを使わずに、docker越しにCUDAを使うという記事(Chainer開発用のDocker環境を整備する)も参考になりました。
導入環境
- OS: Ubuntu Server 14.04.3 LTS
- CPU: Intel(R) Core(TM) i5-4670
- GPU: GeForce GTX 980 Ti
- Memory: 8Gbyte
- HDD: 1Tbyte
準備
フォーマット済みのHDDに新規でubuntuインストールして、GPUのドライバインストールして、dockerインストールするまで。
- Ubuntu Server 14.04.3 LTSをPCにインストールします
- Linuxカーネルが3.19.0-43なので、docker使うのに都合がよいです。参考)Ubuntu 14.04 へ Docker のインストール準備
- CUDA Toolkitをインストールします
- GPUのドライバをインストールするのが目的です。CUDA Toolkitインストールするとドライバもインストールされます。
- CUDA Toolkit 7.5からubuntu14.04用のバイナリをダウンロードします。
- 上記リンクの下部に書いてある通りにインストールします
- インストールが終わったら一度再起動します
- dockerをインストールします
- NVIDIA Dockerはdockerのアドインっぽいので、docker自体はそのままインストールします。
- docker公式のubuntuのインストール方法を書いているページを参考にします
$ sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
$ sudo echo 'deb https://apt.dockerproject.org/repo ubuntu-trusty main' > /etc/apt/sources.list.d/docker.list
$ sudo apt-get update
$ sudo apt-cache policy docker-engine
$ sudo apt-get install docker-engine
$ sudo service docker start
NVIDIA Dockerインストール
nvidia-dockerのインストールページ見ると、ソースコードからインストールしろとしか書いていないので、githubからとってきます。
$ git clone https://github.com/NVIDIA/nvidia-docker.git
$ cd nvidia-docker
$ sudo make install
$ sudo nvidia-docker volume setup
特に何も指定しなければ、/usr/bin/
ディレクトリに、nvidia-docker
とnvidia-docker-plugin
が作成されます。
またdockerのイメージとして、最終的に以下のラベルを持つイメージが作成されます。(同じIDのものは同じイメージです。IDは作成環境によって変わります)
REPOSITORY | TAG | IMAGE ID |
---|---|---|
cuda | 7.5 | 242f4f109770 |
cuda | latest | 242f4f109770 |
cuda | 7.5-devel | 242f4f109770 |
cuda | devel | 242f4f109770 |
nvidia/cuda | latest | 242f4f109770 |
cuda | 7.5-runtime | 6199b491d4b3 |
最後の、sudo nvidia-docker volume setup
の挙動ですが、このコマンド実行後、/var/lib/docker/volumes
ディレクトリに、nvidia_driver_352.63
というファイルができているのを確認しました。
NVIDIA Dockerを使ってみる
$ sudo nvidia-docker run nvidia/cuda nvidia-smi
でnvidia-dockerを実行すると、dockerの中からGPU情報を見て結果を出力します。
+------------------------------------------------------+
| NVIDIA-SMI 352.63 Driver Version: 352.63 |
|-------------------------------+----------------------+----------------------+
| 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 980 Ti Off | 0000:01:00.0 On | N/A |
| 0% 50C P8 23W / 275W | 99MiB / 6140MiB | 0% Default |
+-------------------------------+----------------------+----------------------+
+-----------------------------------------------------------------------------+
| Processes: GPU Memory |
| GPU PID Type Process name Usage |
|=============================================================================|
+-----------------------------------------------------------------------------+
コンテナ自体はdockerからも扱えるのですが、runさせた際に/usr/local/nvidia
ディレクトリが見えないため、nvidia-smiにアクセスできず、こんな結果になります。
$ sudo docker run nvidia/cuda nvidia-smi
exec: "nvidia-smi": executable file not found in $PATH
いろいろ試してみたところ、一度nvidia-dockerでrunさせた後、後からattachで入る時はdockerでもよいことがわかりました。
$ sudo nvidia-docker run -it --name "test1" nvidia/cuda /bin/bash
(ctrl-p, ctrl-qで抜ける)
$ sudo docker attach test1
$ nvidia-smi
-> 成功する
※ちなみに、nvidia-smiのProcessesについては、同一コンテナ内であってもGPUのプロセスが出てこないです。温度とファンの回り具合はわかります。ホストOS側のnvidia-smiには全て出てきます。
Caffeを入れてみる
さて、本来の目的のCaffeをdocker上に導入してみます。せっかくですので、Dockerfileを使ってインストールすることにします。
1. cuDNNを入れる
cuDNNについては、こちらの記事を参考にしてください。
GTC 2015 - Deep Learning用のCUDAライブラリ「cuDNN」
以下のコマンドで、イメージを作成します。Dockerfileの中を見ると、さきほどの「NVIDIA Dockerインストール」で作成したcuda:7.5-devel
のイメージを基にしていることがわかります。
nvidia-docker
ディレクトリからの作業をを想定しています。buildの後の-tオプションはタグ名です。タグ名は手順4で出てくるDockerfileで使います。
$ sudo nvidia-docker build -t cuda:7.5_cudnn70 ./ubuntu/cuda/7.5/devel/cudnn3
以下2〜4の手順は、便宜的に同じディレクトリで作業する前提とします。
2. Anacondaをダウンロードしておく
buildの度に毎回ダウンロードするように、Dockerfileを書いてもいいのですが、やり直す度に毎回ダウンロードするのも時間がかかるので、事前に用意しておきます。
Anacondのダウンロードページから、Linux 64bit, Python2.7を選択してダウンロードします。
コマンドで一気にダウンロードしたい場合は以下になります。
$ curl "https://3230d63b5fc54e62148e-c95ac804525aac4b6dba79b00b39d1d3.ssl.cf1.rackcdn.com/Anaconda2-2.4.1-Linux-x86_64.sh" > Anaconda2-2.4.1-Linux-x86_64.sh
3. CaffeのMakefile.configを先に作っておく
Caffeのmake時に、Makefile.configというファイルが必要なのですが、これは事前に用意しておく必要があります。
雛形を取ってきて編集しましょう。
$ curl https://raw.githubusercontent.com/BVLC/caffe/master/Makefile.config.example -O
$ cp Makefile.config.example Makefile.config
Makefile.configはコメント行を除外しています。
cuDNN使うよというのと、pythonがAnadonda環境だよという点を修正しています。
USE_CUDNN := 1
CUDA_DIR := /usr/local/cuda
CUDA_ARCH := -gencode arch=compute_20,code=sm_20 \
-gencode arch=compute_20,code=sm_21 \
-gencode arch=compute_30,code=sm_30 \
-gencode arch=compute_35,code=sm_35 \
-gencode arch=compute_50,code=sm_50 \
-gencode arch=compute_50,code=compute_50
BLAS := atlas
ANACONDA_HOME := /opt/anaconda2
PYTHON_INCLUDE := $(ANACONDA_HOME)/include \
# $(ANACONDA_HOME)/include/python2.7 \
# $(ANACONDA_HOME)/lib/python2.7/site-packages/numpy/core/include \
PYTHON_LIB := $(ANACONDA_HOME)/lib
INCLUDE_DIRS := $(PYTHON_INCLUDE) /usr/local/include
LIBRARY_DIRS := $(PYTHON_LIB) /usr/local/lib /usr/lib
BUILD_DIR := build
DISTRIBUTE_DIR := distribute
TEST_GPUID := 0
Q ?= @
4. Dockerfileを作る
作業1で作ったイメージをベースにして、以下の作業を行うDockerfileを作成します。
- Anaconda(64bit, Python2.7)のインストール
- Caffe用パッケージのインストール
- Caffeをgithubから取得
- Caffeのmake
あと、build時にエラーが出る部分を修正しています。
AnacondaとCaffeは、/opt以下に作成しています。
以下をコピペして、Dockerfileというファイル名で保存します。
FROM cuda:7.5_cudnn70
COPY Anaconda2-2.4.1-Linux-x86_64.sh /opt
# caffe makefile:use anaconda2 / use cudnn3
COPY Makefile.config /opt
# install anaconda2
RUN echo 'export PATH=/opt/anaconda2/bin:$PATH' > /etc/profile.d/conda.sh && \
cd /opt && \
/bin/bash /opt/Anaconda2-2.4.1-Linux-x86_64.sh -b -p /opt/anaconda2 && \
rm /opt/Anaconda2-2.4.1-Linux-x86_64.sh
# prepare caffe
RUN apt-get update && apt-get install -y \
libatlas-base-dev libprotobuf-dev libleveldb-dev libsnappy-dev libopencv-dev libboost-all-dev libhdf5-serial-dev libgflags-dev libgoogle-glog-dev liblmdb-dev protobuf-compiler git wget && \
rm -rf /var/lib/apt/lists/*
# solution "libdc1394 error: Failed to initialize libdc1394"
# ref) http://stackoverflow.com/questions/12689304/ctypes-error-libdc1394-error-failed-to-initialize-libdc1394
RUN ln -s /dev/null /dev/raw1394
# solution "Error loading shared library libhdf5_hl.so"
# ref) https://github.com/BVLC/caffe/issues/1463
RUN cp /opt/anaconda2/pkgs/hdf5-1.8.15.1-2/lib/libhdf5.so.10.0.1 /lib/x86_64-linux-gnu/ && \
cp /opt/anaconda2/pkgs/hdf5-1.8.15.1-2/lib/libhdf5_hl.so.10.0.1 /lib/x86_64-linux-gnu/ && \
ldconfig
# make caffe
RUN cd /opt && \
git clone https://github.com/BVLC/caffe.git && \
cd caffe && \
cp /opt/Makefile.config . && \
make all -j4 && \
make test -j4
# ref) https://hub.docker.com/r/eduwass/face-the-internet-worker/~/dockerfile/
CMD sh -c 'ln -s /dev/null /dev/raw1394'; bash
面倒な点がひとつありまして、ln -s /dev/null /dev/raw1394
という処理が、イメージ作成時だけではなくて、コンテナを作成する度に必要になります。
参考) How to persist 'ln' in Docker with Ubuntu
そのため、コンテナ起動時に実行するコマンドとして設定しています。
5. Let's nvidia-docker build!
2〜4で作ったファイルを全て同じディレクトリ内に入れて確認します。3つのファイルがありますね。
$ ls
Anaconda2-2.4.1-Linux-x86_64.sh Dockerfile Makefile.config
そのディレクトリで以下のコマンドを実行します。
$ sudo nvidia-docker build -t ml_caffe:7570ana2 .
(たぶんこのタイミングでは、docker build -t ml_caffe:7570ana2 .
でも同じだと思われますが、念のためnvidia-dockerを使っています)
自動的にbuildが始まります。ここではbuild後のイメージタグ名を、ml_caffe:7570ana2
としています。
6. runtestで確認しましょう
makeがうまくいったのか、Caffeの動作環境は問題ないか確認しましょう。一番簡単な確認は、Caffeのmake runtestです。
※ 実は、Dockerfileの最後に、make runtest
って書いたら、GPUが使えなくてエラーが出たので、build時はnvidia-dockerはGPU使えないっぽいです。
以下のコマンドでコンテナ内に入り、make runtestを実行します。
$ sudo nvidia-docker run -it --name "runtest" ml_caffe:7570ana2
root@e14ad9a3b2d6:/# cd /opt/caffe; make runtest
※ root@e14ad9a3b2d6:/#
の部分は、コンテナの中に入った後のシェルプロンプトです。e14ad9a3b2d6
はコンテナIDなので、毎回runする度に変わります。
ちゃんと[RUN][OK]が出力されていたらGPUは認識されています。全てのテストがPASSすることを確認しましょう。最後はこんな感じになります。
[----------] Global test environment tear-down
[==========] 1744 tests from 257 test cases ran. (241958 ms total)
[ PASSED ] 1744 tests.
普通のdockerで実行するとどうなるかやってみましょう。
$ sudo docker run -it --name "runtest_docker" ml_caffe:7570ana2
root@b25a57ebfe81:/# cd /opt/caffe; make runtest
.build_release/tools/caffe
caffe: command line brew
usage: caffe <command> <args>
:
(中略)
:
[----------] 1 test from HDF5OutputLayerTest/3, where TypeParam = caffe::GPUDevice<double>
[ RUN ] HDF5OutputLayerTest/3.TestForward
F0118 12:02:51.914427 240 syncedmem.hpp:18] Check failed: error == cudaSuccess (35 vs. 0) CUDA driver version is insufficient for CUDA runtime version
*** Check failure stack trace: ***
:
(後略)
:
一瞬動いたかな?と思うのですが、すぐにCUDAのドライバのバージョンがおかしいというエラーが出て終了します。
7. MNIST動かしてみましょう
MNISTは機械学習におけるHello worldにあたります。参考)MNIST For ML Beginners
というわけで、以下のコマンドで試してみましょう。コンテナの中に入って作業します。
参考)Caffeで手書き数字(MNIST)の認識学習をする
$ sudo nvidia-docker run -it --name "mnist" ml_caffe:7570ana2
root@a99d3a442790:/# cd /opt/caffe/
root@a99d3a442790:/opt/caffe# ./data/mnist/get_mnist.sh
root@a99d3a442790:/opt/caffe# ./examples/mnist/create_mnist.sh
root@a99d3a442790:/opt/caffe# ./examples/mnist/train_lenet.sh
やっていることは、シンプルです。
- ディレクトリ移動して(cd /opt/caffe/)
- MNIST用データ取得して(./data/mnist/get_mnist.sh)
- データを学習させやすい形に変換して(./examples/mnist/create_mnist.sh)
- 学習させます(./examples/mnist/train_lenet.sh)
学習結果として、最後こんな出力が出てきたら完了です。
I0118 13:18:48.695695 34 solver.cpp:459] Snapshotting to binary proto file examples/mnist/lenet_iter_10000.caffemodel
I0118 13:18:48.700121 34 sgd_solver.cpp:273] Snapshotting solver state to binary proto file examples/mnist/lenet_iter_10000.solverstate
I0118 13:18:48.702507 34 solver.cpp:321] Iteration 10000, loss = 0.00265015
I0118 13:18:48.702540 34 solver.cpp:341] Iteration 10000, Testing net (#0)
I0118 13:18:48.765329 34 solver.cpp:409] Test net output #0: accuracy = 0.9905
I0118 13:18:48.765362 34 solver.cpp:409] Test net output #1: loss = 0.0263541 (* 1 = 0.0263541 loss)
I0118 13:18:48.765379 34 solver.cpp:326] Optimization Done.
I0118 13:18:48.765384 34 caffe.cpp:215] Optimization Done.
以上、nvidia-dockerでのCaffeの導入でした!
はまったところ
nvidia-dockerで1つはまったところがあったので共有しておきます。
コンテナを作った後でdocker rm
なり、nvidia-docker rm
でコンテナ削除した場合は問題ないのですが、nvidia-docker run --rm
で起動してコンテナを自動削除した場合、`nvidia-docker volume setup'で作成したボリュームが勝手に消えることがあります。
突然、Error response from daemon: Error looking up volume plugin nvidia-docker: Plugin not found
というエラーが出てきたので、「何もしていないのにエラーが出た」という言葉が浮かんできました。
気を取り直して条件を探ったところ、コンテナが全く登録されていない時に、nvidia-docker run --rm 〜
を実行すると消えます。
状況を並べるとこうなります。
$ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
$ sudo nvidia-docker run -it --rm ml_caffe:7570ana2
root@34302aeabcfa:/# exit
exit
$ sudo nvidia-docker run -it --rm ml_caffe:7570ana2
Error response from daemon: Error looking up volume plugin nvidia-docker: Plugin not found
バグなのか仕様なのかよくわかってないので、とりあえずの対策としては、
-
--rm
オプションは使わないようにする -
sudo nvidia-docker volume setup; nvidia-docker run --rm 〜
とコマンドを並べて回避 -
Error looking up volume plugin nvidia-docker: Plugin not found
が出たら、sudo nvidia-docker volume setup
を実行する
となりますね。
まとめ
個人で使う分には、環境をdockerのイメージ毎に隔離させることができるので、非常に使い勝手がいいと思っています。
dockerのイメージが、自分のPCのグラフィックカード依存になっているような気がするので、dockerの趣旨に反しているのかもしれませんが、容量用法を守って正しく使えば大丈夫でしょう。
GPUでも楽しいdockerライフを!