Help us understand the problem. What is going on with this article?

Ubuntu14.04.3でnvidia-docker使ってCaffeをインストールしてみた

More than 3 years have passed since last update.

機械学習ライブラリがいろいろあって目移りしますね。私も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インストールするまで。

  1. Ubuntu Server 14.04.3 LTSをPCにインストールします

  2. CUDA Toolkitをインストールします

    • GPUのドライバをインストールするのが目的です。CUDA Toolkitインストールするとドライバもインストールされます。
    • CUDA Toolkit 7.5からubuntu14.04用のバイナリをダウンロードします。
    • 上記リンクの下部に書いてある通りにインストールします
    • インストールが終わったら一度再起動します
  3. dockerをインストールします

$ 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-dockernvidia-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環境だよという点を修正しています。

Makefile.config
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というファイル名で保存します。

cuda7.5_cudnn7.0_anaconda2_caffe/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ライフを!

daxanya1
2018年はVRで動かす方向で。 勉強中:Houdini,VR,FPGA,python,MODO,OpenGL,go,node,Chainer,Slack,CUDA,OpenCV,Arduino
http://enkaku.jp/blog/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした