ネット上には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/bin
bintools
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 1
Tensorflowのコンパイル時のエラーを解決するためのワークアラウンド(応急処置回避策)を以下に示す。
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が正しく認識されているのでこれで使えるはず。