nykergoto’s blog

機械学習とpythonをメインに

Dockerによる深層学習及び機械学習環境構築

機械学習だろうがなんだろうが同じですが、環境構築するところだけで結構めんどくさいことが多いです。 今回はそのへんをまるっと管理できる仮想環境ツールのDockerを用いた環境構築の手順を記していきます。

目次

  • 執筆環境
  • Dockerとは
    • 機械学習を行う上でDockerを使うメリット
    • Dockerのインストール
  • Nvidia Docker
    • 必要条件
    • Install
    • Nvidia-Dockerを使う
    • つくる
    • 二回目以降
    • オプション
  • まとめ

書いていないこと: GeForce GTX 1080 Ti にドライバを入れるまでの作業

執筆環境

Dockerとは

一言でいうと仮想環境です。 今使っているOSとは別のOSを起動するようなものなので、自分の環境を汚すことなくアプリケーションを導入することが可能になります。 Dockerはアプリケーションの単位で仮想環境を作るので

  • 起動時にディスクやメモリを消費しない(使った分だけ消費する)
  • OSレベルの仮想化を行っているため、起動が早い

ことなどが特徴です。 Docker ではイメージとコンテナという概念があり、ざっくり言ってしまうと

  • イメージ: 設計図
  • コンテナ: 設計図を元に作った実際の環境

という関係です。 (プログラム的に言うと、イメージがクラスに、コンテナがインスタンスと言った感じでしょうか) ユーザーは

  1. 設計図を Dockerfile に記述し
  2. docker build でイメージとして登録し
  3. docker run でコンテナを作成

と言ったフローで利用することになります。 とてもざっくりとした説明で厳密には間違っている気しかしないので、詳細はチュートリアルやドキュメントを参照してください。

機械学習を行う上でDockerを使うメリット

一番のメリットは OSの環境設定をコードとして管理できる という点にあります。

機械学習や深層学習を行う際には主に python を用いることが多いですが、はじめに pyenv, vertualenv, anaconda などの環境設定をし、その次に pip で必要なライブラリを導入し、その際に必要なパッケージは apt-get してエラーが出たらググッてパス設定やパッケージ導入をして… など再現性に乏しい環境が出来やすいです。 そのため。新しいサーバーやPCを購入した際、以前と全く同じ環境を用意することができず、前は動いたのに今は動かない、何がおかしいのかよくわからない、などということが発生しがちです。

一方で Docker では、インストールの手順やどのOSを使うかなどは Dockerfile に記述することができます。 そのため Dockerfile さえあれば全く同じ環境を任意のPCで作成することができます。 しかも docker build だけで。これはとてもうれしいですね。なのでやりましょう。

Dockerのインストール

$ sudo apt-get -y install apt-transport-https ca-certificates curl software-properties-common
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
$ sudo apt-get update
$ sudo apt-get install docker-ce

docker に実行権限を与えるため docker グループにユーザーを追加します。

$ sudo adduser <user-name> docker

例えばユーザー名が tiger なら以下のように実行します

[tiger@local ]$ sudo adduser tiger docker
[tiger@local ]$ sudo reboot # 再起動して反映させる
[tiger@local ]$ groups
tiger adm cdrom sudo dip plugdev lpadmin sambashare docker

インストールが終わったら docker が動くことを確認します。 docker run -it hello-world を実行します。 これは hello-world イメージのコンテナを作るコマンドです。 基本的に docker run <image-name> でコンテナが起動します。 あとはオプションがごにょごにょつくだけなのでとても簡単ですね。

$ docker run -it hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
5b0f327be733: Pull complete 
Digest: sha256:175735360662078abd70dacb73c5518d5b3ae7c1ed069d22def5da57c3e917d6
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://cloud.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/engine/userguide/

Nvidia Docker

基本的に丁寧に wiki にまとまっているので困った時は wiki を見ると良いかなと思います。

必要条件

  • kernel version > 3.10
  • Docker >= 1.90
  • NVIDIA drivers >= 340.29

これにプラスアルファで、自分のドライバがどのバージョンか、というのはどの docker image を用いるのかに関係してきます。 ドライバの確認は nvidia-smi すると確認できます。 以下は僕の環境ですが、これだと 381.22 がドライバのバージョンです。

[tiger@local ]$ nvidia-smi
Sat Nov 11 06:15:33 2017       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 381.22                 Driver Version: 381.22                    |
|-------------------------------+----------------------+----------------------+
| 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 108...  Off  | 0000:01:00.0      On |                  N/A |
| 58%   70C    P2   257W / 280W |  10918MiB / 11171MiB |     98%      Default |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|    0      1074    G   /usr/lib/xorg/Xorg                             885MiB |
|    0      2201    G   compiz                                         331MiB |
|    0      2611    G   ...el-token=469C79572BB0987284DA4C6B5EF5678D   112MiB |
|    0     13986    G   unity-control-center                             2MiB |
|    0     24944    C   python                                        9497MiB |
|    0     26329    G   ...el-token=EC61E31D5E5DEE53F6E56703F908C373    82MiB |
+-----------------------------------------------------------------------------+

GPU がどのバージョンの CUDA にまで対応しているのか、は wiki にもまとまっています。 (CUDA > Requirements)

CUDA toolkit version Driver version GPU architecture
6.5 >= 340.29 >= 2.0 (Fermi)
7.0 >= 346.46 >= 2.0 (Fermi)
7.5 >= 352.39 >= 2.0 (Fermi)
8.0 == 361.93 or >= 375.51 == 6.0 (P100)
8.0 >= 367.48 >= 2.0 (Fermi)
9.0 >= 384.81 >= 3.0 (Kepler)

これを見ると 381.22 では CUDA8 までしか利用できないことがわかります。 (僕は Keras & Backend で Tensorflow を用いることが多く、現時点においては Tensorflow は CUDA8 までしかサポートされていない為、バージョンをあげていません。)

Install

Nvidia-Docker の公式 のまんまです。 (僕の場合特に何事もなくインストールできましたが、ディストリビューションによっては大変らしいのでここでコケると辛そう)

wget -P /tmp https://github.com/NVIDIA/nvidia-docker/releases/download/v1.0.1/nvidia-docker_1.0.1-1_amd64.deb
sudo dpkg -i /tmp/nvidia-docker*.deb && rm /tmp/nvidia-docker*.deb

最新版入れたいよーとか昔のやつ試すーという時は release ページ から rpmwget してきます。

Nvidia-Dockerを使う

docker コンテナを作成するときのコマンドを docker run から nvidia-docker run に置き換えるだけです。 そうするとよしなに内部でホスト側の GPU 環境とコンテナ内の環境を橋渡ししてくれるので、コンテナ内部でも GPU が使えるよーという仕組み(なはず)です。

試しに nvidia-smi がコンテナ内部でも動くか確認します。 以下はCUDA8のイメージをpullしているので、CUDA8対応以下のドライバが入っている場合には、タグの部分 8.0-devel を書き換える必要があります。

# Test nvidia-smi
$ nvidia-docker run --rm nvidia/cuda:8.0-devel nvidia-smi
8.0-devel: Pulling from nvidia/cuda
ae79f2514705: Already exists 
c59d01a7e4ca: Pull complete 
41ba73a9054d: Pull complete 
f1bbfd495cc1: Pull complete 
0c346f7223e2: Pull complete 
5dcd01667896: Pull complete 
ca677f607487: Pull complete 
b4637619a887: Pull complete 
8c644ff287da: Pull complete 
Digest: sha256:10edf770586c33dde900618c929f7e1c49197a618c7133452a4bed0bfdfa0478
Status: Downloaded newer image for nvidia/cuda:8.0-devel
Fri Nov 10 21:27:16 2017       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 381.22                 Driver Version: 381.22                    |
|-------------------------------+----------------------+----------------------+
| 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 108...  Off  | 0000:01:00.0      On |                  N/A |
| 57%   70C    P2   251W / 280W |  11041MiB / 11171MiB |     96%      Default |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
+-----------------------------------------------------------------------------+

ちゃんとGPUを認識しているのでOKです。

つくる

では実際に環境を作ります。

git clone git@github.com:nyk510/docker-zoo.git
cd docker-zoo
docker build -t penguin ./penguin

Dockerfile の中身は以下のようになっています。

FROM nvidia/cuda:8.0-cudnn6-devel-ubuntu16.04

LABEL maintainer="nyker <nykergoto@gmail.com>"

# update
RUN apt-get update

# install utilities
RUN apt-get install -y sudo git zsh openssh-server wget gcc libatlas-base-dev

# userの追加
RUN groupadd -g 1000 developer

# user:penguin, password:highway
RUN useradd -g developer -G sudo -m -s /bin/bash penguin
RUN echo "penguin:highway" | chpasswd

# 以下は penguin での操作
USER penguin
WORKDIR /home/penguin/

# docker build のディレクトリにあるファイル (.) を, カレントディレクトリ (`./`) にコピー
ADD . ./

# pyenv
RUN git clone git://github.com/yyuu/pyenv.git .pyenv
ENV HOME /home/penguin
ENV PYENV_ROOT ${HOME}/.pyenv
ENV PATH ${PYENV_ROOT}/shims:${PYENV_ROOT}/bin:${PATH}

RUN pyenv install anaconda3-5.0.1
RUN pyenv rehash
RUN pyenv install miniconda3-4.3.30
RUN pyenv rehash
RUN pyenv global anaconda3-5.0.1

# mxnet
RUN pip install mxnet-cu80

# keras
RUN pip install keras==2.0.9 tensorflow-gpu

# peco
RUN wget https://github.com/peco/peco/releases/download/v0.5.0/peco_linux_amd64.tar.gz
RUN tar -zxvf peco_linux_amd64.tar.gz
ENV PATH ${HOME}/peco_linux_amd64/:${PATH}

やってることはユーザーの作成と pyenvでの環境 + anaconda + miniconda / mxnet(gpu) / keras-tensorflow(gpu) のインストールです。 基本的には, シェルを記述しているだけなのでとてもシンプルです。 自分で好きなライブラリを入れまくりましょう。

build が終わったらコンテナを作成して、コンテナ内部でちゃんと GPU を認識しているか確認しましょう。 この時 nvidia-docker で run することをお忘れなく。 nvidia-smi が動けば成功です。

[tiger@local]$ nvidia-docker run -it --name penguin-sample penguin zsh
[penguin@ed5b133fe0fd ~]$ nvidia-smi
Sat Nov 11 01:16:02 2017
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 381.22                 Driver Version: 381.22                    |
|-------------------------------+----------------------+----------------------+
| 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 108...  Off  | 0000:01:00.0      On |                  N/A |
| 54%   69C    P2   154W / 280W |  11101MiB / 11171MiB |     72%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
+-----------------------------------------------------------------------------+

二回目以降

nvidia-docker run でコンテナをつくってログアウトするとコンテナは停止状態になります。 もう一度コンテナに入りたいときは

  1. docker start <container-name> でコンテナを起動
  2. docker exec -it <command> でコマンドを実行

という手順を取ります。 二回目以降は nvidia-docker で入る必要はありません。 なので先の penguin-sample コンテナの例で行くと以下のようになります。

[tiger@local ]$ docker start penguin-sample
penguin-sample
[tiger@local ]$ docker exec -it penguin-sample zsh
[penguin@ed5b133fe0fd ~]$

またこの時コンテナは一度目の情報を保存しています。なのでコンテナ内部で何かファイルを作成したり、環境変数を変えたり、apt-getしたりするとそれは次回以降も反映されます。

オプション

普通にコンテナを起動することはできました。 でも今の penguin-smaple コンテナはローカル環境と完全に閉じているのでファイルのやり取りなどができない状態になっていてちょっと不便です。 docker では -v を用いてディレクトリを bind することができます。 使い方は -t <path/to/local>:<path/to/container> です。 この時パスはフルパスを指定する必要があることに注意してください。 ~/hoge:/hoge とかは動きません。

例えばローカル環境で /data というディレクトリがあり、そこにデータセットなどがダウンロードされているとします。これをコンテナと接続しようと思うと以下のようになります。

[tiger@local ]$ nvidia-docker run -it -v /data/:/data --name penguin-with-data penguin zsh
[penguin@397c1018a407 ~]$ cd /data
[penguin@397c1018a407 /data]$ ls
data1   data2   mnist

まとめ

僕も内部の事とかよくわかってませんが、さくっと使うだけでもとても価値のあるサービスだと思うので、インフラ以外でも流行るといいなと思います。

環境構築って大変ですしね。