2022年5月14日土曜日

Fedora 36 でTensorflow 2.9rc2(CUDA11.6 cuDNN8.4.0)をビルドする

 目的


Tensorflowの2.9rc2 + CUDAをFedora 36でソースビルドする。


環境


  • SW
    • Fedora 35 x86_64
    • Python 3.10.4
    • GCC 12.0.1 20220308
    • CUDA 11.6 + cuDNN v8.4.0
  • HW
    • CPU AMD Ryzen 7 1700
    • GPU GeForce GTX 1070


事前の準備


CUDA、cuDNNのインストール


これは、以前のブログと同様。
RPM Fusionを参考にCUDAを、NVIDIAよりcuDNNをそれぞれインストールする。


Bazelのソースビルド


TensorFlow v2.9はBazel 5.0.0以上でのビルドが必要。
FedoraのCOPR repositoryからインストール(公式の手順にもある)できるバージョンは4系でビルドできない。
このため、v5.1.1をソースビルドする。
$ sudo dnf install java-11-openjdk-devel
$ wget https://github.com/bazelbuild/bazel/releases/download/5.1.1/bazel-5.1.1-dist.zip
$ unzip -qq bazel-5.1.1-dist.zip
$ env EXTRA_BAZEL_ARGS="--host_javabase=@local_jdk//:jdk" bash ./compile.sh
$ sudo cp output/bazel /usr/local/bin/


GCC11.2のビルド


CUDA 11.6がサポートするGCCのバージョンは11.2で、Fedora 36のGCC12は対象外。
このため、GCC 11.2をソースビルドする。
ビルドについては、以前のブログを参照。


ソースダウンロード&ビルド


$ wget http://ftp.tsukuba.wide.ad.jp/software/gcc/releases/gcc-11.2.0/gcc-11.2.0.tar.gz
$ tar xf gcc-11.2.0.tar.gz
$ gcc-11.2.0/
$ ./contrib/download_prerequisites
$ mkdir build && cd build
$ ../configure \
    --enable-bootstrap \
    --enable-languages=c,c++ \
    --prefix=/home/USER/gcc/11.2 \
    --enable-shared \
    --enable-threads=posix \
    --enable-checking=release \
    --disable-multilib \
    --with-system-zlib \
    --enable-__cxa_atexit \
    --disable-libunwind-exceptions \
    --enable-gnu-unique-object \
    --enable-linker-build-id \
    --with-gcc-major-version-only \
    --with-linker-hash-style=gnu \
    --enable-plugin \
    --enable-initfini-array \
    --with-isl \
    --enable-libmpx \
    --enable-gnu-indirect-function \
    --build=x86_64-redhat-linux
$ make -j$(nproc)
$ make install


specsファイルの作成&設定


コンパイルしたGCC11でビルドした際に、適切な動的リンクライブラリ(libstdc++.so)がリンクされるようにSPECEファイルを修正する。
$ /home/USER/gcc/11.2/bin/gcc -dumpspecs > specs
$ vi specs

# before
*link_libgcc:
%D

# after
*link_libgcc:
%{!static:%{!static-libgcc:-rpath /home/USER/gcc/11.2/lib64/}} %D

$ mv specs /home/USER/gcc/11.2/lib/gcc/x86_64-redhat-linux/11/


Tensorflowのビルド


さて、本題。TensorFlow 2.9-rc2をビルドする。


virtualenvの設定


まずはvirtualenv(virtualenvwapper)でTensorflow用の仮想Python環境を作成し、必要なモジュールをインストールする。
$ mkvirtualenv tf2.9rc2
$ pip install pip numpy wheel
$ pip install keras_preprocessing --no-deps
$ pip install packaging

今回、packagingモジュールも追加でインストールが必要だった。
(ビルド時にModuleNotFoundError: No module named 'packaging'エラーとなった)


ビルド


今までどおりconfigure&buildなのだが、今回は追加の手順が必要。
CUDA_HOST_COMPILERに使用するGCC11とGCC12でGLIBCXXのABIが異なってしまう。
ビルドは成功するが、import時に以下のエラーが発生してしまう。

$ python
Python 3.10.4 (main, Mar 25 2022, 00:00:00) [GCC 12.0.1 20220308 (Red Hat 12.0.1-0)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import tensorflow as tf
Traceback (most recent call last):
  File "/home/USER/.virtualenvs/tf2.9rc2/lib64/python3.10/site-packages/tensorflow/python/pywrap_tensorflow.py", line 62, in <module>
    from tensorflow.python._pywrap_tensorflow_internal import *
ImportError: /home/USER/gcc/11.2/lib64/libstdc++.so.6: version `GLIBCXX_3.4.30' not found (required by /home/USER/.virtualenvs/tf2.9rc2/lib64/python3.10/site-packages/tensorflow/python/_pywrap_tensorflow_internal.so)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/USER/.virtualenvs/tf2.9rc2/lib64/python3.10/site-packages/tensorflow/__init__.py", line 37, in <module>
    from tensorflow.python.tools import module_util as _module_util
  File "/home/USER/.virtualenvs/tf2.9rc2/lib64/python3.10/site-packages/tensorflow/python/__init__.py", line 36, in <module>
    from tensorflow.python import pywrap_tensorflow as _pywrap_tensorflow
  File "/home/USER/.virtualenvs/tf2.9rc2/lib64/python3.10/site-packages/tensorflow/python/pywrap_tensorflow.py", line 77, in <module>
    raise ImportError(
ImportError: Traceback (most recent call last):
  File "/home/USER/.virtualenvs/tf2.9rc2/lib64/python3.10/site-packages/tensorflow/python/pywrap_tensorflow.py", line 62, in <module>
    from tensorflow.python._pywrap_tensorflow_internal import *
ImportError: /home/USER/gcc/11.2/lib64/libstdc++.so.6: version `GLIBCXX_3.4.30' not found (required by /home/USER/.virtualenvs/tf2.9rc2/lib64/python3.10/site-packages/tensorflow/python/_pywrap_tensorflow_internal.so)


Failed to load the native TensorFlow runtime.
See https://www.tensorflow.org/install/errors for some common causes and solutions.
If you need help, create an issue at https://github.com/tensorflow/tensorflow/issues and include the entire stack trace above this error message.

Fedora36のGCC12 libstdc++とビルドしたGCC11 libstdc++のGLIBCXXのABIの不一致。
これを回避するために、ビルド時のオプションに"-std=gnu++14"を付与する。

あとは、Githubからソースを取得し、configureスクリプト実行し、ビルドを行う。
  • CUDAのサポートを有効とする。
  • Host compilerにGCC11.2のgccのパスを指定してあげる。
  • ビルドオプションには"--config=v2"、"-std=gnu++14"、"--config=nonccl "を指定。

$ ./configure 
You have bazel 5.1.1- (@non-git) installed.
Please specify the location of python. [Default is /home/USER/.virtualenvs/tf2.9rc2/bin/python3]: 


Found possible Python library paths:
  /home/USER/.virtualenvs/tf2.9rc2/lib/python3.10/site-packages
  /home/USER/.virtualenvs/tf2.9rc2/lib64/python3.10/site-packages
Please input the desired Python library path to use.  Default is [/home/USER/.virtualenvs/tf2.9rc2/lib/python3.10/site-packages]

Do you wish to build TensorFlow with ROCm support? [y/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]: 
No TensorRT support will be enabled for TensorFlow.

Found CUDA 11.6 in:
    /usr/local/cuda-11.6/targets/x86_64-linux/lib
    /usr/local/cuda-11.6/targets/x86_64-linux/include
Found cuDNN 8 in:
    /usr/local/cuda-11.6/targets/x86_64-linux/lib
    /usr/local/cuda-11.6/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: 6.1]: 


Do you want to use clang as CUDA compiler? [y/N]: 
nvcc will be used as CUDA compiler.

Please specify which gcc should be used by nvcc as the host compiler. [Default is /usr/bin/gcc]: /home/USER/gcc/11.2/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]: 
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 and Compute Library for the Arm Architecture (ACL).
	--config=monolithic  	# Config for mostly static monolithic build.
	--config=numa        	# Build with NUMA support.
	--config=dynamic_kernels	# (Experimental) Build kernels into separate shared objects.
	--config=v1          	# Build with TensorFlow 1 API instead of TF 2 API.
Preconfigured Bazel build configs to DISABLE default on features:
	--config=nogcp       	# Disable GCP support.
	--config=nonccl      	# Disable NVIDIA NCCL support.
Configuration finished
$ bazel build \
    --config=cuda \
    --config=v2 \
    --config=nonccl \
    --config=opt \
    --cxxopt="-std=gnu++14" \
    //tensorflow/tools/pip_package:build_pip_package
$ ./bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg
$ cp /tmp/tensorflow_pkg/tensorflow-2.9.0rc2-cp310-cp310-linux_x86_64.whl ./
$ pip3 install tensorflow-2.9.0rc2-cp310-cp310-linux_x86_64.whl


インストールの確認


  • tf.__version__が2.9-rc2であること。
  • GPUデバイスを認識していること。


2022年5月7日土曜日

meta-tensorflow-liteをyocto kirkstoneに対応するときにつまずいた点

 目的


meta-tensorflow-liteをkikstoneに対応した際に行ったこと、つまずいたことを記録する。
また、今回もtiwtterでかなりアドバイスをいただいた。感謝。



meta-tensorflow-liteの対応


kikstoneはyoctoのメジャーバージョンがアップし、4.0になった。

かなり変更があり、自分のレシピにもかなりの影響があった。
  1. SRC_URIのgit url指定の変更
  2. SRCREVにコミットIDを指定
  3. do_fetch以外でのダウンロードの抑制
  4. CMAKE_SYSTEM_PROCESSORの制御を変更

なお、kirkstoneに対応したmeta-tensorflow-liteは以下で公開した。




SRC_URIのgit url指定の変更


SRC_URIにgit を指定している場合、
  • プロトコル(protocol=???)
  • ブランチ(branch=???)
を必ず指定することになった。
meta-tensorflow-liteはhonisterからどちらも指定していたので問題はなかった。

変換用のスクリプトも用意されている。
また、レシピの命名規則などを変換するスクリプト(convert-xxxxxxxx.py)も用意されている。これらは事前にかけておいた方がよい。
(いくつかoverride syntaxの"_"を":"に変更する必要があった箇所があった)


SRCREVのコミットIDの指定


honisterまでのレシピでbitbakeすると以下のエラーが発生した。

ERROR: python3-tensorflow-lite-2.8.0-r0 do_fetch: Bitbake Fetcher Error: FetchError("Recipe uses a floating tag/branch without a fixed SRCREV yet doesn't call bb.fetch2.get_srcrev() (use SRCPV in PV for OE).", None)

どうやら、kirkstoneではSRCREVにタグ名の指定はNGの模様。
コミットIDを指定する必要があった。リリースノートには記載が見当たらず、、、(もしかしたら見逃しているだけかも)

honisterまでのレシピは、以前にissueでいただいたやり方で${PV}をつかった自動化でもあった(レシピのファイル名のバージョンをあげるだけでよい)

次回以降はTensorFlowのバージョンアップの際にコミットIDの変更が必要になった...


do_fetch以外でモジュールをダウンロードする対応


これは影響の大きかった変更点。yocto 4.0のリリースノートにも記載がある。

TensorFlow LiteはCMakeのconfigureでサブモジュールをダウンロードするため、大きく影響を受けた。
どうすればよいのか悩んでいたところ、twitterでアドバイスをうけた(ほんとうにありがとうございます)。

Kirkstoneからはdo_fetch以外でダウンロードを行う場合は、該当タスクにフラグをたてる必要があるとのこと。
ただ、フラグをたてても状況が変わらずだった(サブモジュールのダウンロードができない)。
何が違うのか?ミニマムなサンプルを作成して事象を再現させて確認。

原因は、TensorFLow LiteのCMakeでExternalProject_Addではなく、FetchContent_Declare、FetchContent_PopulateなどのFetchContentを使用していることが原因だった。

cmake.bbclassでCMAKEのオプションにオプションにFETCHCONTENT_FULLY_DISCONNECTEDが追加されていた(kirkstoneより)。
FETCHCONTENT_FULLY_DISCONNECTEDオプションはFetchContentのダウンロードを抑制するオプションとのこと(知らなかった!)。
このFETCHCONTENT_FULLY_DISCONNECTEDはExternalProject_Addに影響しない模様。
これが動作の違いになっていた。

このため、レシピにはEXTRA_OECMAKEでFETCHCONTENT_FULLY_DISCONNECTEDを変更&do_configureでネットワークからのダウンロードを有効にするようにした。

EXTRA_OECMAKE:append = " -DFETCHCONTENT_FULLY_DISCONNECTED=OFF"
do_configure[network] = "1"


CMAKE_SYSTEM_PROCESSORの指定方法の変更


上記のレシピ修正でなんとかaarch64やriscv向けのターゲットでビルドできるようになった。
しかしながら、armv6やarmv7向けのビルドが失敗する。

レシピでHOST_ARCHを変更していたことが原因の模様(honisterまでは問題なかったが、kirkstoneではエラーとなってしまった)。

HOST_ARCHを設定していた理由は、cmake.bbclassのCMAKE_SYSTEM_PROCESSORオプションを制御したかったため。

TensorFlow Liteのサブモジュールでpytorch/cpuinfoはCMakeのCMAKE_SYSTEM_PROCESSORオプションを意識する。
このため、TensorFlow LiteのCMakeビルドの際にオプションを指定している。
具体的には、初代ラズパイ、ラズパイ0は"armv6"、その他ラズパイ32bitは"armv7"を期待している。

yoctoのcmake.bbclassでは、CMAKE_SYSTEM_PROCESSORオプションにHOST_OS(TARGET_OS)を指定している。
meta-raspberrypiでビルドする際、"raspberrypi"や"raspberrypi4"などのarm 32bitの場合、HOST_OS(TARGET_OS)には"arm"が設定されるため、TensorFlow Liteのビルドが失敗する。
このため、TARGET_OSをレシピより"armv6"や"armv7"など期待する値に上書きすることで対応していた。

kirkstoneからはこのやり方がエラーとなってしまうため、TARGET_OSは変更せず、TensorFlow LiteのCMakeLists.txtにパッチを当ててCMAKE_SYSTEM_PROCESSORオプションを設定するように変更した。
(EXTRA_OECMAKEで"-DCMAKE_SYSTEM_PROCESSORを指定してもうまくいかなかった)


最後に


今回もかなり助けられて対応することができた。ほんとに感謝。

まだ、Raspberry Piで動作確認できない(core-image-westonでブート時にディスプレイが表示されない)やTensorFlow Lite v2.9のリリースも近いのでまだまだ確認&対応していく予定。