Monthly Hacker's Blog

プログラミングや機械学習の記事を中心に書きます。

dockerでディープラーニング(chainer)環境を整える

できること

この記事では、次のことができるようになります。

  • dockerを使ったディープラーニング(chainer)環境構築

経緯

名前だけは知っていたものの、手を出していなかったdockerですが、試してみたらとても便利だったのでまとめてみます。巷にはdockerに関する記事はたくさんあるのですが、ただインストールするだけだったり、本当にこの環境で機械学習をやっているの?と疑ってしまうような環境だったりで、満足いくものがなかったので、試行錯誤してみました。

この記事では、コンテナやイメージなど、dockerの用語の解説は最小限に抑え、快適な機械学習環境を自分で構築できることを目指します。

dockerを使ったほうが良いケース

例えば2つのテーマで研究を並行していて、それぞれchainerのv1系とv3系を使っているくらいのケースであれば、condaなどで仮想環境を作るだけで事足りると思います。やはりdockerを使うべきはGPUを使いたい環境でCUDA周りの面倒な設定をしたくないときです。もちろん、前述のケースでdockerを使うのもありですが、そのメリットを感じられるほど私はまだdockerを使いこなしていません。ということで、以降はGPUがある環境でdockerを使うことを想定します。

インストール

インストールするのは3つです。

  • nvidia driver
  • docker
  • nvidia-docker

dockerではドライバーまで扱うことができないため、GPU用のドライバーは予めインストールする必要があります。dockerは主役なので絶対に必要です。最後にnvidia-dockerですが、dockerはそのままではGPUを認識しません。色々と手を加えると認識するようですが、そこを自動化してくれるのがnvidia-dockerです。コマンドも互換性があり、dockerをnvidia-dockerに置き換えるだけで(知っている限り)全てのコマンドが動きます。インストールの手順は他にも多くの情報があるので、そちらを参照してください。なお、普段使用しているユーザーをdockerグループに追加しておいてください。後で必要になります。

一般的な環境構築

dockerでは、Dockerfileというファイルに構築したい環境を書いていきます。例えばchainerの公式dockerを見てますと

見ていただくとなんとなく分かるかと思いますが、順に説明していきます。

FROM

ベースにする環境です。上のコードはchainerのgithubから引用しているので、コードが更新されると変わりますが、ブログ執筆時はnvidia/cuda:7.5-cudnn5-develが指定されています。これは、nvidiaが提供しているcudaという環境のうち、7.5-cudnn5-develというタグがついているものをベースにしているということです。そのタグ名通り、CUDA7.5/cuDNN5が入っている環境です。これでGPU周りの環境構築は済みました。今までの苦労が嘘のようです。

RUN

環境構築に必要なコマンドです。主にaptやpipで、自分が必要なパッケージをインストールするのに使います。

ということで、基本的にはこのDockerfileを書き換えるか、FROMでchainer/chaierを指定したDockerfileを作ればいいのですが、1つ問題がありました。それはCUDAとcuDNNのバージョンが古いことです。比較はしていませんが、どちらも最新版のほうが高速化されているはずなので、できれば最新版を使いたいところです。しかも、chainer v3ではCUDA9.0/cuDNN7まで対応しているのに公式dockerはCUDA7.5/cuDNN5です。そこで、CUDA9.0/cuDNN7に対応したDockerfileを作りました。

github.com

実はFROMの仮想環境を単純に変えるだけでは動かなかったので、多少変更を加えています。python3系しか用意していないので、もし2系が必要な方がいればgithubでPRお待ちしています。

機械学習に特化した環境構築

dockerの用途として、プロダクト用のコードが環境に依存せずに動くようにすることがあります。逆に言うと、そうした用途では機械学習のように色々なコードを試行錯誤しながら動かすことは稀で、基本的には1Dockerfileで1プログラムだけ動かします。その場合は気にならないのですが、機械学習だと気になることがあります。それは、dockerは基本的にrootで動かすということです。つまり、何も設定をしないとrootで全てのプログラムが実行されます。そのため、学習プログラムが吐き出したログの所有権がrootにあり、削除できなかったり編集できなかったりといったことが起きます。正直とても不便です。私は次のようなDockerfileを用意することで解決しています。

FROM dhgrs/chainer:latest
RUN pip install matplotlib==2.1.0 librosa==0.5.1 Pillow==4.3.0 pandas==0.20.3
ARG uid
ARG gid
ARG user
RUN echo "${user}:x:${uid}:${gid}:${user},,,::/bin/bash" >> /etc/passwd && \
    echo "${user}:x:${uid}:" >> /etc/group
RUN mkdir /.cupy && \
    chown ${user}:${user} /.cupy
USER ${user}

自分で作ったchainer環境をFROMで持ってきて、RUNでpipするところまでは良いかと思います。このファイルでは新しい文法が2つ出てきます。

ARG

Dockerfileから環境を作ることをビルドといいます。ビルドの際に引数に指定したものを使うと明示しています。

USER

どのユーザーで実行するか明示しています。

このファイルを簡単に解説します。USERで指定するユーザーはあくまでdocker内のユーザーであり、例えばここで既存のユーザー名を指定してもログの所有権はそのユーザーにはなりません。これは、ユーザー名は実は名前だけでなくユーザーIDで管理されているからです。つまりユーザーIDが異なると、ユーザー名が一緒でも別ユーザーと認識されます。そこで、docker内外でユーザーIDを一緒にすることで、ログの所有権を既存ユーザーにすることができます。そのコマンドが1つ目のRUNです。2つ目のRUNは、cupyが学習時にキャッシュ用のディレクトリを作るのですが、ルートディレクトリに作るため、rootでないとアクセスできなくてエラーが出るので、予め作成しておきます。

ビルドする

実際に環境を構築するビルドは、構築する環境の名前をbuildとして、Dockerfileがあるディレクトリで次のコマンドでできます。

cat Dockerfile | docker build -t develop --build-arg uid=`id -u` --build-arg gid=`id -g` --build-arg user=`whoami` -

正常にビルドができると、ビルドした環境一覧に表示されます。次のコマンドで確認できます。

docker images

imagesというオプションで分かるかと思いますが、この構築した環境のことをイメージと呼びます。

イメージを使う

先ほどビルドしたdevelopというイメージを使うときは、次のコマンドです。

nvidia-docker run -v /home/$USER:/home/$USER -it develop /bin/bash -

少し補足すると、イメージを使うのはGPUを使うときなので、必ずdockerではなくnvidia-dockerを使います。また、docker内では基本的にdocker外のファイルにアクセスできません。-vオプションで明示的にマウントする必要があります。今回はホームディレクトリをマウントする例です。

エイリアスを登録

毎回上記のコマンドを打つのは面倒なので、私はエイリアスに登録しています。ubuntuであれば、次のコマンドでエイリアスに登録ができます。

echo "alias develop=\"nvidia-docker run -v /home/$USER:/home/$USER -it develop /bin/bash -\"" >> ~/.bashrc

nohupなどバックグラウンドで計算を回し続けたいとき(2017/10/22追記)

大事なことを書き忘れていました。深層学習など、学習に時間がかかる手法を動かすときは、nohupコマンドを使って次のようにやることが多いと思います。

nohup python train.py -g 0 &

しかし、docker上でこのコマンドを打ち、ターミナルを消したりdockerを終了したりすると、プロセスも死にます。なぜなら、dockerは仮想環境なので、dockerが終了するとプロセスが動く環境がなくなるからです。ではどうしたら良いかというと、Ctrl+p, Ctrl+qです。これで、docker環境を起動したまま抜けることができます。一度抜けた環境に戻る場合は、

docker ps

で環境のID(CONTAINER ID)を調べます。例えばIDが1234だとすると、

docker attach 1234

のようにして再度環境に入ることができます。

最後に

dockerは一度環境構築さえやってしまえば後は非常に楽です。とはいえその一度が大変というのはよく分かりました。この記事で少しでもdockerのハードルを下げられればと思います。