- 投稿日:2021-01-29T17:47:58+09:00
最新のTensorFlow 2(GPU版)をCentOS7上で極力root権限なしにソースからビルド&インストールする
ネット上にはCentOS7かつ最新のTensorflow 2(GPU対応版)のインストール方法手順が書かれた記事がほとんどなかったので書いてみる。
Ubuntu 20と違ってCentOS 7.8にはお手軽にtensorflow-gpuを入れる手段がないので、ソースからビルドを行う。あとついでにgccやpythonもソースからビルドしてインストールする。
かなり難しいインストール作業で、トータルで5時間くらいかかる。
参考資料
- tensorflow 公式ソースビルドガイド
- How to install the latest version of Tensorflow (2.3.0) on a machine where the default gcc is too old or too new and you don't have root access これがかなり参考になった。ちょっとミスがあるので修正が必要。
マシン環境
- CentOS 7.8.2003
- GPU: NVIDIA RTX 3090 2枚差し (CUDA 11.1.105)
以下ではTensorFlow2のソースからのビルド&インストールに必要となるソフトウェア類gcc, python, javaSDK, bazelを
/home/apps
以下に置いて適宜利用していくことにする。CUDA 11.1, cudnn 8, CUDA SDK
root権限を持っている場合はCUDA Toolkit 11.1を普通にインストールすればよい。rootを持っていない場合はcudaのインストーラでrunfileを使えばインストール先をホームディレクトリ以下に指定できるはず。cudnn 8も同様に公式サイトからダウンロードする。これについてはGPU版を使う上で避けて通れないので、なんらかの形でインストールされていることを前提にする。
参考:https://stackoverflow.com/questions/39379792/install-cuda-without-root。
gcc 8.4.0
CentOS7の
/usr/bin/gcc
にデフォルトで存在するgcc 4.8.5は古すぎて使いたくなかったので(パフォーマンスも落ちる?)、ソースからビルドしたgcc 8.4.0を使う。gccnum="8.4.0" wget https://ftp.gnu.org/gnu/gcc/gcc-${gccnum}/gcc-${gccnum}.tar.xz tar Jxvf gcc-${gccnum}.tar.xz cd gcc-${gccnum} ./contrib/download_prerequisites ./configure --with-system-zlib --disable-multilib --disable-bootstrap --enable-languages=c,c++,fortran --prefix=/home/apps/compiler/gcc/${gccnum} make -j8 && make install
/home/apps/compiler/gcc/8.4.0
以下にインストールされた。python 3.8.7
module load gcc/8.4.0 wget https://www.python.org/ftp/python/3.8.7/Python-3.8.7.tar.xz tar xf Python-3.8.7.tar.xz cd Python-3.8.7 ./configure --prefix=/home/apps/python/3.8.7 --enable-ipv6 --enable-loadable-sqlite-extensions --with-system-ffi --enable-optimizations make -j8 altinstallオプションはHomebrewで使われているものを流用したが、実質
--enable-optimizations
以外はあまり関係ないと思われる。/home/apps/compiler/python/3.8.7
以下にインストールされた。Java SDK 15.0.2
bazelのビルドに必要らしい。
_linux-x64_bin.tar.gz
のものをダウンロードする。以下ではwget
でとってこようとしているが、素直にブラウザを使って公式ページ上からJava SDKをダウンロードしてきたほうがいい。# wget https://download.oracle.com/otn-pub/java/jdk/15.0.2+7/hogehogehoghoge/jdk-15.0.2_linux-x64_bin.tar.gz tar zxvf jdk-15.0.2_linux-x64_bin.tar.gz mv jdk-15.0.2 /home/apps export JAVA_HOME=/home/apps/jdk-15.0.2/
/home/apps/jdk-15.0.2/
以下にインストールし、環境変数JAVA_HOME
を設定しておく。bazel 3.1.0
バイナリ版のインストーラもあるけれど、あれだとうまくいかないみたいなので仕方なくソースビルドすることにする。ちなみに元GitHubのマスター(2.4.2以降も?)ではbazelの要求バージョンが3.7.2になっているかもしれないので、その場合はダウンロードするbazelのバージョン番号を変更して以下の操作を行う。
wget https://github.com/bazelbuild/bazel/releases/download/3.1.0/bazel-3.1.0-dist.zip unzip bazel-3.1.0-dist.zip -d bazel-3.1.0 cd bazel-3.1.0ここで、
third_party/grpc/bazel/generate_cc.bzl
ファイルに対して https://github.com/bazelbuild/bazel/pull/11860/files にかかれてあるように、use_default_shell_env = True,
の一行を入れて修正する。終わったらコンパイルを行う。# さっき入れたgcc 8.4.0をGCCROOTに設定する GCCROOT=/home/apps/compiler/gcc/8.4.0 export GCC_HOST_COMPILER_PATH=$GCCROOT/bin/gcc export GCC_HOST_COMPILER_PREFIX=$GCCROOT/bin export CXX=$GCCROOT/bin/gcc export CC=$GCCROOT/bin/gcc export LD_LIBRARY_PATH=$GCCROOT/lib64 export LDFLAGS="-L$GCCROOT/lib -L$GCCROOT/lib64" export CXXFLAGS="-L$GCCROOT/lib -L$GCCROOT/lib64" export JAVA_HOME=/home/apps/jdk-15.0.2/ env BAZEL_LINKOPTS=-static-libstdc++:-static-libgcc BAZEL_LINKLIBS=-l%:libstdc++.a:-lm bash ./compile.shコンパイル時にBAZELのリンクオプションを付けておく。でき上がったbazelを
/home/apps/bazel/3.1.0/bin
に置くmkdir -p /home/apps/bazel/3.1.0/bin cp output/bazel /home/apps/bazel/3.1.0/binbintools
gcc 8.4.0のbinディレクトリには
gcc-ar
があってもar
がないので、bintoolsを入れることで解決できる(ような気がする)wget https://ftp.gnu.org/gnu/binutils/binutils-2.36.tar.gz tar zxvf binutils-2.36.tar.gz cd binutils-2.36 ./configure --prefix=/home/apps/binutils/2.36 make -j 16 MAKEINFO=true make install MAKEINFO=true終わったら、先程インストールしたgcc 8.4.0のbinディレクトリに一時的にこのbinutilsでインストールした
ar
のシンボリックリンクを貼っておくln -s /home/apps/binutils/2.36/bin/ar /home/apps/compiler/gcc/8.4.0/bin/arこうすることで後のtensorflowのbazel build処理中のリンク処理がうまくいった。
tensorflow 2.4.1
まずtensorflow 2.4.1をダウンロードする。
# tensorflow 2.4.1のダウンロード $ git clone https://github.com/tensorflow/tensorflow.git -b v2.4.1 --recursive --depth 1Tensorflowのコンパイル時のエラーを解決するためのワークアラウンド(応急処置回避策)を以下に示す。
protocが正しくないバージョンのstdc++にリンクされてしまう問題を次のようにして解決する。
third_party/gpus/crosstool/cc_toolchain_config.bzl.tpl
のファイルの543行目を以下のように変更する- flag_group(flags = ["-lc++" if cpu == "darwin" else "-lstdc++"]), + flag_group(flags = ["-lc++" if cpu == "darwin" else "-l:libstdc++.a"]),
third_party/gpus/crosstool/cc_toolchain_config.bzl.tpl
ファイルの241行目あたり、objcopyが上でインストールしたbinutilsのものを使うように変更する。def _tool_paths(cpu, ctx): if cpu in ["local", "darwin"]: return [ tool_path(name = "gcc", path = ctx.attr.host_compiler_path), tool_path(name = "ar", path = ctx.attr.host_compiler_prefix + ( "/ar" if cpu == "local" else "/libtool" )), tool_path(name = "compat-ld", path = ctx.attr.host_compiler_prefix + "/ld"), tool_path(name = "cpp", path = ctx.attr.host_compiler_prefix + "/cpp"), tool_path(name = "dwp", path = ctx.attr.host_compiler_prefix + "/dwp"), tool_path(name = "gcov", path = ctx.attr.host_compiler_prefix + "/gcov"), tool_path(name = "ld", path = ctx.attr.host_compiler_prefix + "/ld"), tool_path(name = "nm", path = ctx.attr.host_compiler_prefix + "/nm"), - tool_path(name = "objcopy", path = ctx.attr.host_compiler_prefix + "/objcopy"), + tool_path(name = "objcopy", path = "/home/apps/binutils/2.36/bin/objcopy"), tool_path(name = "objdump", path = ctx.attr.host_compiler_prefix + "/objdump"), tool_path(name = "strip", path = ctx.attr.host_compiler_prefix + "/strip"), ]システムデフォルトのgcc(
/bin/gcc
)を使わない場合(例えば上のように/home/apps/
以下にインストールしたgcc 8.4.0を使おうとしているとき)、TensorFlow 2.4.1では以下の修正をthird_party/flatbuffers/build_defs.bzl
に適用しておく。将来的に2.4.2以降は修正されているはず。
https://github.com/tensorflow/tensorflow/pull/46550/files/60aea05061a01b18ff02582341ab374abf253437
./configure
での設定に進む前に、gcc, python3.8, bazelへのPATHを通しておく。$ export PATH="/home/apps/bazel/3.1.0/bin:$PATH" $ echo $PATH /home/apps/bazel/3.1.0/bin:/home/apps/python/3.8.7/bin:/home/apps/compiler/gcc/8.4.0/bin:/home/yoshitakam/.local/bin:/usr/bin:/home/moriwaki/bin:/usr/local/sbin:/usr/sbin
/home/apps/bazel/3.1.0/bin:/home/apps/python/3.8.7/bin:/home/apps/compiler/gcc/8.4.0/bin
が入っていることを確認する(順不同) 終わったら./configure
での設定へ。# configureでbazel buildのための設定ファイルを作る $ ./configure You have bazel 3.1.0- (@non-git) installed. Please specify the location of python. [Default is /bin/python3]: /home/apps/python/3.8.7/bin/python3.8 Found possible Python library paths: /home/apps/python/3.8.7/lib/python3.8/site-packages Please input the desired Python library path to use. Default is [/home/apps/python/3.8.7/lib/python3.8/site-packages] Do you wish to build TensorFlow with ROCm support? [y/N]: N No ROCm support will be enabled for TensorFlow. Do you wish to build TensorFlow with CUDA support? [y/N]: y CUDA support will be enabled for TensorFlow. Do you wish to build TensorFlow with TensorRT support? [y/N]: N No TensorRT support will be enabled for TensorFlow. Found CUDA 11.1 in: /usr/local/cuda-11.1/targets/x86_64-linux/lib /usr/local/cuda-11.1/targets/x86_64-linux/include Found cuDNN 8 in: /usr/local/cuda-11.1/targets/x86_64-linux/lib /usr/local/cuda-11.1/targets/x86_64-linux/include Please specify a list of comma-separated CUDA compute capabilities you want to build with. You can find the compute capability of your device at: https://developer.nvidia.com/cuda-gpus. Each capability can be specified as "x.y" or "compute_xy" to include both virtual and binary GPU code, or as "sm_xy" to only include the binary code. Please note that each additional compute capability significantly increases your build time and binary size, and that TensorFlow only supports compute capabilities >= 3.5 [Default is: 3.5,7.0]: 8.6 Do you want to use clang as CUDA compiler? [y/N]: N nvcc will be used as CUDA compiler. Please specify which gcc should be used by nvcc as the host compiler. [Default is /home/apps/compiler/gcc/8.4.0/bin/gcc]: Please specify optimization flags to use during compilation when bazel option "--config=opt" is specified [Default is -Wno-sign-compare]: Would you like to interactively configure ./WORKSPACE for Android builds? [y/N]: N Not configuring the WORKSPACE for Android builds. Preconfigured Bazel build configs. You can use any of the below by adding "--config=<>" to your build command. See .bazelrc for more details. --config=mkl # Build with MKL support. --config=mkl_aarch64 # Build with oneDNN support for Aarch64. --config=monolithic # Config for mostly static monolithic build. --config=ngraph # Build with Intel nGraph support. --config=numa # Build with NUMA support. --config=dynamic_kernels # (Experimental) Build kernels into separate shared objects. --config=v2 # Build TensorFlow 2.x instead of 1.x. Preconfigured Bazel build configs to DISABLE default on features: --config=noaws # Disable AWS S3 filesystem support. --config=nogcp # Disable GCP support. --config=nohdfs # Disable HDFS support. --config=nonccl # Disable NVIDIA NCCL support. Configuration finished
CUDA compute capabilities
のところで入力する数字は https://developer.nvidia.com/cuda-gpus ここで確認する。RTX3090の場合は8.6になる。
./configure
が終わったら、公式ガイドに従って次にbazel build
を行うが、このとき以下のようなリンクオプションをたくさん付けておく。また、cudaを使いたい場合は必ず--config=cuda
をつける。gcc-5以降の互換性を保ちたい場合は--cxxopt="-D_GLIBCXX_USE_CXX11_ABI=0"
をつける。env BAZEL_LINKOPTS=-static-libstdc++:-static-libgcc BAZEL_LINKLIBS=-l%:libstdc++.a:-lm BAZEL_CXXOPTS=-std=gnu++0x bazel build --config=opt --config=cuda --cxxopt="-D_GLIBCXX_USE_CXX11_ABI=0" //tensorflow/tools/pip_package:build_pip_package --action_env="LD_LIBRARY_PATH=${LD_LIBRARY_PATH}" --verbose_failuresビルドには8コアマシンで4時間くらいかかるので、ssh先でビルドが終わるまで監視するのがめんどくさい場合は、
nohup env BAZEL //(途中コマンド省略)// --verbose_failures &
としておけば良けば、sshが切れても処理が続けられる。他にはdisown
コマンドを使う方法もある。ビルドに失敗したとき、やり直す前に
bazel clean --expunge
で一度ビルド途中のファイルを消去しておく。正しくビルドが成功すると、TensorFlowのディレクトリから見て
./bazel-bin/tensorflow/tools/pip_package/build_pip_package
というファイルが作られているはずなので、これを以下のコマンドでwheelファイル化させる。# リリース版(2.4.1とかバージョン番号がついている方)はこちらのコマンド ./bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg # githubのマスターから直接引っ張ってきた場合はnightly buildフラグを付けると良い # ./bazel-bin/tensorflow/tools/pip_package/build_pip_package --nightly_flag /tmp/tensorflow_pkg成功すると
/tmp/tensorflow_pkg
以下にwhlファイルが生成されているので、任意のディレクトリ上に移す(例:/home/apps
)。mv /tmp/tensorflow_pkg/tensorflow-2.4.1-cp38-cp38-linux_x86_64.whl /home/appsあとはこれをpython3.8のpip3.8などでインストールすれば使えるようになる(依存パッケージ解決のためにインターネット環境が必要かも)。
/home/apps/python/3.8.7/bin/python3.8 -m pip install /home/apps/tensorflow-2.4.1-cp38-cp38-linux_x86_64.whl --user
--user
の代わりに、-t
オプションを付けて-t /home/apps/tensorflow-gpu/2.4.1_python3.8_cuda11
とすれば、そのディレクトリにパッケージをインストールすることもでき、あとで環境変数PYTHONPATH
を通せば使えるようになる。動作テスト
最後にテストしてみる。
python3.8
起動前に、上記のpython3.8, gcc-8.4.0, cuda-11.1について環境変数PATH
とLD_LIBRARY_PATH
を通しておく。私はPATHの管理についてenvironment moduleを使っています。$ python3.8 Python 3.8.7 (default, Jan 29 2021, 03:36:15) [GCC 8.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from tensorflow.python.client import device_lib 2021-01-29 15:47:38.281608: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcudart.so.11.0 >>> device_lib.list_local_devices() 2021-01-29 15:47:44.310282: I tensorflow/core/platform/cpu_feature_guard.cc:142] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations: SSE3 SSE4.1 SSE4.2 AVX AVX2 FMA To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags. 2021-01-29 15:47:44.313095: I tensorflow/compiler/jit/xla_gpu_device.cc:99] Not creating XLA devices, tf_xla_enable_xla_devices not set 2021-01-29 15:47:44.315158: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcuda.so.1 2021-01-29 15:47:44.553626: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1720] Found device 0 with properties: pciBusID: 0000:01:00.0 name: GeForce RTX 3090 computeCapability: 8.6 coreClock: 1.695GHz coreCount: 82 deviceMemorySize: 23.70GiB deviceMemoryBandwidth: 871.81GiB/s 2021-01-29 15:47:44.554559: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1720] Found device 1 with properties: pciBusID: 0000:81:00.0 name: GeForce RTX 3090 computeCapability: 8.6 coreClock: 1.695GHz coreCount: 82 deviceMemorySize: 23.70GiB deviceMemoryBandwidth: 871.81GiB/s 2021-01-29 15:47:44.554693: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcudart.so.11.0 2021-01-29 15:47:44.560335: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcublas.so.11 2021-01-29 15:47:44.560419: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcublasLt.so.11 2021-01-29 15:47:44.568157: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcufft.so.10 2021-01-29 15:47:44.573367: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcurand.so.10 2021-01-29 15:47:44.591659: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcusolver.so.11 2021-01-29 15:47:44.597937: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcusparse.so.11 2021-01-29 15:47:44.600959: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcudnn.so.8 2021-01-29 15:47:44.609122: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1862] Adding visible gpu devices: 0, 1 2021-01-29 15:47:44.609182: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcudart.so.11.0 2021-01-29 15:47:45.879186: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1261] Device interconnect StreamExecutor with strength 1 edge matrix: 2021-01-29 15:47:45.879243: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1267] 0 1 2021-01-29 15:47:45.879253: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1280] 0: N N 2021-01-29 15:47:45.879261: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1280] 1: N N 2021-01-29 15:47:45.883077: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1406] Created TensorFlow device (/device:GPU:0 with 22428 MB memory) -> physical GPU (device: 0, name: GeForce RTX 3090, pci bus id: 0000:01:00.0, compute capability: 8.6) 2021-01-29 15:47:45.886622: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1406] Created TensorFlow device (/device:GPU:1 with 22428 MB memory) -> physical GPU (device: 1, name: GeForce RTX 3090, pci bus id: 0000:81:00.0, compute capability: 8.6) [name: "/device:CPU:0" device_type: "CPU" memory_limit: 268435456 locality { } incarnation: 12593410599457824982 , name: "/device:GPU:0" device_type: "GPU" memory_limit: 23518436480 locality { bus_id: 4 numa_node: 3 links { } } incarnation: 9832514123427674549 physical_device_desc: "device: 0, name: GeForce RTX 3090, pci bus id: 0000:01:00.0, compute capability: 8.6" , name: "/device:GPU:1" device_type: "GPU" memory_limit: 23518436480 locality { bus_id: 2 numa_node: 1 links { } } incarnation: 10036722414424779821 physical_device_desc: "device: 1, name: GeForce RTX 3090, pci bus id: 0000:81:00.0, compute capability: 8.6" ]1ノードに2つあるRTX3090が正しく認識されているのでこれで使えるはず。