こんにちは、金杉です。
GPUを使って機械学習をするとき、リソースや開発で課題を抱えている人は多いのではないでしょうか。個人でGPUを所有していたり、学習時間にあまりこだわらなければ問題ないと思います。しかし、本格的なDeep Learningをやろうとすると、プロジェクト間で同じGPUを共有するケースや、複数の環境で学習をさせるなど、リソースプランニングをしっかりやる必要があります。クラウド型GPUを利用になるのであれば多少楽になるものの、CUDA環境やアプリケーションを都度インストールするのも手間ですよね。
そんなリソースが限られたなかで開発を効率化するために、今回はnvidia-dockerを使ってDeep Learningをコンテナで動かす方法を紹介したいと思います。
※このブログはnvidia-dockerの公式ドキュメントを参考にしています。
Issue
冒頭でも簡単に言及しましたが、Deep Learningを本格的に進めるにはさまざまな課題があります。
- チーム内で開発環境の共有が必要
- ライブラリやツールのバージョン管理が煩雑
- ドライバや開発環境の都度デプロイが手間
- 自社所有のGPUとクラウドGPUなど、複数環境を使い分けしたい
- 複数GPUを同サーバーに搭載しており、無駄が発生する
これらを解決するには、GPUの抽象化(コンテナ化)が向いています。モビリティが格段に向上する他、アプリケーションの共有や構築が簡単になり、結果的にコストダウンにも繋がります。GPUのコンテナ化にはいくつかの選択肢がありますが、Dockerが一番汎用的なので、今回はnvidia-dockerにフォーカスします。
nvidia-dockerとは
nvidia-dockerはNVIDIAのハードウェアを活用できるGPUコンテナを自動で認識し、セットアップできるようなToolkitを搭載した、DockerをラッピングしたCLI(プラグイン)です。NVIDIA社により開発されています。
Dockerについてはご存知、もしくはすでにご利用されているかたが多いと思います。Dockerの一番の特長としてあげられるのは、ハードウェアやプラットフォームへの依存性がないという点です。しかし、GPUは専用ハードウェアのため、利用になるには専用のドライバが必要です。よって、従来のDockerからはGPUリソースが認識できず、GPUのコンテナ化はできませんでした。ホストOSとコンテナの両サイドにドライバをインストールするという手段もありますが、その場合両方のドライバのバージョンが同じである必要があり、Docker本来の利便性を失ってしまいます。
上記を解決するために、nvidia-dockerはホストOSにGPUドライバを持たせ、コンテナ側にCUDA Toolkitを持たせ、コンテナイメージをドライバに依存しない形を実現しました。
▲ nvidia-docker利用イメージ図
出典 https://github.com/NVIDIA/nvidia-docker/tree/2.0
環境構築
nvidia-dockerを動作させるには、次の3つのコンポーネントをインストールする必要があります。
- GPUドライバ
- Docker
- nvidia-docker
今回の環境
項目 | 詳細 |
---|---|
クラウド | IDCFクラウド |
リージョンとゾーン | 東日本リージョン2 luxゾーン |
マシンタイプ | gpu.7XLM40 |
スペック | 56vCPU, 256GB RAM |
GPU | NVIDIA Tesla M40 × 1 |
OS | Ubuntu 16.04 LTS 64-bit |
CUDA Toolkit | 9.0 |
Docker | 17.09.0-ce |
nvidia-docker | 1.0.1 |
Dockerイメージ | nvidia/cuda:8.0-cudnn6-runtime-ubuntu16.04 |
nvidia-dockerを使う際、ホスト側のドライバのバージョン、コンテナのToolkitのバージョンとGPUのアーキテクチャの関係性についてはこちらのページをご参考ください。基本的にホスト側のドライバは新しいバージョンにしておくのが推奨です。コンテナ側のCUDA Toolkitは使うフレームワークやアプリケーションに依存すると思うので、その都度最適なバージョンを選択するのが良いです。今回は動かすTensorFlowのサンプルの関係で、ホスト側にはCUDA Toolkit 9.0を搭載させているのに対し、イメージでは8.0を選択しています。
1. GPUドライバのインストール
NVIDIA公式のダウンロードページより、GPUのタイプやOSが選択できます。
以下はホスト側での作業です。
OSアップデートを実施 # apt update && apt upgrade -y && reboot # apt-get install -y gcc # wget http://us.download.nvidia.com/tesla/384.81/nvidia-diag-driver-local-repo-ubuntu1604-384.81_1.0-1_amd64.deb # dpkg -i nvidia-diag-driver-local-repo-ubuntu1604-384.81_1.0-1_amd64.deb # apt-get update # apt-get install -y cuda-drivers # reboot
2. Docker環境インストール
Docker CEの公式ページの手順です。
以下はホスト側での作業です。
# apt-get install -y apt-transport-https ca-certificates curl software-properties-common # curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - # apt-key fingerprint 0EBFCD88 pub 4096R/0EBFCD88 2017-02-22 Key fingerprint = 9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88 uid Docker Release (CE deb) <docker@docker.com> sub 4096R/F273FCD8 2017-02-22 # add-apt-repository \ "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) \ stable" # apt-get update # apt-get install -y docker-ce テスト # docker run hello-world
3. nvidia-dockerインストール
現在はバージョン1.0が主流ですが、バージョン2.0もαテストとして公開されています。今回は1.0を使いますが、2.0のインストール方法についても軽く紹介します。
nvidia-docker 1.0.1
nvidia-dockerの公式Wikiです。
以下はホスト側での作業です。
# wget -P /tmp https://github.com/NVIDIA/nvidia-docker/releases/download/v1.0.1/nvidia-docker_1.0.1-1_amd64.deb # dpkg -i /tmp/nvidia-docker*.deb && rm /tmp/nvidia-docker*.deb GPUが正しく認識されていればインストール成功です # nvidia-docker run --rm nvidia/cuda nvidia-smi +-----------------------------------------------------------------------------+ | NVIDIA-SMI 384.81 Driver Version: 384.81 | |-------------------------------+----------------------+----------------------+ | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | |===============================+======================+======================| | 0 Tesla M40 24GB Off | 00000000:14:00.0 Off | 0 | | N/A 41C P0 57W / 250W | 11MiB / 22939MiB | 0% Default | +-------------------------------+----------------------+----------------------+
nvidia-docker 2.0
公式wikiを参考に、nvidia-docker 2.0のメリットを紹介します。
- Docker CLIのラッピングが不要になった (nvidia-dockerコマンドではなく、標準のdockerコマンドが使えるようになった)
- デーモンを新たにスタートさせる必要がなくなった
- GPUアイソレーションが環境変数NVIDIA_VISIBLE_DEVICESでできるようになった
- 公式のCUDAイメージだけでなく、すべてのDockerイメージからGPUを認識できるようになった
- CentOSとUbuntu向けにパッケージリポジトリが用意された
- libnvidia-containerに基づいた新たな実装方法になった
バージョン2.0をインストールする際は、バージョン1.0で起動しているすべてのコンテナを停止し、削除する必要があります。また、1.0のパッケージも完全に削除する必要があるのでご注意ください。
以下はホスト側での作業です。
リポジトリを入れる # curl -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add - # sudo tee /etc/apt/sources.list.d/nvidia-docker.list <<< \ "deb https://nvidia.github.io/libnvidia-container/ubuntu16.04/amd64 / deb https://nvidia.github.io/nvidia-container-runtime/ubuntu16.04/amd64 / deb https://nvidia.github.io/nvidia-docker/ubuntu16.04/amd64 /" # apt-get update # apt-get install -y nvidia-docker2 dockerコマンドでコンテナを起動する際にnvidia runtimeを指定します # docker run --runtime=nvidia --rm nvidia/cuda nvidia-smi +-----------------------------------------------------------------------------+ | NVIDIA-SMI 384.81 Driver Version: 384.81 | |-------------------------------+----------------------+----------------------+ | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | |===============================+======================+======================| | 0 Tesla M40 24GB Off | 00000000:14:00.0 Off | 0 | | N/A 38C P0 63W / 250W | 0MiB / 22939MiB | 100% Default | +-------------------------------+----------------------+----------------------+
サンプル実行
それでは、必要なソフトウェアが一通り揃ったので、試しにコンテナを起動してTensorFlowのCIFAR-10を動かしてみます。CIFAR-10は機械学習のなかでもよく使われるベンチマークの例題で、学習のアウトプットはRGB 32×32ピクセルの画像を10個のカテゴリ(飛行機、犬、猫など)に分類できるCNNです。
以下はホスト側での作業です。
コンテナ起動、NVIDIAが提供しているcuDNNがプリインストールされているイメージを使用 # nvidia-docker run --rm -i -t nvidia/cuda:8.0-cudnn6-runtime-ubuntu16.04 /bin/bash
以下はコンテナ内での操作です。
必要なパッケージをインストール # apt-get update # apt-get install python-pip python-dev python3-pip python3-dev git -y # pip3 install --upgrade pip # pip3 install tensorflow-gpu CIFAR-10のモデルを落とす # git clone https://github.com/tensorflow/models # cd models/tutorials/image/cifar10/ サンプル実行開始 # python3 cifar10_train.py | tee -a results.txt .... name: Tesla M40 24GB major: 5 minor: 2 memoryClockRate (GHz) 1.112 pciBusID 0000:14:00.0 Total memory: 22.40GiB Free memory: 22.29GiB ... 2017-10-23 09:30:33.384193: step 0, loss = 4.68 (256.9 examples/sec; 0.498 sec/batch) 2017-10-23 09:30:33.641038: step 10, loss = 4.64 (4983.5 examples/sec; 0.026 sec/batch) ... 2017-10-23 09:30:56.448989: step 1000, loss = 2.26 (3485.6 examples/sec; 0.037 sec/batch) 2017-10-23 09:30:56.667036: step 1010, loss = 2.48 (5870.3 examples/sec; 0.022 sec/batch) ... 2017-10-23 09:31:19.319086: step 2000, loss = 1.64 (3560.0 examples/sec; 0.036 sec/batch) 2017-10-23 09:31:19.545818: step 2010, loss = 1.62 (5645.4 examples/sec; 0.023 sec/batch) ... 2017-10-23 09:32:28.360640: step 5000, loss = 0.91 (3561.1 examples/sec; 0.036 sec/batch) 2017-10-23 09:32:28.582057: step 5010, loss = 1.09 (5781.0 examples/sec; 0.022 sec/batch)
ちゃんとコンテナからGPUを認識できており、学習ができているのがわかります。
イメージの保存
学習させた結果を他の環境に持っていくのにコンテナはとても便利です。ここでは、リポジトリを使うケースと使わないケース両方を紹介します。
リポジトリを使う場合
今回はDocker Hubを使用します。Docker Hubでのアカウント作成やリポジトリの作成はDocker Hubでできます。ちゃんとしたプロジェクト管理が必要な場合や複数人へ共有する必要があるときに向いています。プロジェクトによってはプライベートリポジトリをGitHubやGitLabで構築するという選択肢もあります。プライベートリポジトリについては別途紹介する記事を出したいと思ってます。
以下はホスト側での作業です。
Container IDを取得 # docker ps [Container ID]の部分は1530abdc1234などの番号で、[Repository]はidcf/pj1など、[:Tag]は:v1など # docker commit [Container ID] [Repository[:Tag]] イメージが保存されていればOK # docker images Docker Hubへログイン、そしてpush # docker login # docker push [Repository[:Tag]] 別ホストログインし、イメージをpull # docker login # docker pull [Repository[:Tag]]
リポジトリを使わない場合
リポジトリの用意が手間だったり、SCPなどでお手軽にイメージを移動したい場合は、リポジトリを使わなくても実施が可能です。なお、冒頭のイメージ作成の部分は、リポジトリを使うときの手順と同じです。
以下はホスト側での作業です。
コンテナIDを取得 # docker ps [Container ID]の部分は1234abcd1234などの番号で、[Repository]はidcf/pj1など、[:Tag]は:v1など # docker commit [Container ID] [Repository[:Tag]] イメージが保存されていればOK # docker images docker saveコマンドでtarファイルへ保存 # docker save -o [filename.tar] [Repository[:Tag]] SCPなどでファイルを転送 # scp [filename.tar] user@x.x.x.x:/path 別ホスト上でdocker loadコマンドでイメージをロード # docker load -i [filename.tar] イメージが保存されていればOK # docker images
まとめ
まだGPUをコンテナで利用されている事例は多くありませんが、今後はDeep Learningのさらなる加速により需要が増えてくるでしょう。
今回ブログを執筆した時点では、CUDA9とcuDNN7がリリースされて間も無かったため、TensorFlowのサンプルがCUDA8とcuDNN6にしか対応していなかったことが構築してから発覚しました。。が、ここでコンテナの出番です。exitして、もう一度別バージョンのイメージを指定して立ち上げるのみなので、"fire and forget"ができます。Deep Learningの手間な部分をほとんど解決してくれるのがnvidia-dockerで、今後のアップデートも楽しみです。
今後は複数GPUやApache Mesosを使ってのスケジューリングについても紹介していきたいと思いますので、お楽しみに!