TensorFlow on DockerでGPUを使えるようにする方法
2016/05/08
  • このエントリーをはてなブックマークに追加


どうも、おはこんばんちわ、カブクの足立です。

「カブクのエンジニアが普段何をしているのか」を明らかにする目的で始まったオフィシャルゆるブログ。
今回は、「TensorFlowでGPUが使えない」というエントリーに感動したので(失敗こそ大切な情報!)、私のTensorFlowの実行環境を構築する方法とその失敗・解決方法をご紹介します。

TensorFlowをGPUで動かす環境を作る

環境

以下の環境を構築します。

  • OS: Ubuntu 14.04.2
  • ビデオカード: NVIDIA GeForce GTX980Ti
  • Driver: nvidia-352
  • Library: CUDA Toolkit(v7.5.18), cuDNN(v7.0)
  • Framework: TensorFlow 0.8.0(Linux GPU版)
  • etc: Python2.7.6, Docker 1.10.2

ビデオカードのドライバをインストールする

$ sudo apt-get install software-properties-common
$ sudo add-apt-repository ppa:mamarley/nvidia

ドライバ名を調べます。
$ apt list | grep nvidia

わたしはこの中からnvidia-352-devというドライバをインストールしました(今時点で最新のドライバが良いと思います)。

$ sudo apt-get install nvidia-352-dev

TensorFlow?ではUVM(Unified Virtual Memory)も必要みたいなので同じバージョンのUVMもインストールしておきます。
※UVMの詳細説明はコチラが分かりやすかったです。

$ sudo apt-get install nvidia-352-uvm

インストールが完了すると再起動を促されますので従います。

$ sudo shutdown -r now

CUDA関連ライブラリのインストール

オフィシャルの情報にしたがってGPUを使うための環境を構築します。

CUDAを利用するためのライブラリCUDA Toolkit 7.5はTarget Platformを指定するとInstaller Typeに応じたファイル(debファイルなど)をダウンロードできます。
ネットワーク経由でインストールするdeb(local)というファイルをダウンロードします。
次のコマンドでインストールします。
$ sudo dpkg -i cuda-repo-ubuntu1404_7.5-18_amd64.deb
$ sudo apt-get update
$ sudo apt-get install cuda

環境変数を設定しておきます。
$ export CUDA_HOME=/usr/local/cuda
$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${CUDA_HOME}/lib64

次にCUDAを使ったDeepLearning向けライブラリcuDNNをインストールします。
こちらのライブラリをダウンロードするには、現時点ではNVIDIAに利用目的を申請し受領される必要があります。難しく考える必要はなく、研究・調査目的などでも受領されました。
ただ、申請してからダウンロードできるまでは1日ぐらい時間を要しましたので早めにコチラからダウンロードしておくことをおすすめします。

インストールはダウンロードしたファイルを所定の位置にコピーするだけです。
$ tar xvzf cudnn-7.5-linux-x64-v4.tgz
$ sudo cp cudnn-7.5-linux-x64-v4/cudnn.h /usr/local/cuda/include
$ sudo cp cudnn-7.5-linux-x64-v4/libcudnn* /usr/local/cuda/lib64
$ sudo chmod a+r /usr/local/cuda/lib64/libcudnn*

TensorFlowをインストール

あとはTensorFlowを入れるだけ。
$ sudo apt-get install python-pip python-dev
$ sudo pip install --upgrade https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow-0.8.0-cp27-none-linux_x86_64.whl

環境を切り分けておきたい場合はvirtualenvをお忘れなく。

動作環境の確認

全て正常にインストールされているかを確認するためPythonを起動しTensorFlowを読み込んでみます。
$ python
>>> import tensorflow as tf

以下のメッセージが表示されたらまずはCUDA Toolkitのインストールが正しく行えています。
I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcublas.so locally
I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcudnn.so locally
I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcufft.so locally
I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcuda.so.1 locally
I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcurand.so locally

つづいて
s = tf.Session()
というPythonコードを入力します。
以下のメッセージでエラーが表示されなければビデオカードのドライバのインストールも正常に完了していることが確認できたことになり、TensorFlowでGPUを使う準備が整いました。

I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:900] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
I tensorflow/core/common_runtime/gpu/gpu_init.cc:102] Found device 0 with properties:
name: GeForce GTX 980 Ti
major: 5 minor: 2 memoryClockRate (GHz) 1.076
pciBusID 0000:01:00.0
Total memory: 6.00GiB
Free memory: 5.84GiB
I tensorflow/core/common_runtime/gpu/gpu_init.cc:126] DMA: 0
I tensorflow/core/common_runtime/gpu/gpu_init.cc:136] 0:   Y
I tensorflow/core/common_runtime/gpu/gpu_device.cc:717] Creating TensorFlow device (/gpu:0) -> (device: 0, name: GeForce GTX 980 Ti, pci bus id: 0000:01:00.0)
I tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:51] Creating bin of max chunk size 1.0KiB
...(中略)...
I tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:51] Creating bin of max chunk size 8.00GiB

Docker環境を構築する

今後なにかの役に立つかと思い、DockerでもGPUを使えるようにしておきたかったので、その構築方法もご紹介します。

考え方はシンプルです。Dockerコンテナを起動する際にホストOSのdeviceを繋ぎ、ホストOSのCUDAライブラリを共有するだけです。

$ docker run --device /dev/nvidia0:/dev/nvidia0 \
             --device /dev/nvidiactl:/dev/nvidiactl \
             --device /dev/nvidia-uvm:/dev/nvidia-uvm \
             -v /usr/local/cuda/lib64:/usr/local/cuda/lib64 \
             -v /usr/lib/x86_64-linux-gnu/libcuda.so:/usr/lib/x86_64-linux-gnu/libcuda.so \
             -v /usr/lib/x86_64-linux-gnu/libcuda.so.1:/usr/lib/x86_64-linux-gnu/libcuda.so.1 \
             -v /usr/lib/x86_64-linux-gnu/libcuda.so.352.79:/usr/lib/x86_64-linux-gnu/libcuda.so.352.79 \
             -it ubuntu:14.04.2 /bin/bash

デバイスの接続とかライブラリの共有だとかいちいち面倒なので、私は以下のスクリプトを利用しています。

#!/bin/bash

IMAGE_NAME=ubuntu:14.04.2
SHELL=/bin/bash

CUDA_LIB="-v /usr/local/cuda/lib64:/usr/local/cuda/lib64"
CUDA_SO=$(\ls /usr/lib/x86_64-linux-gnu/libcuda* | xargs -I{} echo '-v {}:{}')
DEVICES=$(\ls /dev/nvidia* | xargs -I{} echo '--device {}:{}')

docker run $DEVICES $CUDA_LIB $CUDA_SO -it $IMAGE_NAME $SHELL

あとは、起動したDockerコンテナでCUDAライブラリなど環境変数を設定してTensorFlowをインストールすればDocker上でGPUを使ったTensorFlowを実行できます。

結局、この例のようにホストOSのデバイスやらライブラリを使い回すならDocker化する必要なく、virtualenvで十分です。
今後、CUDAライブラリのバージョンアップなどで動作検証が必要な状況になったら、ライブラリだけはDockerコンテナ側に持っていき、テストしてから環境を更新するという使い方を考えています。
ドライバだけはホストOSとコンテナでバージョンを合せる必要があるためホストOSのものを使うのが良いと思います。
※そもそもドライバをアップデートしたらマシンの再起動が必要なので止めざるをえないですし…

トラブルシューティング

ここに至るまでに様々なトラブルに見まわれたので、その症状と解決方法をご紹介します。

libcudart.so.7.0: cannot open shared object file

pythonを起動してimport tensorflowを実行した際にlibcudart.soが見つからないエラーが表示されることがあります。
>>> import tensorflow
Traceback (most recent call last):
...(中略)...
    _mod = imp.load_module('_pywrap_tensorflow', fp, pathname, description)
ImportError: libcudart.so.7.0: cannot open shared object file: No such file or directory
下記の2つを確認してみてください。

1. LD_LIBRARY_PATHにCUDA Toolkitのライブラリのパスを通し忘れ
2. TensorFlowとCUDA Toolkitのバージョン互換

1つめは下記を実行し忘れていないか確認してください。
$ export CUDA_HOME=/usr/local/cuda
$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${CUDA_HOME}/lib64

2つめはTensorFlowが求めるCUDA Toolkitのバージョンを正しくする必要があります。
例えばTensorFlow0.6.0の場合はCUDA Toolkit7.0で動作します(6.5や7.5では動きません)。
TensorFlowのバージョンを上げるなど適切なバージョン関係を構築し解決できます。

Couldn't open CUDA library libcuda.so

import tensorflowをした時に下記のようなlibcuda.so系が見つからないエラーが表示された場合。
>>>import tensorflow
I tensorflow/stream_executor/dso_loader.cc:99] Couldn't open CUDA library libcuda.so.1. LD_LIBRARY_PATH: /usr/local/cuda/lib64:

libcuda.soはマシン環境によって配置場所が違うことがあるようです。
libcuda.soがあるディレクトリをLD_LIBRARY_PATHに追加することで解決できました。

私の環境では/usr/lib/x86_64-linux-gnuがデフォルトでパスが通っているので、そこにlibcuda.soが配置されていなければこのエラーが起きます。

デフォルトパスの確認は。
$ more /etc/ld.so.conf.d/x86_64-linux-gnu.conf
# Multiarch support
/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu

マシン・OSに環境に応じて確認すべきファイル(x86_64-linux-gnu.confなど)が異なるので注意して下さい。

failed call to cuInit

import tensorflowがうまくいったとしても、まだ油断できません。
デバイスが正しく動作するかの検証が出来ていないからです。
TensorFlowではSessionを作るタイミングでデバイスに通信をするようなので、そこで初めて確認できます。
import tensorflow as tf
s = tf.Session()

ここでfailed call to cuInitというCUDAの初期化に失敗したようなエラーが表示されることがあります。
E tensorflow/stream_executor/cuda/cuda_driver.cc:481] failed call to cuInit: CUDA_ERROR_UNKNOWN

これはOSがデバイスモジュールを正しく認識できていない場合に発生しました。
下記の3つのファイルが存在するか、確認してください。
$ ls /dev/nvidia*
/dev/nvidia-uvm  /dev/nvidia0  /dev/nvidiactl

※ビデオカードが複数刺さっている場合はさらにファイルが多いかもしれません。

ビデオカード(nvidia0)やUnifided Virtual Memory(nvidia-uvm)が存在しない場合、下記のコマンドでNVIDIAのカーネルモジュールをロードできます。
$ nvidia-modprobe -u -c=0

コマンドオプションはオフィシャルサイトを参考にして下さい。

could not find cudnnConvolutionBackwardData_v2 in cudnn DSO

CUDA ToolkitのバージョンとcuDNNライブラリの互換性で問題が起きている場合にこのエラーが出ました。
例えばCUDA Toolkit7.5の場合cuDNNライブラリはv4(cudnn-7.0-linux-x64-v4.0-prod)で動作しましたがv5だと動作しませんでした。
このエラーは深掘りできていないので、どこで互換性の問題が出ているのか詳しく分かっていません…。

kernel version does not much DSO version

Dockerを利用している場合でホストOSのnvidiaドライバのバージョンとコンテナのnvidiaドライバのバージョンが一致していない場合TensorFlowのSessionを作成したタイミングで
kernel version 352.68 does not match DSO version 352.79 -- connnot find working devices in this configuration
というエラーが出ました。ホストOSとコンテナのnvidiaドライバのバージョンを一致させておく必要があるようです。
今回の環境構築方法ではホストOSのファイルをDockerコンテナ側に共有しているのでこのエラーは発生しないはずですが…

2017.02.01追記

TensorflowのバージョンとGPUドライバのバージョンにも互換性に関するトラブルがあります。
例えばTensorflow r1.0を下記のプログラムで動作確認をすると。

import tensorflow as tf
hello = tf.constant('Hello TensorFlow!')
sess = tf.Session()

以下のエラーメッセージが表示されます。これはTensorflowが期待するドライバーがインストールされていないことを意味します。
※正確にはTensroflowが利用するCuda Toolkitが利用するnVidia GPUドライバー

E tensorflow/stream_executor/cuda/cuda_diagnostics.cc:303 kernel version 367.57.0 does not match DSO version 361.93.2 -- cannot find working devices in this configuration

ドライバーをアップデートして解決できます。
上記の場合ではkernelのversion 367.57.0が求められているので、下記のコマンドでインストールしてマシンを再起動することで解決できます。

まずは、ドライバー名を調べる。
$ apt list | grep nvidia

nvidia-367というドライバが見つかるはず。
それをインストール。

$ sudo apt-get install nvidia-367

インストールが完了したら、マシンを再起動。

$ sudo shutdown -r now

GPUの最新の機能を利用する場合はどうしても最新のCuda Toolkitやドライバーが必要です。
TensorflowをはじめとするDeep Learningフレームワークは性能が大事なので、フレームワークのバージョンとドライバーのバージョンの互換性は気をつけたいところです。

以上が私のハマったエラーとその解決方法でした。
TensorFlowだけでなくCUDA自信やcuDNNなどのライブラリ、はたまたNVIDIA GPUのアーキテクチャの変更など変化が激しいので、互換性の問題はこれからもドンドンでてきそうなので、はまり次第、共有していこうと思います。

おわりに

改めて見返すと、一歩ずつ丁寧にハマっていった感がありますね。
そうこう言っているうちにCUDA Toolkit 8のリリースが近く、NVIDIA GPUの新アーキテクチャPascalもあり、動向がアクティブなので楽しみ楽しみ。