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もあり、動向がアクティブなので楽しみ楽しみ。

その他の記事

Other Articles

2019/05/20
[Web フロントエンド] 状態更新ロジックをフレームワークから独立させる

2019/04/16
C++のenable_shared_from_thisを使う

2019/04/12
OpenAPI 3 ファーストな Web アプリケーション開発(Python で API 編)

2019/04/08
WebGLでレイマーチングを使ったCSGを実現する

2019/04/02
『エンジニア採用最前線』に感化されて2週間でエンジニア主導の求人票更新フローを構築した話

2019/03/29
その1 Jetson TX2でk3s(枯山水)を動かしてみた

2019/03/27
任意のブラウザ上でJestで書いたテストを実行する

2019/02/08
TypeScript で “radian” と “degree” を間違えないようにする

2019/02/05
Python3でGoogle Cloud ML Engineをローカルで動作する方法

2019/01/18
SIGGRAPH Asia 2018 参加レポート

2019/01/08
お正月だョ!ECMAScript Proposal全員集合!!

2019/01/08
カブクエンジニア開発合宿に行ってきました 2018秋

2018/12/25
OpenAPI 3 ファーストな Web アプリケーション開発(環境編)

2018/12/23
いまMLKitカスタムモデル(TF Lite)は使えるのか

2018/12/21
[IoT] Docker on JetsonでMQTTを使ってCloud IoT Coreと通信する

2018/12/11
TypeScriptで実現する型安全な多言語対応(Angularを例に)

2018/12/05
GASでCompute Engineの時間に応じた自動停止/起動ツールを作成する 〜GASで簡単に好きなGoogle APIを叩く方法〜

2018/12/02
single quotes な Black を vendoring して packaging

2018/11/14
3次元データに2次元データの深層学習の技術(Inception V3, ResNet)を適用

2018/11/04
Node Knockout 2018 に参戦しました

2018/10/24
SIGGRAPH 2018参加レポート-後編(VR/AR)

2018/10/11
Angular 4アプリケーションをAngular 6に移行する

2018/10/05
SIGGRAPH 2018参加レポート-特別編(VR@50)

2018/10/03
Three.jsでVRしたい

2018/10/02
SIGGRAPH 2018参加レポート-前編

2018/09/27
ズーム可能なSVGを実装する方法の解説

2018/09/25
Kerasを用いた複数入力モデル精度向上のためのTips

2018/09/21
競技プログラミングの勉強会を開催している話

2018/09/19
Ladder Netwoksによる半教師あり学習

2018/08/10
「Maker Faire Tokyo 2018」に出展しました

2018/08/02
Kerasを用いた複数時系列データを1つの深層学習モデルで学習させる方法

2018/07/26
Apollo GraphQLでWebサービスを開発してわかったこと

2018/07/19
【深層学習】時系列データに対する1次元畳み込み層の出力を可視化

2018/07/11
きたない requirements.txt から Pipenv への移行

2018/06/26
CSS Houdiniを味見する

2018/06/25
不確実性を考慮した時系列データ予測

2018/06/20
Google Colaboratory を自分のマシンで走らせる

2018/06/18
Go言語でWebAssembly

2018/06/15
カブクエンジニア開発合宿に行ってきました 2018春

2018/06/08
2018 年の tree shaking

2018/06/07
隠れマルコフモデル 入門

2018/05/30
DASKによる探索的データ分析(EDA)

2018/05/10
TensorFlowをソースからビルドする方法とその効果

2018/04/23
EGLとOpenGLを使用するコードのビルド方法〜libGLからlibOpenGLへ

2018/04/23
技術書典4にサークル参加してきました

2018/04/13
Python で Cura をバッチ実行するためには

2018/04/04
ARCoreで3Dプリント風エフェクトを実現する〜呪文による積層造形映像制作の舞台裏〜

2018/04/02
深層学習を用いた時系列データにおける異常検知

2018/04/01
音声ユーザーインターフェースを用いた新方式積層造形装置の提案

2018/03/31
Container builderでコンテナイメージをBuildしてSlackで結果を受け取る開発スタイルが捗る

2018/03/23
ngUpgrade を使って AngularJS から Angular に移行

2018/03/14
Three.jsのパフォーマンスTips

2018/02/14
C++17の新機能を試す〜その1「3次元版hypot」

2018/01/17
時系列データにおける異常検知

2018/01/11
異常検知の基礎

2018/01/09
three.ar.jsを使ったスマホAR入門

2017/12/17
Python OpenAPIライブラリ bravado-core の発展的な使い方

2017/12/15
WebAssembly(wat)を手書きする

2017/12/14
AngularJS を Angular に移行: ng-annotate 相当の機能を TypeScrpt ファイルに適用

2017/12/08
Android Thingsで4足ロボットを作る ~ Android ThingsとPCA9685でサーボ制御)

2017/12/06
Raspberry PIとDialogflow & Google Cloud Platformを利用した、3Dプリンターボット(仮)の開発 (概要編)

2017/11/20
カブクエンジニア開発合宿に行ってきました 2017秋

2017/10/19
Android Thingsを使って3Dプリント戦車を作ろう ① ハードウェア準備編

2017/10/13
第2回 魁!! GPUクラスタ on GKE ~PodからGPUを使う編~

2017/10/05
第1回 魁!! GPUクラスタ on GKE ~GPUクラスタ構築編~

2017/09/13
「Maker Faire Tokyo 2017」に出展しました。

2017/09/11
PyConJP2017に参加しました

2017/09/08
bravado-coreによるOpenAPIを利用したPythonアプリケーション開発

2017/08/23
OpenAPIのご紹介

2017/08/18
EuroPython2017で2名登壇しました。

2017/07/26
3DプリンターでLチカ

2017/07/03
Three.js r86で何が変わったのか

2017/06/21
3次元データへの深層学習の適用

2017/06/01
カブクエンジニア開発合宿に行ってきました 2017春

2017/05/08
Three.js r85で何が変わったのか

2017/04/10
GCPのGPUインスタンスでレンダリングを高速化

2017/02/07
Three.js r84で何が変わったのか

2017/01/27
Google App EngineのFlexible EnvironmentにTmpfsを導入する

2016/12/21
Three.js r83で何が変わったのか

2016/12/02
Three.jsでのクリッピング平面の利用

2016/11/08
Three.js r82で何が変わったのか

2016/12/17
SIGGRAPH 2016 レポート

2016/11/02
カブクエンジニア開発合宿に行ってきました 2016秋

2016/10/28
PyConJP2016 行きました

2016/10/17
EuroPython2016で登壇しました

2016/10/13
Angular 2.0.0ファイナルへのアップグレード

2016/10/04
Three.js r81で何が変わったのか

2016/09/14
カブクのエンジニアインターンシッププログラムについての詩

2016/09/05
カブクのエンジニアインターンとして3ヶ月でやった事 〜高橋知成の場合〜

2016/08/30
Three.js r80で何が変わったのか

2016/07/15
Three.js r79で何が変わったのか

2016/06/02
Vulkanを試してみた

2016/05/20
MakerGoの作り方

2016/04/27
Blenderの3DデータをMinecraftに送りこむ

2016/04/20
Tensorflowを使ったDeep LearningにおけるGPU性能調査

→
←

関連職種

Recruit

バックエンドエンジニア(Python・Go)

業務内容

当ポジションは弊社Webサービスのバックエンド機能設計及び実装を担当します。 サービス毎の開発チームで2週間スプリントのスクラム開発を実施しています。 週次で開発チームミーティングを実施し、実装設計の相談や工数見積もりを行います。 全ての開発コードはレビューと自動テストによって品質を保っています。 また、リファクタリングやフレームワークのバージョンアップも開発フローに組込み、技術的負債を放置しない開発を目指しています。

フロントエンドエンジニア(TypeScript)

業務内容

当ポジションは弊社Webサービスのフロントエンド機能設計及び実装を担当します。 サービス毎の開発チームで2週間スプリントのスクラム開発を実施しています。 週次で開発チームミーティングを実施し、実装設計の相談や工数見積もりを行います。 全ての開発コードはレビューと自動テストによって品質を保っています。 また、リファクタリングやフレームワークのバージョンアップも開発フローに組込み、技術的負債を放置しない開発を目指しています。

機械学習エンジニア

業務内容

センサーデータの分析モデルの調査・研究・開発。 Kabuku Connectの製造データ(3D、2D)から情報を抽出するモデルの構築。 データの前処理や学習、ハイパーパラメータチューニング、獲得モデルの評価、プロダクションのデータパイプラインとの連携をお願いします。

インターン(Webエンジニア)

業務内容

業務から独立した、調査・研究系のタスクをおまかせしています。コードレビュー、 社内での報告会、 ブログ記事執筆を通して着実にスキルアップしていただくことを目指しています。 (希望があれば、プロダクトの開発業務もおまかせします。)

→
←

お客様のご要望に「Kabuku」はお応えいたします。
ぜひお気軽にご相談ください。

お電話でも受け付けております
03-6380-2750
営業時間:09:30~18:00
※土日祝は除く