はじめに
Debian GNU/Linux Stretch 環境に backports から CUDA9.11 をインストールして、コンパイル済みの TensorFlow パッケージを利用しようと、以下のコマンドを実行すると「libcublas.so.9.0 が見つかりませんよ」というエラーになりました。
$ pip3 install --user tensorflow-gpu
$ python3 train.py
(中略)
ImportError: libcublas.so.9.0: cannot open shared object file: No such file or directory
なるほど、CUDA9.1 向けに TensorFlow をビルドしなければならないのだなと軽く考えたのが、運の尽き。それがこんな泥沼の1週間になろうとは…
前提となる環境
- Debian GNU/Linux Stretch を使っています。
- CUDA は backports.debian.org から CUDA9.1 をインストールしています。
- cuDNN や NCCL は、NVidia が公開している Ubuntu 用のパッケージをインストールしています。
正確な環境設定手順は、https://gist.github.com/tsuchm/bf61370ccb2fac639c811d77d0c61fa7 で公開している Ansible Playbook の通りです。
基本的な手順
手順は、公式ページで解説されている通り…のはずなのですが、後述のようにトラブルシュートが必要になります。
bazel のインストール
最初に、bazel をインストールします。ただし、最新の 0.19.1 を使って bazel build
すると、以下のようなエラーになります。
ERROR: Config value cuda is not defined in any .rc file
このエラーを回避するには、https://stackoverflow.com/questions/52041661/geting-error-config-value-cuda-is-not-defined-in-any-rc-file-when-trying-to-tr に説明されている通り、0.16 よりも以前のバージョン(本稿では 0.15.2 を使用)をインストールする必要があります。
依存パッケージのインストール
ビルド時に必要なパッケージをインストールしておきます。
$ sudo apt install python3-numpy python3-pip python3-six python3-all-dev python3-wheel swig
$ pip3 install --user --upgrade mock
$ pip3 install --user --upgrade keras_applications==1.0.5 --no-deps
$ pip3 install --user --upgrade keras_preprocessing==1.0.3 --no-deps
TensorFlow のソースをダウンロード・ビルド
TensorFlow のソースをダウンロードします。2018年11月現在の最新リリース版は 1.12 系列ですから、1.12 系列のブランチの最新版をビルドするために、ブランチを移動します。
$ git clone https://github.com/tensorflow/tensorflow.git
$ cd tensorflow
$ git checkout r1.12
$ ./configure
configure
スクリプトを実行して、設定ファイル .tf_configure.bazelrc
を生成します。注意点としては、
- Python のバイナリを
/usr/bin/python3
と明示的に指定しないと、Python2 用にビルドされてしまいます。 - 2018年11月現在の Debian GNU/Linux Stretch + backports では CUDA9.1 が
/usr
以下にインストールされていますから、そのように指定します。
このコマンドの結果、以下のような設定ファイルが生成されます。
build --action_env PYTHON_BIN_PATH="/usr/bin/python3"
build --action_env PYTHON_LIB_PATH="/usr/lib/python3/dist-packages"
build --python_path="/usr/bin/python3"
build --define with_ignite_support=true
build --define with_xla_support=true
build --action_env TF_NEED_OPENCL_SYCL="0"
build --action_env TF_NEED_ROCM="0"
build --action_env TF_NEED_CUDA="1"
build --action_env CUDA_TOOLKIT_PATH="/usr"
build --action_env TF_CUDA_VERSION="9.1"
build --action_env CUDNN_INSTALL_PATH="/usr/lib/x86_64-linux-gnu"
build --action_env TF_CUDNN_VERSION="7"
build --action_env NCCL_INSTALL_PATH="/usr/lib/x86_64-linux-gnu"
build --action_env NCCL_HDR_PATH="/usr/include"
build --action_env TF_NCCL_VERSION="2"
build --action_env TF_CUDA_COMPUTE_CAPABILITIES="3.5,7.0"
build --action_env TF_CUDA_CLANG="0"
build --action_env GCC_HOST_COMPILER_PATH="/usr/bin/x86_64-linux-gnu-gcc-6"
build --config=cuda
test --config=cuda
build:opt --copt=-march=x86-64
build:opt --copt=-O3
build:opt --host_copt=-march=native
build:opt --define with_default_optimizations=true
build:v2 --define=tf_api_version=2
次に、以下のコマンドで、TensorFlow 全体をビルドします。
$ bazel build --config=opt --verbose_failures //tensorflow/tools/pip_package:build_pip_package
なお、.tf_configure.bazelrc
に build --config=cuda
という指定があるので、bazel コマンドの実行時には --config=cuda
オプションを指定しなくとも、GPU バージョンがビルドされるはずです。
次に、以下のコマンドで、/tmp/tensorflow_pkg/
配下に pip パッケージを作成します。
$ ./bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg
最後に、以下のコマンドで、作成された pip パッケージをインストールします。
$cd pip3 install --user /tmp/tensorflow_pkg/tensorflow-1.12.0-cp35-cp35m-linux_x86_64.whl
トラブルシュート
TensorFlow は、ビルドスクリプト内部で protobuf などのライブラリをダウンロード・ビルドしています。そして、このライブラリと、システム側にインストールしているライブラリのバージョンが 完全に一致 していないと各種のトラブルの原因になります。通常であれば、インストール対象で使われているバージョンよりも新しいバージョンがシステムにインストールされていれば良いことが大半なのですが、TensorFlow のビルドプロセスでは、 完全に一致 している必要があります。
基本的には、workspace.bzl
というファイルに、バージョンやダウンロード URL が記述されていますので、これを逐一確認していくことになります。
protobuf のバージョン
protobuf のバージョンが一致していないと、以下のようなエラーでビルドに失敗します。これは、https://stackoverflow.com/questions/46235376/tensorflow-protobuf-version-mismatch で紹介されている問題と同じです。
bazel-out/k8-opt/genfiles/tensorflow/core/framework/types.pb.h:17:2: error: #error This file was generated by an older version of protoc which is
bazel-out/k8-opt/genfiles/tensorflow/core/framework/types.pb.h:18:2: error: #error incompatible with your Protocol Buffer headers. Please
bazel-out/k8-opt/genfiles/tensorflow/core/framework/types.pb.h:19:2: error: #error regenerate this file with a newer version of protoc.
システムにインストールされている protobuf のバージョンは、以下のコマンドで確認できます。
$ dpkg -l libprotobuf-dev
要望=(U)不明/(I)インストール/(R)削除/(P)完全削除/(H)保持
| 状態=(N)無/(I)インストール済/(C)設定/(U)展開/(F)設定失敗/(H)半インストール/(W)トリガ待ち/(T)トリガ保留
|/ エラー?=(空欄)無/(R)要再インストール (状態,エラーの大文字=異常)
||/ 名前 バージョン アーキテクチャ 説明
+++-=====================================================-===============================-===============================-================================================================================================================
ii libprotobuf-dev:amd64 3.6.1-4~bpo9+1 amd64 protocol buffers C++ library (development files) and proto files
それに対して、tensorflow/workspace.bzl
中の protobuf をダウンロードしている箇所を確認すると、以下のように 3.6.0 が指定されています。
PROTOBUF_URLS = [
"https://mirror.bazel.build/github.com/google/protobuf/archive/v3.6.0.tar.gz",
"https://github.com/google/protobuf/archive/v3.6.0.tar.gz",
]
PROTOBUF_SHA256 = "50a5753995b3142627ac55cfd496cebc418a2e575ca0236e29033c67bd5665f4"
PROTOBUF_STRIP_PREFIX = "protobuf-3.6.0"
そこで、3.6.1 をダウンロードするように tensorflow/workspace.bzl
の URL と SHA256SUM などを書き換えます。
--- a/tensorflow/workspace.bzl
+++ b/tensorflow/workspace.bzl
@@ -381,11 +381,11 @@ def tf_workspace(path_prefix = "", tf_repo_name = ""):
)
PROTOBUF_URLS = [
- "https://mirror.bazel.build/github.com/google/protobuf/archive/v3.6.0.tar.gz",
- "https://github.com/google/protobuf/archive/v3.6.0.tar.gz",
+ "https://github.com/protocolbuffers/protobuf/archive/v3.6.1.tar.gz",
+ "https://github.com/protocolbuffers/protobuf/archive/v3.6.1.tar.gz",
]
- PROTOBUF_SHA256 = "50a5753995b3142627ac55cfd496cebc418a2e575ca0236e29033c67bd5665f4"
- PROTOBUF_STRIP_PREFIX = "protobuf-3.6.0"
+ PROTOBUF_SHA256 = "3d4e589d81b2006ca603c1ab712c9715a76227293032d05b26fca603f90b3f5b"
+ PROTOBUF_STRIP_PREFIX = "protobuf-3.6.1"
tf_http_archive(
name = "protobuf_archive",
なお、URL は、たとえ同一であっても、2回以上指定しておく必要があります。
なお、Debian GNU/Linux Buster 環境で Python3.7 向けにビルドしようとすると、以下のようにコンパイルエラーが発生します。
external/protobuf_archive/python/google/protobuf/pyext/extension_dict.cc: In function 'PyObject* google::protobuf::python::extension_dict::_FindExtensionByName(google::protobuf::python::ExtensionDict*, PyObject*)':
external/protobuf_archive/python/google/protobuf/pyext/extension_dict.cc:56:45: error: invalid conversion from 'const char*' to 'char*' [-fpermissive]
((*(charpp) = PyUnicode_AsUTF8AndSize(ob, (sizep))) == NULL? -1: 0): \
~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~
external/protobuf_archive/python/google/protobuf/pyext/extension_dict.cc:184:7: note: in expansion of macro 'PyString_AsStringAndSize'
if (PyString_AsStringAndSize(arg, &name, &name_size) < 0) {
^~~~~~~~~~~~~~~~~~~~~~~~
おそらく protobuf-3.6.1 が Python3.7 と非互換なのだろうと思われます。https://gist.github.com/tsuchm/8d54fdabb2ba760da91fa67ca5842a2d に記載の通り、Debian GNU/Linux Buster で使われている protobuf-3.6.1.3 を使ってビルドするように指定すれば、回避できます。
ICU (International Components for Unicode) のバージョン
ICU のバージョンが一致していないと、以下のようなエラーでビルドに失敗します。
ImportError: /home/user/.cache/bazel/_bazel_tsuchiya/fb08a882423ce1af8fd909db27d4a772/execroot/org_tensorflow/bazel-out/host/bin/tensorflow/create_tensorflow.python_api_1.runfiles/org_tensorflow/tensorflow/python/_pywrap_tensorflow_internal.so: undefined symbol: uscript_getScript_57
システムにインストールされている ICU のバージョンは、以下のコマンドで確認できます。
$ dpkg -l icu-devtools
要望=(U)不明/(I)インストール/(R)削除/(P)完全削除/(H)保持
| 状態=(N)無/(I)インストール済/(C)設定/(U)展開/(F)設定失敗/(H)半インストール/(W)トリガ待ち/(T)トリガ保留
|/ エラー?=(空欄)無/(R)要再インストール (状態,エラーの大文字=異常)
||/ 名前 バージョン アーキテクチャ 説明
+++-=====================================================-===============================-===============================-================================================================================================================
ii icu-devtools 57.1-6+deb9u2 amd64 Development utilities for International Components for Unicode
そこで、システムで使っているバージョンの ICU を使うように、third_party/icu/workspace.bzl
を以下のように書き換えます。
--- a/third_party/icu/workspace.bzl
+++ b/third_party/icu/workspace.bzl
@@ -5,11 +5,11 @@ load("//third_party:repo.bzl", "third_party_http_archive")
def repo():
third_party_http_archive(
name = "icu",
- strip_prefix = "icu-release-62-1",
- sha256 = "e15ffd84606323cbad5515bf9ecdf8061cc3bf80fb883b9e6aa162e485aa9761",
+ strip_prefix = "icu-release-57-1",
+ sha256 = "f86486f6825fdb01da28349a3d956db409e297dc2f15144c2456cfa0c6f8cd8d",
urls = [
- "https://mirror.bazel.build/github.com/unicode-org/icu/archive/release-62-1.tar.gz",
- "https://github.com/unicode-org/icu/archive/release-62-1.tar.gz",
+ "https://mirror.bazel.build/github.com/unicode-org/icu/archive/release-57-1.tar.gz",
+ "https://github.com/unicode-org/icu/archive/release-57-1.tar.gz",
],
build_file = "//third_party/icu:BUILD.bazel",
)
コンパイラのバージョン
公式ページに説明されている通り、公式には GCC 4.8 でビルドが確認されています。そのため、以下のように .tf_configure.bazelrc
に指定して、GCC 4.8 を使うと安全でしょう。
build --action_env GCC_HOST_COMPILER_PATH="/usr/bin/x86_64-linux-gnu-gcc-4.8"
GCC 6 を使ってビルドしていて PyString が未定義であるなどのエラーが出た場合には、--cxxopt
オプションが役立つかもしれません。
$ bazel build --config=opt --verbose_failures //tensorflow/tools/pip_package:build_pip_package --cxxopt="-D_GLIBCXX_USE_CXX11_ABI=0"
Quota Exceeded
TensorFlow は protobuf や各種の周辺ライブラリを全て、一度ダウンロードして展開・ビルドします。そのため、~/.cache/bazel/
というディレクトリ配下にかなりの容量(約18GB)を必要とします。quota がかかっている共用マシンなどでは、quota exceeded で中断する場合があります。回避するには、環境変数 TEST_TMPDIR
または --output-base
オプションに、十分な空き容量があるディレクトリを指定しましょう。
TensorFlow が GPU を使っているかチェック
以下のような listgpu.py
というファイルを用意して実行します。
from tensorflow.python.client import device_lib
device_lib.list_local_devices()
$ python3 listgpu.py
これで GPU がきちんと列挙されなければ、少なくとも TensorFlow から GPU が見えていません。
-
backports を利用しない Debian GNU/Linux Stretch 環境では、CUDA は 8.0 になります。CUDA8.0 に対応した TensorFlow は相当古いバージョンになるため、今回動かそうとしているプログラムは動きませんでした。なお、Debian GNU/Linux Buster 環境であれば CUDA9.2 になりますから、基本的には、このページの手順でコンパイル可能です。 ↩