LoginSignup
3
4

More than 3 years have passed since last update.

Debian GNU/Linux Stretch + CUDA9.1 の環境で TensorFlow 1.2 をビルドする

Last updated at Posted at 2018-11-17

はじめに

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 以下にインストールされていますから、そのように指定します。

このコマンドの結果、以下のような設定ファイルが生成されます。

.tf_configure.bazelrc
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.bazelrcbuild --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 を使うと安全でしょう。

.tf_configure.bazelrc
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 というファイルを用意して実行します。

listgpu.py
from tensorflow.python.client import device_lib
device_lib.list_local_devices()
$ python3 listgpu.py

これで GPU がきちんと列挙されなければ、少なくとも TensorFlow から GPU が見えていません。


  1. backports を利用しない Debian GNU/Linux Stretch 環境では、CUDA は 8.0 になります。CUDA8.0 に対応した TensorFlow は相当古いバージョンになるため、今回動かそうとしているプログラムは動きませんでした。なお、Debian GNU/Linux Buster 環境であれば CUDA9.2 になりますから、基本的には、このページの手順でコンパイル可能です。 

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4