ラベル DNN の投稿を表示しています。 すべての投稿を表示
ラベル DNN の投稿を表示しています。 すべての投稿を表示

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デバイスを認識していること。


2021年12月23日木曜日

Fedora 35 の GCC 11.2.1 20211203 で Tensorflow 2.7(CUDA11.5 cuDNN8.3.1)をビルドする

目的



その後、Fedora 35のGCCのバージョンがアップデートした。
  • GCC 11.2.1 20210728 → GCC 11.2.1 20211203

この変更、特にlibstdc++のヘッダーファイルの変更が影響してCUDA関連のビルドが失敗することがわかった。

注)
もちろんCUDAのGCCサポートはFedora 34のGCC 11(おそらく11.1)となっている。

このため、Fedora 35のGCC 11.2はサポート対象外であり、前回ビルドできたことは奇跡だったのである。
(そうでも、ちょっといきなりビルドできなくなるのはひどいが、、、)


GCC 11.2.1 20211203でのCUDA関連のビルドエラー


Fedora 35 の GCC 11.2.1 20211203でビルドすると下記の記事でまとめたが、ビルドエラーが発生する。
なお、これはTensorFlowに限らず、CUDA関連のビルドはすべて失敗すると思われる。


環境


2021.12.18時点の環境は以下。
  • Fedora 35 x86_64
  • Python 3.10.0 (default, Oct  4 2021, 00:00:00) [GCC 11.2.1 20210728 (Red Hat 11.2.1-1)] on linux
  • gcc version 11.2.1 20211203 (Red Hat 11.2.1-7) (GCC) 
  • CUDA 11.5 + cuDNN v8.3.1


事前の準備


対応の方針としては、いつものごとくCUDA用のGCCを別途用意する。
これは過去も同様。今回はGCC11.1を用意する。

GCC 11.1のビルド


ソースのダウンロード&変更


GCC11.1のソースをダウンロードする。

wget https://ftp.gnu.org/gnu/gcc/gcc-11.1.0/gcc-11.1.0.tar.gz
tar xf gcc-11.1.0.tar.gz
cd gcc-11.1.0/


そのままではビルドエラーになってしまうため、コードの一部を変更する。
エラーの内容、変更箇所は以下のGentooのバクレポートを参照。


ビルド


これも今までの通り。
以下のビルドオプションでビルドを行う。

./contrib/download_prerequisites
mkdir build
../configure \
  --enable-bootstrap \
  --enable-languages=c,c++ \
  --prefix=/home/xxxx/gcc/11.1 \
  --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ファイルを作成、設定する。

/home/xxxx/gcc/11.1/bin/gcc -dumpspecs > specs
$ vi specs

# before
*link_libgcc:
%D

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

$ mv specs /home/xxxx/gcc/11.1/lib/gcc/x86_64-redhat-linux/11/


TensorFlowのビルド


あとは、いつもどおりビルドすればOK。


Configure


configureではCUDAのコンパイラーにGCC11.1を指定する。

 ./configure 
You have bazel 3.7.2 installed.
Please specify the location of python. [Default is /home/xxxx/.virtualenvs/tf2.7/bin/python3]: 


Found possible Python library paths:
  /home/xxxx/.virtualenvs/tf2.7/lib/python3.10/site-packages
  /home/xxxx/.virtualenvs/tf2.7/lib64/python3.10/site-packages
Please input the desired Python library path to use.  Default is [/home/xxxx/.virtualenvs/tf2.7/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.5 in:
    /usr/local/cuda-11.5/targets/x86_64-linux/lib
    /usr/local/cuda-11.5/targets/x86_64-linux/include
Found cuDNN 8 in:
    /usr/local/cuda-11.5/targets/x86_64-linux/lib
    /usr/local/cuda-11.5/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/xxxx/gcc/11.1/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 \
  //tensorflow/tools/pip_package:build_pip_package
./bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg
pip install /tmp/tensorflow_pkg/tensorflow-2.7.0-cp310-cp310-linux_x86_64.whl


2021年5月1日土曜日

Fedora 34 でTensorflow 2.5-rc2(CUDA11.3 cuDNN8.2.0)をビルドする

目的


Tensorflowの2.5-rc2をFedora 34でソースビルドする。
2.5の正式版リリースに向けての準備と備忘録。


環境


  • Fedora 34 x86_64
  • Python 3.9.4(virtualenv)
  • GCC 11.0.1 20210324
  • CUDA 11.3 + cuDNN 8.2.0
  • CPU AMD Ryzen 7 1700
  • GPU GeForce GTX 1070


事前の準備


GCC10.2のビルド


前回の記事
と同様、CUDA 11.3がサポートするGCCは10.2.1で、Fedora 34のGCC11ではビルドができない。このため、まずはGCC10のビルドを行う。
GCCのビルドについては、以前の記事を参照。


GCC9.3のソースダウンロード&ビルド


ソースをダウンロードし、ビルドする。

$ wget http://ftp.tsukuba.wide.ad.jp/software/gcc/releases/gcc-10.2.0/gcc-10.2.0.tar.gz
$ tar xf gcc-10.2.0.tar.gz
$ cd gcc-10.2.0/
$ ./contrib/download_prerequisites 
$ mkdir build
$ cd build/
$ ../configure \
    --enable-bootstrap \
    --enable-languages=c,c++ \
    --prefix=/home/xxxxx/gcc/10.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 -j16
$ make install


specsファイルの作成


コンパイルしたGCC9でビルドした際に、適切な動的リンクライブラリ(libstdc++.so)がリンクされるようにSPECEファイルを修正する。

$ /home/xxxxx/gcc/10.2/bin/gcc -dumpspecs > specs
$ vi specs

# before
*link_libgcc:
%D

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

$ mv specs /home/xxxx/gcc/10.2/lib/gcc/x86_64-redhat-linux/10/


Environment Modulesの設定


GCC10をEnvironment Modulesで切り替えられるようにする。environment-modulesをインストール後、/etc/modulefiles 配下に、gcc10xのファイルを作成する。

$ sudo dnf install environment-modules

$ sudo vi /etc/modulefiles/gcc10x

#%Module 1.0
#
#  gcc-10.X module for use with 'environment-modules' package:
#

prepend-path    PATH                    /home/xxxxx/gcc/10.2/bin/


Bazelのビルド


Bazel をソースビルドする。最新の3.7.4をソースビルドした。
公式のソースビルド方法はここを参照。手順どおりであり詳細の説明は割愛。


CUDA、cuDNNのインストール


CUDA: 11.3、cuDNN: 8.2.0をインストール。
  • CUDAはRPM Fusion Howto/ CUDA を参考にインストールを行う。
  • cuDNNはNVIDIAのダウンロードサイトからダウンロード、インストールを行う。
    2021.5.1時点ではRPM Fusion(Machine Learning repository)からインストールできるcuDNNがCUDA11.1の組み合わせで古いため。


Tensorflowのビルド


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


virtualenvの設定


まずはvirtualenv(virtualenvwapper)でTensorflow用の仮想Python環境を作成し、必要なモジュールをインストールする。

$ mkvirtualenv -p python3  tf2.5-rc2
$ pip install pip numpy wheel
$ pip install keras_preprocessing --no-deps

ビルド


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


(tf2.5-rc1) $ git clone -b r2.5 https://github.com/tensorflow/tensorflow.git
(tf2.5-rc2) $ cd tensorflow
(tf2.5-rc2) $ ./configure 
(tf2.5-rc2) $ bazel build \
    --config=cuda \
    --config=v2 \
    --config=nonccl \
    --config=opt \
    //tensorflow/tools/pip_package:build_pip_package
(tf2.5-rc2) $ ./bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg
(tf2.5-rc2) $ pip install /tmp/tensorflow_pkg/tensorflow-2.5.0rc2-cp39-cp39-linux_x86_64.whl


インストール確認


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

(tf2.5-rc2) $ python
Python 3.9.4 (default, Apr  6 2021, 00:00:00) 
[GCC 11.0.1 20210324 (Red Hat 11.0.1-0)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import tensorflow as tf
2021-05-01 21:07:37.359112: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0
INFO:tensorflow:Enabling eager execution
INFO:tensorflow:Enabling v2 tensorshape
INFO:tensorflow:Enabling resource variables
INFO:tensorflow:Enabling tensor equality
INFO:tensorflow:Enabling control flow v2
>>> tf.__version__
'2.5.0-rc2'
>>> from tensorflow.python.client import device_lib
>>> device_lib.list_local_devices()
2021-05-01 21:08:05.854665: 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-05-01 21:08:05.865237: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcuda.so.1
2021-05-01 21:08:05.908808: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-05-01 21:08:05.909879: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1733] Found device 0 with properties: 
pciBusID: 0000:0a:00.0 name: NVIDIA GeForce GTX 1070 computeCapability: 6.1
coreClock: 1.7085GHz coreCount: 15 deviceMemorySize: 7.93GiB deviceMemoryBandwidth: 238.66GiB/s
2021-05-01 21:08:05.909938: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0
2021-05-01 21:08:05.942599: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcublas.so.11
2021-05-01 21:08:05.942733: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcublasLt.so.11
2021-05-01 21:08:05.953447: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcufft.so.10
2021-05-01 21:08:05.970201: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcurand.so.10
2021-05-01 21:08:05.982505: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcusolver.so.11
2021-05-01 21:08:05.994212: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcusparse.so.11
2021-05-01 21:08:05.995767: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudnn.so.8
2021-05-01 21:08:05.995901: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-05-01 21:08:05.996970: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-05-01 21:08:05.997553: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1871] Adding visible gpu devices: 0
2021-05-01 21:08:05.997592: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0
2021-05-01 21:08:06.441157: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1258] Device interconnect StreamExecutor with strength 1 edge matrix:
2021-05-01 21:08:06.441208: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1264]      0 
2021-05-01 21:08:06.441217: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1277] 0:   N 
2021-05-01 21:08:06.441408: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-05-01 21:08:06.441949: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-05-01 21:08:06.442510: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-05-01 21:08:06.442984: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1418] Created TensorFlow device (/device:GPU:0 with 6992 MB memory) -> physical GPU (device: 0, name: NVIDIA GeForce GTX 1070, pci bus id: 0000:0a:00.0, compute capability: 6.1)
[name: "/device:CPU:0"
device_type: "CPU"
memory_limit: 268435456
locality {
}
incarnation: 17887149015766682436
, name: "/device:GPU:0"
device_type: "GPU"
memory_limit: 7332429824
locality {
  bus_id: 1
  links {
  }
}
incarnation: 2150787946068778776
physical_device_desc: "device: 0, name: NVIDIA GeForce GTX 1070, pci bus id: 0000:0a:00.0, compute capability: 6.1"
]
>>> 

OK!

2021年1月31日日曜日

MetadataをTensorFlow Lite モデルに追加してTensorFlow Lite Support Task Libraryで推論する

 目的


TensorFlow Lite SupportTFLite Model Metadataを使ってTensorFlow Lite モデルにMetadataを追加する。Metadataを追加したモデルでTensorFlow Lite Support Task Libraryで推論してみる。


前回まで


前回のブログ「Raspberry PiでTensorFlow Lite Support Task Libraryをやってみる。」ではTensorFlow Lite Support Task Library (C++)をつかってRaspberry Pi 4で動くカメラキャプチャのサンプルを作った。

このときに使ったTensorFlow Lite モデル(TF-Lite モデル)はmetadataが追加されたモデルを使用した(TensorFlow Hubから入手したモデル)。

今回はTF-Lite モデルにmetadataを自分で追加して、TensorFlow Lite Support Task Library(TF Lite Support Task Library)で推論することをやってみる。


Metadataとは?


公式の「Adding metadata to TensorFlow Lite models」を参照。
既にあるTF-Lite モデルに補足の情報を追加することができる。


追加できる情報


以下の情報が追加できる。
  • モデル全体の説明(概要、著作権、作成者などの情報)
  • 入力の説明(画像フォーマット、正規化、入力幅の情報)
  • 出力の説明とラベル(説明やラベルとのマッピング)

追加した情報は「Netron」や「AndroidStudio」で参照できる。


Metaデータを追加する利点


  • 配布するモデルファイルの作成者、著作権を明示できる。
    TF-Lite モデルは端末にダウンロードが前提。
    公開・非公開に関わらず情報があるとありがたい。
  • TF Lite Support Task Libraryを使うと入力、出力情報から必要な前・後処理を行ってくれる。
    • 前処理: リサイズ、正規化、モデルのInput(Float, Int)
    • 後処理: ラベルとのマッピング
  • 推論コードのラッパーを自動生成してくれる。
    TensorFlowのドキュメント「メタデータを使用してモデルインターフェイスを生成する
    (まだよく理解していないので後で試す)


Medadataを追加するには?


TF Lite SupportのAPI(Python)を利用する。
Pythonのpipパッケージはtflite-supportが利用できる。


Object detectionモデルにmetadataを組み込む


TensorFlow 1 Detection Model Zooのpre-trainedモデル「ssd_mobiledet_cpu_coco」にMetadataを追加する。
このモデルはFloatモデルで、InputもFloat32である。

なお、Metadataを追加しない状態でTF Lite Support Task Libraryで推論するとエラーとなってしまう。
これは、TF Lite Support Task LibraryがInputの正規化するための情報が無いため。

$ ./bazel-bin/tensorflow_lite_support/examples/task/vision/pi/object_detector_capture \
    --model_path=/home/pi/ssdlite_mobiledet_cpu.tflite \
    --num_thread=4 --score_threshold=0.5
Detection failed: Input tensor has type kTfLiteFloat32: it requires specifying NormalizationOptions metadata to preprocess input images.

モデルにmetadataを追加して推論ができることまで確認する。


参考のコード・サンプル


Metadetaを追加するライブラリはここで実装。

MetadataはFlatBuffersのスキーマで定義。

よく使われるモデルのラッパーの定義。
(Image classification、Object detection、Image segmentation)
使い方はドキュメントはテストコードを参照。


作成したサンプル


Object detectionモデルにmetadataを書き込むサンプルを作成した。

前回のtflite-supportのforkリポジトリに追加。



実装


Pythonでの実装の概要を説明。

必要なimport
from tensorflow_lite_support.metadata import metadata_schema_py_generated as _metadata_fb
from tensorflow_lite_support.metadata.python.metadata_writers import metadata_info
from tensorflow_lite_support.metadata.python.metadata_writers import object_detector
from tensorflow_lite_support.metadata.python.metadata_writers import writer_utils

サンプルではMetadataPopulatorForObjectDetectorクラスの_create_metadataメソッドでmetadataを生成。

metadata_info.GeneralMdでモデル全体の情報を生成。
    # Creates model info.
    self.general_md = metadata_info.GeneralMd(
      name=self.model_info.name,
      version=self.model_info.version,
      description=("Identify which of a known set of objects might be present "
                   "and provide information about their positions within the "
                   "given image or a video stream."),
      author="Test",
      licenses=("Apache License. Version 2.0 "
                 "http://www.apache.org/licenses/LICENSE-2.0.")
    )

metadata_info.InputImageTensorMdでモデルの入力を生成。
  • norm_mean、norm_stdで正規化のパラメータを指定。
  • tensor_typeは入力モデルから取得(writer_utils.get_input_tensor_types)。
  • モデルはtensorflow.python.platform.resource_loaderを使って読み込み。
    # Load model to buffer.
    self.model_buffer = self._load_file(self.model_file_path)

    # Creates input info.
    self.input_md = metadata_info.InputImageTensorMd(
      name="normalized_input_image_tensor",
      description=("Input image to be classified. The expected image is {0} x {1}, with "
                   "three channels (red, blue, and green) per pixel. Each value in the "
                   "tensor is a single byte between {2} and {3}.".format(
                     self.model_info.image_width, self.model_info.image_height,
                     self.model_info.image_min, self.model_info.image_max)),
        norm_mean=self.model_info.mean,
        norm_std=self.model_info.std,
        color_space_type=_metadata_fb.ColorSpaceType.RGB,
        tensor_type=writer_utils.get_input_tensor_types(self.model_buffer)[0])

metadata_info.CategoryTensorMdでモデルの出力を生成。
  • ラベルファイルのパスを渡してあげると、ラベルも一緒に追加してくれる。
    # Creates output info.
    self.output_category_md = metadata_info.CategoryTensorMd(
        name="category",
        description="The categories of the detected boxes.",
        label_files=[
            metadata_info.LabelFileMd(file_path=file_path)
            for file_path in self.label_file_path
        ])

object_detector.MetadataWriter.create_from_metadata_infoで生成したMetadataをモデルに追加する。
戻り値のMetadataWriterのpopulateメソッドを呼び出すことでMetadataを追加したモデルを得ることができる。あとはバイナリファイルに書き込むだけ。
    self.writer = object_detector.MetadataWriter.create_from_metadata_info(
        model_buffer=self.model_buffer, general_md=self.general_md,
        input_md=self.input_md, output_category_md=self.output_category_md)
    model_with_metadata = self.writer.populate()

    with open(self.export_model_path, "wb") as f:
      f.write(model_with_metadata)

ラッパーを使っているので簡単にmetadataを追加できる。

また、MetadataWriterのget_metadata_jsonメソッドで追加したMetadataをJSONフォーマットで得ることができる。
  def get_metadata_json(self):
    return self.writer.get_metadata_json()


環境の準備


ホストPCでMetadataを書き込む(Windows、LinuxどちらもOK)。
今回はWindowsで実施。

TF Lite Support Task LibraryのPythonパッケージは0.1.0が公開されている(2021.01.30時点)。
しかし、metadata_writersのラッパーは0.1.0には含まれていないため、tflite-support-nightlyを使う。
TensorFlowはCPUのみで問題ない。

$ pip install tflite-support-nightly
$ pip install tensorflow


Metadataを組み込むモデル、ラベルファイル


モデル


ssd_mobiledet_cpu_coco」をダウンロードして目的のTF-Liteモデルを得る。
model.tfliteをssdlite_mobiledet_cpu.tfliteにリネームして使用。
$ wget http://download.tensorflow.org/models/object_detection/ssdlite_mobiledet_cpu_320x320_coco_2020_05_19.tar.gz
$ tar xf ssdlite_mobiledet_cpu_320x320_coco_2020_05_19.tar.gz
$ mv ssdlite_mobiledet_cpu_320x320_coco_2020_05_19/model.tflite ssdlite_mobiledet_cpu.tflite

ラベル


coco datasetsのラベルを記載したファイル(labelmap.txt)を用意する。
今回はTF Lite Supportのテストデータを使用する。


Metadataを組み込む


リポジトリをclone後、サンプルのスクリプトを実行する。
引数は追加するモデル、ラベルファイル、出力ディレクトリ。
$ git clone https://github.com/NobuoTsukamoto/tflite-support.git
$ cd tensorflow_lite_support\examples\metadata
$ mkdir model_with_metadata
$ python metadata_writer_for_object_detection.py \
    --model_file=PATH_TO\ssdlite_mobiledet_cpu.tflite \
    --label_file=PATH_TO\labelmap.txt \
    --export_directory=.\model_with_metadata

model_with_metadataディレクトリに以下が生成される。
  • ssdlite_mobiledet_cpu.tflite 👈 metadataを追加したモデル
  • ssdlite_mobiledet_cpu.json 👈 追加したmetadataのJSONファイル


Metadataを確認


Netronと生成したJSONファイルを確認。

Netron


Input(normalized_input_image_tensor)を選択すると追加したmetadataの情報が表示される。
Metadataを追加したモデル

追加前と比べるとよくわかる。
Metadataを追加する前のオリジナルのモデル



JSON


出力したmetadataのJSON。
Netronでは表示できない内容も確認できる。
{
  "name": "SSDLite with MobileDet-CPU",
  "description": "Identify which of a known set of objects might be present and provide information about their positions within the given image or a video stream.",
  "version": "v1",
  "subgraph_metadata": [
    {
      "input_tensor_metadata": [
        {
          "name": "normalized_input_image_tensor",
          "description": "Input image to be classified. The expected image is 320 x 320, with three channels (red, blue, and green) per pixel. Each value in the tensor is a single byte between 0 and 255.",
          "content": {
            "content_properties_type": "ImageProperties",
            "content_properties": {
              "color_space": "RGB"
            }
          },
          "process_units": [
            {
              "options_type": "NormalizationOptions",
              "options": {
                "mean": [
                  127.5
                ],
                "std": [
                  127.5
                ]
              }
            }
          ],
          "stats": {
            "max": [
              1.0
            ],
            "min": [
              -1.0
            ]
          }
        }
      ],
      "output_tensor_metadata": [
        {
          "name": "location",
          "description": "The locations of the detected boxes.",
          "content": {
            "content_properties_type": "BoundingBoxProperties",
            "content_properties": {
              "index": [
                1,
                0,
                3,
                2
              ],
              "type": "BOUNDARIES"
            },
            "range": {
              "min": 2,
              "max": 2
            }
          },
          "stats": {
          }
        },
        {
          "name": "category",
          "description": "The categories of the detected boxes.",
          "content": {
            "content_properties_type": "FeatureProperties",
            "content_properties": {
            },
            "range": {
              "min": 2,
              "max": 2
            }
          },
          "stats": {
          },
          "associated_files": [
            {
              "name": "labelmap.txt",
              "description": "Labels for categories that the model can recognize.",
              "type": "TENSOR_VALUE_LABELS"
            }
          ]
        },
        {
          "name": "score",
          "description": "The scores of the detected boxes.",
          "content": {
            "content_properties_type": "FeatureProperties",
            "content_properties": {
            },
            "range": {
              "min": 2,
              "max": 2
            }
          },
          "stats": {
          }
        },
        {
          "name": "number of detections",
          "description": "The number of the detected boxes.",
          "content": {
            "content_properties_type": "FeatureProperties",
            "content_properties": {
            }
          },
          "stats": {
          }
        }
      ],
      "output_tensor_groups": [
        {
          "name": "detection_result",
          "tensor_names": [
            "location",
            "category",
            "score"
          ]
        }
      ]
    }
  ],
  "author": "Test",
  "license": "Apache License. Version 2.0 http://www.apache.org/licenses/LICENSE-2.0."
}

Metadataを組み込んだモデルを使ってみる


前回のブログで使ったサンプルプログラム(Object Detector)を使って推論してみる。
モデルをRaspberry Pi 4に転送して推論する。
$ ./bazel-bin/tensorflow_lite_support/examples/task/vision/pi/object_detector_capture \
    --model_path=/home/pi/ssdlite_mobiledet_cpu.tflite \
    --num_thread=4 --score_threshold=0.5
Detection failed: Input tensor has type kTfLiteFloat32: it requires specifying NormalizationOptions metadata to preprocess input images.

今度はエラーがなく、推論できる👍


感想


推論のコードが変更せずに利用できることはとても良いことだと思う。
(ただ、個人で使う場合、metadataを追加するコストを考えるとあまりメリットがない気も、、、Input, Outputを統一してしまえばいいし、、、)
次は「メタデータを使用してモデルインターフェイスを生成する」を使ってAndroidアプリを試してみよう。

2021年1月17日日曜日

Raspberry PiでTensorFlow Lite Support Task Libraryをやってみる。

目的


TensorFlow Lite Support Task Library (C++)をつかってRaspberry Pi4で動くカメラキャプチャのサンプルを作ってみる。
TensorFlow LiteのAPIと比べて簡単に実装できるのかを確認してみる。


動機


2020年9月ぐらいから tensorflow / tflite-support のリポジトリに気がついたり、公式ブログでもアナウンスがあったりしたので気になっていた。
ようやく年末年始の休みにやってみることにした。




TensorFlow Lite Support Task Libraryとは?


公式のドキュメントは日本語にも翻訳されている。
自分の理解は以下。
  • TensorFlow Lite APIよりも簡単に扱うことができるAPIを用意。
  • タスク(画像分類、物体検出、自然言語処理、...etc)ごとにAPIを用意。
  • モデルのInput / Outputの形式(Float, INT, Shape...)を気にしなくてよい。
TensorFlow Lite APIと比べて扱いやすいAPIを用意することが目的と推測。

また、TensorFlow Lite Support Task Library は、他のライブラリを含めてTensorFlow Lite Supportと称している模様。

TensorFlow Lite Supportに含まれるライブラリは
  • TensorFlow Lite Support Library
  • TensorFlow Lite Model Metadata
  • TensorFlow Lite Support Codegen Tool
  • TensorFlow Lite Support Task Library ← 今回はこれ
があり、これらを使うことで特にモバイルのアプリの作成やデプロイを簡単にしようとする目的だと推測。

以下、簡単な特徴を記載。
これらは今後変更になる可能性があるので注意。


サポート言語


Native、Android、iOSの各プラットフォームで開発できる言語をサポートしている。
  • Java
  • C++ (WIP)
  • Swift (WIP)


用意されているタスク


2021.01.17時点では以下がサポートされている。

画像系

自然言語系


タスクを自作するには?


上記のタスク以外で、独自のタスクを実装することも可能。


モデル


通常のTensorFlow Lite モデルも可能だが、metadataを追加したTensorFlow Lite モデルを扱うことができる。TensorFlow Lite モデルにmetadataを追加することで
  • Task LibraryがInput, Outputの違い(型、サイズ)を吸収してくれる
    (アプリがリサイズ、型変換を意識しなくてよい)
  • Labelファイルが不要となる(モデルに埋め込める)。
    (モデルとラベルを一元管理でき、Task Libraryが結果からラベルを返してくれる)
といった利点が出てくる。

また、metadataにはモデルの説明や著作権などのライセンス情報も埋め込むことができる。これはOn-deviceな実行の場合、モデルが端末に配布(ダウンロード)される。ユーザーにはモデルが見えるので、ライセンスが明示できることは非常にありがたいと思う。Labelもモデルに埋め込めれば管理も容易になる。

TensorFlow Hubではmetadataが組み込まれたTensorFlow Liteモデルがある。例えば、ssd_mobilenet_v1を確認するとmetadetaにはモデルの説明、ライセンス、Input、Outputの詳細が確認できる。

metadataを追加したTensorFlow Lite モデルについては、次回以降にもう少し詳細化してみたい。


ラズパイ4でカメラキャプチャのサンプルを作る


今回は、画像のタスクのサンプル(CLI Demos for C++ Vision Task APIs)を参考に、ラズパイ4(64bit)とPiCameraでカメラキャプチャでタスクを実行するサンプル(C++)を作った。

Task Library(C++)の使い勝手を確認してみる。

元のリポジトリからForkしたリポジトリを作成。




用意したサンプル


画像で用意されているタスクのサンプルを作成してみた。


環境


自分の環境は以下。
  • Raspberry Pi 4 4GB
  • Raspberry Pi OS 64bit
  • Raspberry Pi Camera Module V2.1(UVCカメラでもOKなはず)


ビルド環境の準備と必要なモジュールのインストール


サンプルは元リポジトリと同様Bazelをつかってビルドしている。また、カメラキャプチャするためOpenCVを利用。ビルドはクロスコンパイルしたかったが、今回はHostのラズパイでビルドする。
クロスコンパイルするにはMediaPipeのサンプルと同じようにDokcerで行なえばいいと思う。

# Install required library
$ sudo apt install git libopencv-dev

# Install build tool.
$ wget https://github.com/bazelbuild/bazel/releases/download/3.7.2/bazel-3.7.2-linux-arm64
$ chmod +x bazel-3.7.2-linux-arm64
$ sudo mv bazel-3.7.2-linux-arm64 /usr/local/bin/bazel
$ sudo apt install openjdk-11-jdk


ビルド


リポジトリをClone後、それぞれをビルド。ビルド時間はおおよそ15~20分程度。
# Clone repository
$ git clone https://github.com/NobuoTsukamoto/tflite-support.git
$ cd tflite-support


Image Classifier


# Build Image Classifier
$ bazel build \
    --verbose_failures \
    tensorflow_lite_support/examples/task/vision/pi/image_classifier_capture


Object Detector


# Build Image Classifier
$ bazel build \
    --verbose_failures \
    tensorflow_lite_support/examples/task/vision/pi/object_detector_capture


Image Segmenter


# Build Image Classifier
$ bazel build \
    --verbose_failures \
    tensorflow_lite_support/examples/task/vision/pi/image_segmenter_capture


実行


TensorFlow Hubからmetadataが組み込まれたTensorFlow Liteモデルをダウンロードして実行する。


Image Classifier


# Download the model
$ curl \
   -L 'https://tfhub.dev/google/lite-model/aiy/vision/classifier/birds_V1/3?lite-format=tflite' \
   -o ./aiy_vision_classifier_birds_V1_3.tflite

# Run the classification tool.
$ ./bazel-bin/tensorflow_lite_support/examples/task/vision/pi/image_classifier_capture \
    --model_path=./aiy_vision_classifier_birds_V1_3.tflite \
    --num_thread=4


Object Detector


# Download the model.
$ curl \
   -L 'https://tfhub.dev/tensorflow/lite-model/ssd_mobilenet_v1/1/metadata/2?lite-format=tflite' \
   -o ./ssd_mobilenet_v1_1_metadata_2.tflite

# Run the detection tool.
$ ./bazel-bin/tensorflow_lite_support/examples/task/vision/pi/object_detector_capture \
    --model_path=./ssd_mobilenet_v1_1_metadata_2.tflite \
    --score_threshold=0.5 \
    --num_thread=4




Image Segmenter


# Download the model.
$ curl \
    -L 'https://tfhub.dev/tensorflow/lite-model/deeplabv3/1/metadata/1?lite-format=tflite'  \
    -o ./deeplabv3_1_metadata_1.tflite

# Run the segmantation tool.
$ ./bazel-bin/tensorflow_lite_support/examples/task/vision/pi/image_segmenter_capture \
    --model_path=./deeplabv3_1_metadata_1.tflite \
    --num_thread=4




ハマったこと


WORKSPACEにOpenCVのBUILDを追加したところビルドエラーが発生。
/usr/include/c++/8/cstdlib:75:15: fatal error: stdlib.h: No such file or directory
#include_next <stdlib.h>

詳細はこのIssueにある通りで、ビルドのパラメータに「build --spawn_strategy=standalone」が指定されていると、/usr/includeのインクルードパスが追加されなくなるためである模様。
ビルドパラメータを削除して対応した。


TensorFlow Lite Support Task Libraryの使いやすさ


ある程度、サンプルを実装してみた感想。画像系のタスクでの結果なので、自然言語系のタスクは触れていないので注意。


モデルのロード(タスクの生成)


モデルのロードはそのタスクのクラスを生成することで実現する。モデルパス、閾値やthread数などのパラメータもオプションとして指定する。

物体検出タスクだとこんな感じでObjectDetectorのインスタンスを生成。
  // Build ObjectDetector.
  const ObjectDetectorOptions& options = BuildOptions();
  ASSIGN_OR_RETURN(std::unique_ptr<ObjectDetector> object_detector,
                   ObjectDetector::CreateFromOptions(options));

オプションの指定(BuildOptions関数)はこんな感じ。
ObjectDetectorOptions BuildOptions() {
  ObjectDetectorOptions options;
  // モデルパスを指定
  options.mutable_model_file_with_metadata()->set_file_name(
      absl::GetFlag(FLAGS_model_path));
  // 出力の最大数
  options.set_max_results(absl::GetFlag(FLAGS_max_results));
  // 推論でのスレッドの並列数
  options.set_num_threads(absl::GetFlag(FLAGS_num_thread));
  // スコアの閾値
  if (absl::GetFlag(FLAGS_score_threshold) >
      std::numeric_limits<float>::lowest()) {
    options.set_score_threshold(absl::GetFlag(FLAGS_score_threshold));
  }
  // 出力クラスのホワイトリスト
  for (const std::string& class_name :
       absl::GetFlag(FLAGS_class_name_whitelist)) {
    options.add_class_name_whitelist(class_name);
  }
  // 出力クラスのブラックリスト
  for (const std::string& class_name :
       absl::GetFlag(FLAGS_class_name_blacklist)) {
    options.add_class_name_blacklist(class_name);
  }
  return options;
}                 

スコアの閾値や出力数の個数はアプリ側で制御すると煩雑になりがちなのでオプションで指定できるのは便利。また、画像分類、物体検出タスクでは出力のホワイトリスト、ブラックリストが指定できる。TensorFlow Hubなどで用意されたPre-trainedモデルを使う場合で、必要なクラスを制御したいときは便利だと思う。

各クラスのオプションはprotoとして記述されている。以下を参照。

画像系

自然言語系にはオプションはないように見える?


入力(画像)


TF-Liteで面倒なのは画像の入力だと思う。

モデルにあわせてリサイズや型(Float、INT)の変換、標準化が必要。必要な情報はモデルから取得できるのだが、すべての形式にあわせようとするとかなり冗長なコードとなってしまう。自分はFloat、INTモデルに限らず、モデルの入力はINT8で統一してしまうことでリサイズだけ意識するようにしている。ただ、Floatモデルの場合はINT⇒Floatへのdequantizedを挟むのでほんの少しだけもったいない気がする。

Task Libraryではモデルの入力を意識する必要がない(Libraryが吸収してくれる)。
アプリは画像データをFlatBuffer形式するだけ。各入力データの形式(Gray, RGB, RGA, YUV, Raw)からFlatBufferに変換するIFが用意されている。

OpenCVでカメラキャプチャしたデータの場合、BGR⇒RGBに変換、CreateFromRgbRawBufferを使ってFlatBufferを生成すればよい。CreateFromRgbRawBufferの引数にはcv::MatのサイズとRawデータを与える。
    cap >> frame; // capture frame.
    cv::cvtColor(frame, input_im, cv::COLOR_BGR2RGB); // BGR to RGB

    // Frame in a FrameBuffer.
    std::unique_ptr<FrameBuffer> frame_buffer;
    frame_buffer = CreateFromRgbRawBuffer(input_im.data, {input_im.cols, input_im.rows});                

2,3行で入力データが生成できるのはとてもありがたい。

また、CreateFromRgbRawBufferの引数には画像の向きも指定できる(引数のFrameBuffer::Orientation orientation)。
モバイルの場合、9軸センサーの値などから、スマホの向きを考慮して画像の向きを考える必要がある。Task Libraryではアプリで画像を回転する必要がなく、ライブラリ内部で回転してくれる。

画像のデータだけでなくて、サイズや向き、フォーマットを指定するのでFlatBufferで入力データを指定するということで理解した。


推論


推論自身は各タスクのメソッド(Classify、Detect、Segment、etc...)を呼び出し、入力データのFlatBufferを指定してあげる。
さほどTensorFlow Lite APIのinvokeと変わらない。
    // Run object detection and draw results on input image.
    ASSIGN_OR_RETURN(DetectionResult result,
                     object_detector->Detect(*frame_buffer));          

入力したデータはどのように変換されるかはここに記載がある。
  • RGBAやYUVなどの形式の場合はRGBに変換される。
  • アスペクトを維持せず、モデルの入力サイズにリサイズ。
    (アスペクト比を維持しないので要注意)
  • Orientationのパラメータによって、画像を回転して推論。


出力


画像分類、物体検出タスクの場合、それぞれ結果はClassificationResult、DetectionResult
として取得することができる。生のOutputTensorを意識する必要はない。

物体検出タスクの場合は下記のようにBoundingBoxの位置やサイズ、クラスのラベル名が取得できる。モデルにラベルが組み込まれていれば、出力クラスのindexから該当のラベルの文字列をとってくる処理も不要になる。これはほんとに便利。
DrawCaptionはOpenCVの文字列描画を行う独自の関数)
absl::Status EncodeResultToMat(const DetectionResult& result,
                               cv::Mat& image) {
  for (int index = 0; index < result.detections_size(); ++index) {
    // Get bounding box as left, top, right, bottom.
    const BoundingBox& box = result.detections(index).bounding_box();
    const Detection& detection = result.detections(index);
    const int x = box.origin_x();
    const int y = box.origin_y();
    const int width = box.width();
    const int height = box.height();

    // Draw. Boxes might have coordinates outside of [0, w( x [0, h( so clamping
    // is applied.
    cv::rectangle(image, cv::Rect(x, y, width, height), kBuleColor, kLineThickness);

    // Draw. Caption.
    std::ostringstream caption;

    if (detection.classes_size() == 0) {
      caption << "  No top-1 class available";
    } else {
      const Class& classification = detection.classes(0);

      if (classification.has_class_name()) {
        caption << classification.class_name();
      } else {
        caption << classification.index();
      }
      caption << " (" << std::fixed << std::setprecision(2) << classification.score() << ")";
      DrawCaption(image, cv::Point(x-3, y), caption.str());
    }
  }

  return absl::OkStatus();
}           

また、Image Segmenterの場合、ループ処理でOutputとのマスクをとる必要がある。OpenCVを利用している場合はcv::Mat::forEachでループしてあげれば並列化も期待できると思う。なお、このサンプルではcolored_labelsでLabelごとのcolormap(いわゆるPASCAL VOCのcolormap)で色付けしている。
std::unique_ptr<cv::Mat> EncodeMaskToMat(const SegmentationResult& result) {
  if (result.segmentation_size() != 1) {
    std::cout << "Image segmentation models with multiple output segmentations are not "
        "supported by this tool." << std::endl;
    return nullptr;
  }
  const Segmentation& segmentation = result.segmentation(0);
  // Extract raw mask data as a uint8 pointer.
  const uint8* raw_mask =
      reinterpret_cast<const uint8*>(segmentation.category_mask().data());

  // Create RgbImageData for the output mask.
  auto seg_im = std::make_unique<cv::Mat>(cv::Size(segmentation.width(), segmentation.height()), CV_8UC3);
  auto wdith = seg_im->cols;
  seg_im->forEach<cv::Vec3b>([&](cv::Vec3b &src, const int position[2]) -> void {
    size_t index = position[0] * wdith + position[1];
    Segmentation::ColoredLabel colored_label =
        segmentation.colored_labels(raw_mask[index]);
        src[0] = colored_label.b();
        src[1] = colored_label.g();
        src[2] = colored_label.r();
    });
  
  return seg_im;
}        


感想


使ってみた感想


Native C++しか使っていないが、

使いやすい点
  • TensorFlow Liteモデルの入出力の型、サイズを意識しなくてよいので実装がとても楽。
  • TensorFlow Lite APIを使た場合と比べて1/2~1/3の実装で済む。
  • OpenCVを使っても実装が楽。とくにInputをFlatBufferへの変換。
    (たぶんこれはAndroid、iOSの場合もそうかも?)

使いにくかった点
  • Bazelを使ったビルド(これは自分が慣れていないせいもある)。
    とくにOpenCVを追加したらなぜかビルドエラー。。。
  • APIのリファレンスがまだ整備されていない。
    まだ正式リリースでもない状態なので仕方がない。今後に期待。


MediaPipeとは何が違うの?


MediaPipeもTensorFlow Lite モデルを扱うことができるクロスプラットフォームなライブラリである。
MediaPipeとTask Libraryを比べた場合、
  • MediaPipeは画像系のタスクに特化、Task Libraryは画像以外のタスクも可能。
  • MediaPipeは推論部分だけでなくて、前処理、後処理も含めてのフレームワーク。
    作成したコンポーネントを再利用可能として、開発を容易とする。
    Task Libraryは推論部分の実装を容易とする。
で、それぞれ目的が異なると思う。
TF-Liteモデルを使ってお手軽にアプリを実装したい場合は、Task Libraryを使うほうが良いと思う。MediaPipeは簡単にという訳にはいかない。ある程度、MediaPipeのFrameworkとしての内容を理解していないと難しいと思う。


Coralとは何が違うの


Coral EdgeTPUのPyCoral API(Python)libcoral API(C++)もTF-Liteモデルを扱うことができる(EdgeTPU delegateだけでなくて)。どちらもTensorFlow Lite モデルをより簡単に扱うためのAPIを提供しているようにも見える。
  • Task LibraryはEdgeTPU delegateができない。
  • Pythonで扱うことができるAPIはPyCoralのみ。
で、EdgeTPUを扱う場合はPyCoral、libcoarl APIを扱う以外はないのが現状。2つのライブラリがわかれているのがもったいない気もするけど、、、


次は?


今回はTensorFlow Lite Support Task LibraryのNative C++を扱ってみた。楽に実装ができるのはいいねと思うけど、あとはBazelとかドキュメントが充実してくるといいなぁと思う。

つぎはTask Libraryとセットで必要になるTensorFlow Liteモデルへのmetadataの組み込みをやってみよう。

2020年11月21日土曜日

Fedora 33でTensorflow 2.4-rc2(CUDA11.1 cuDNN8.0.5)をビルドする

目的


Tensorflowの2.4-rc2をFedora 33でソースビルドする。
2.4の正式版リリースに向けての準備と備忘録。


環境


  • Fedora 33 x86_64
  • python 3.9.0(virtualenv)
  • GCC 10.2.1
  • CUDA 11.1 + cuDNN 8.0.5
  • CPU AMD Ryzen 7 1700
  • GPU GeForce GTX 1070


事前の準備


GCC9のビルド


前回の記事
と同様、CUDA 11.1がサポートするGCCは9で、Fedora 33のGCC10ではビルドができない。このため、まずはGCC9のビルドを行う。
GCCのビルドについては、以前の記事を参照。


GCC9.3のソースダウンロード&ビルド


ソースをダウンロードし、ビルドする。

$ wget https://ftp.gnu.org/gnu/gcc/gcc-9.3.0/gcc-9.3.0.tar.gz
$ cd gcc-9.3.0/
$ ./contrib/download_prerequisites 
$ mkdir build
$ cd build/
$ ../configure \
    --enable-bootstrap \
    --enable-languages=c,c++ \
    --prefix=/home/xxxx/gcc/9.3 
    --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 -j16
$ make install


specsファイルの作成


コンパイルしたGCC9でビルドした際に、適切な動的リンクライブラリ(libstdc++.so)がリンクされるようにSPECEファイルを修正する。

$ /home/xxxx/gcc/9.3/bin/gcc -dumpspecs > specs
$ vi specs

# before
*link_libgcc:
%D

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

$ mv specs /home/xxxx/gcc/9.3/lib/gcc/x86_64-redhat-linux/9/


Environment Modulesの設定


GCC8をEnvironment Modulesで切り替えられるようにする。/etc/modulefiles 配下に、gcc9xのファイルを作成する。

#%Module 1.0
#
#  gcc-9.X module for use with 'environment-modules' package:
#

conflict        gcc5x gcc7x gcc9x
prepend-path    PATH                    /home/xxxx/gcc/9.3/bin/


Bazelのビルド


Bazel をソースビルドする。最新の3.7をソースビルドした。
公式のソースビルド方法はここを参照。手順どおりであり詳細の説明は割愛。


CUDA、cuDNNのインストール


CUDA: 11.1、cuDNN: 8.0.5をインストール。CUDAはRPM Fusion Howto/ CUDA を参考にインストールを行う。Which driver Packageにもあるとおり、RPM FusionとCUDAのリポジトリの両方にnvidia driverが存在するが、バージョンの不一致を起こしてしまうことがある。手順どおりにインストールしないとCUDAとnvidia driverの不一致で使えない。。。
cuDNNはNVIDIAのダウンロードサイトからダウンロード、インストールを行う。


Tensorflowのビルド


さて、本題。TensorFlow 2.4-rc1をビルドする。


virtualenvの設定


まずはvirtualenv(virtualenvwapper)でTensorflow用の仮想Python環境を作成し、必要なモジュールをインストールする。

$ mkvirtualenv -p python3  tf2.4-rc2
$ pip install pip six numpy wheel setuptools mock 'future>=0.17.1'
$ pip install keras_applications --no-deps
$ pip install keras_preprocessing --no-deps

ビルド


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


(tf2.4-rc1) $ wget https://github.com/tensorflow/tensorflow/archive/v2.4.0-rc1.tar.gz
(tf2.4-rc1) $ tar xf v2.4.0-rc1.tar.gz 
(tf2.4-rc1) $ cd tensorflow-2.4.0-rc1/
(tf2.4-rc1) $ ./configure 
(tf2.4-rc1) $ bazel build \
                --config=opt \
                --config=v2 \
                --cxxopt="-D_GLIBCXX_USE_CXX11_ABI=0" \
                --config=cuda \
                --config=nonccl \
                --verbose_failures \
                //tensorflow/tools/pip_package:build_pip_package
(tf2.4-rc1) $ ./bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg
(tf2.4-rc1) $ pip install /tmp/tensorflow_pkg/tensorflow-2.4.0rc2-cp39-cp39m-linux_x86_64.whl


インストール確認


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

(tf2.4-rc1) $ python
    Python 3.9.0 (default, Oct  6 2020, 00:00:00) 
[GCC 10.2.1 20200826 (Red Hat 10.2.1-3)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import tensorflow as tf
2020-11-21 09:17:05.361081: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcudart.so.11.0
>>> from tensorflow.python.client import device_lib
>>> device_lib.list_local_devices()
2020-11-21 09:17:17.759422: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcuda.so.1
2020-11-21 09:17:17.800045: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:941] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2020-11-21 09:17:17.803501: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1720] Found device 0 with properties: 
pciBusID: 0000:09:00.0 name: GeForce GTX 1070 computeCapability: 6.1
coreClock: 1.7085GHz coreCount: 15 deviceMemorySize: 7.93GiB deviceMemoryBandwidth: 238.66GiB/s
2020-11-21 09:17:17.803527: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcudart.so.11.0
2020-11-21 09:17:17.805845: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcublas.so.11
2020-11-21 09:17:17.805916: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcublasLt.so.11
2020-11-21 09:17:17.806788: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcufft.so.10
2020-11-21 09:17:17.816671: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcurand.so.10
2020-11-21 09:17:17.854541: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcusolver.so.11
2020-11-21 09:17:17.862372: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcusparse.so.11
2020-11-21 09:17:17.961227: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcudnn.so.7
2020-11-21 09:17:17.961410: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:941] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2020-11-21 09:17:17.962245: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:941] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2020-11-21 09:17:17.962836: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1862] Adding visible gpu devices: 0
2020-11-21 09:17:17.963571: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcudart.so.11.0
2020-11-21 09:17:18.745397: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1261] Device interconnect StreamExecutor with strength 1 edge matrix:
2020-11-21 09:17:18.745448: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1267]      0 
2020-11-21 09:17:18.745463: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1280] 0:   N 
2020-11-21 09:17:18.747106: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:941] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2020-11-21 09:17:18.747776: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:941] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2020-11-21 09:17:18.748358: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:941] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2020-11-21 09:17:18.748909: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1406] Created TensorFlow device (/device:GPU:0 with 7120 MB memory) -> physical GPU (device: 0, name: GeForce GTX 1070, pci bus id: 0000:09:00.0, compute capability: 6.1)
2020-11-21 09:17:18.751666: I tensorflow/compiler/jit/xla_gpu_device.cc:99] Not creating XLA devices, tf_xla_enable_xla_devices not set
[name: "/device:CPU:0"
device_type: "CPU"
memory_limit: 268435456
locality {
}
incarnation: 12838272530603917041
, name: "/device:GPU:0"
device_type: "GPU"
memory_limit: 7466015808
locality {
  bus_id: 1
  links {
  }
}
incarnation: 3621828384593309124
physical_device_desc: "device: 0, name: GeForce GTX 1070, pci bus id: 0000:09:00.0, compute capability: 6.1"
]

OK!

2020年6月6日土曜日

Jetson NanoでTF-TRTを試す(JetPack 4.4 Developer Preview)

目的


(ずいぶん前になるが)
JetPack 4.4 Developer Previewがリリースされたので、TF-TRT(TensorFlow integration with TensorRT)を使ってFP16に最適化したモデルでの処理時間を計測する。
また、TensorFlow Object Detection APIにも新しいモデル(MnasFPN、MobileDets)が追加されたので試してみる。


まとめ


  • SSDLite MobileNet V3は前回(JetPack4.3 + TF1.15)より多少の改善がみられる。
  • MnasFPNは思った効果(推論時間)は確認できなかった。
  • MobileNet V3がMobileNet V2より有効(推論時間がはやい)ことが確認できた。
  • Object detection modelではMobileDetsが良さそう。


インストール


JetPack4.4、TensorFlow、TF-TRT modelsのインストールを行う。


JetPack


JetPack SDKからJetPack 4.4 Developer PreviewのSDイメージをダウンロードして、SDカードに書き込み。


TensorFlow


TensorFlow on Jetson Platform をもとにTensorFlowをインストールする。
注意することは、バージョンは1.xをインストールすること(TensorFlow Object Detection APIは1.x系のみ対応)。
インストール時は‘tensorflow<2’をつける。
※2020.6.6時点では1.15.2がインストールされる。


TF-TRT Models


TF-TRT Models(TensorFlow/TensorRT Models on Jetson)のリポジトリをcloneし、インストールする。
なお、オリジナルのリポジトリからforkして、変更を加えている。
  • submodule(TensorFlow Model Garden)を更新。
    (MobileDetsを変換したいため)
  • 変換スクリプト、ベンチマークスクリプトを追加



まず、TensorFlow Object Detection APIに必要なパッケージをインストールする。

$ sudo apt install pyton3-tk python3-matplotlib
$ pip3 install --user tf_slim

その後、TF-TRT Modelsのリポジトリをcloneし、インストールスクリプトを実行する。

$ git clone https://github.com/NobuoTsukamoto/tf_trt_models.git
$ cd tf_trt_models/
$ ./install.sh python3

※Python3での実行のみ確認している。


モデルの変換


変換スクリプトを使用して、TF-TRT FP16モデルに変換する。


Object detection model


pre-trained modelの場合は、prefixを指定すれば変換する。
ただ、mobiledetはフォルダの構成が他と違うため手動でダウンロード&変換が必要。
今回の対象モデルは以下。
  • ssdlite_mobilenet_v2 (※)
  • ssdlite_mobilenet_v3_small
  • ssdlite_mobilenet_v3_large
  • ssd_mobilenet_v2_mnasfpn
  • ssdlite_mobiledet_cpu
  • ssdlite_mobiledet_edgetpu
  • ssdlite_mobiledet_dsp
  • ssdlite_mobilenet_edgetpu

変換は以下のように実行

cd examples/detection/
$ python3 convert.py --model=ssdlite_mobilenet_v2_coco
$ python3 convert.py --model=ssdlite_mobilenet_v3_small_coco --force_nms_cpu
$ python3 convert.py --model=ssdlite_mobilenet_v3_large_coco --force_nms_cpu
$ python3 convert.py --model=ssd_mobilenet_v2_mnasfpn_coco --force_nms_cpu

# ssdlite_mobilenet_edgetpuはwgetできないのでリンクをクリックしてダウンロード
$ cd data/
$ tar xf checkpoints_ssdlite_mobilenet_edgetpu_coco_quant.tar.gz
$ cd ../
$ python3 convert.py --path=data/ssdlite_mobilenet_edgetpu_coco_quant/ --force_nms_cpu

$ cd data/
$ wget http://download.tensorflow.org/models/object_detection/ssdlite_mobiledet_cpu_320x320_coco_2020_05_19.tar.gz
$ tar xf ssdlite_mobiledet_cpu_320x320_coco_2020_05_19.tar.gz
$ cd ssdlite_mobiledet_cpu_320x320_coco_2020_05_19
$ mv model.ckpt-400000.data-00000-of-00001  model.ckpt.data-00000-of-00001
$ mv model.ckpt-400000.index model.ckpt.index
$ mv model.ckpt-400000.meta model.ckpt.meta
$ cd ../../
$ python3 convert.py --path=data/ssdlite_mobiledet_cpu_320x320_coco_2020_05_19 --force_nms_cpu

$ cd data/
$ wget http://download.tensorflow.org/models/object_detection/ssdlite_mobiledet_edgetpu_320x320_coco_2020_05_19.tar.gz
$ tar xf ssdlite_mobiledet_edgetpu_320x320_coco_2020_05_19.tar.gz
$ cd data/ssdlite_mobiledet_edgetpu_320x320_coco_2020_05_19/fp32/
$ mv model.ckpt-400000.data-00000-of-00001  model.ckpt.data-00000-of-00001
$ mv model.ckpt-400000.index model.ckpt.index
$ mv model.ckpt-400000.meta model.ckpt.meta
$ cd ../../
$ python3 convert.py --path=data/ssdlite_mobiledet_edgetpu_320x320_coco_2020_05_19/fp32 --force_nms_cpu

$ cd data/
$ wget http://download.tensorflow.org/models/object_detection/ssdlite_mobiledet_dsp_320x320_coco_2020_05_19.tar.gz
$ tar xf ssdlite_mobiledet_dsp_320x320_coco_2020_05_19.tar.gz
$ cd data/ssdlite_mobiledet_dsp_320x320_coco_2020_05_19/fp32/
$ mv model.ckpt-400000.data-00000-of-00001  model.ckpt.data-00000-of-00001
$ mv model.ckpt-400000.index model.ckpt.index
$ mv model.ckpt-400000.meta model.ckpt.meta
$ cd ../
$ python3 convert.py --path=data/ssdlite_mobiledet_dsp_320x320_coco_2020_05_19/fp32  --force_nms_cpu

前回のブログでもふれた「NMSをCPU実行に書き換えると変換できない」が発生するため、NMSはそのまま(GPUで実行)とする。変換スクリプトに"--force_nms_cpu"を指定する。


Image classification model


こちらも変換スクリプトを用意している。
pre-trained modelのprefixを指定すれば変換する。
今回の対象モデルは以下(Inputはすべて244x244)。
  • mobilenet_v2 (depth_multiplier=0.5)
  • mobilenet_v2 (depth_multiplier=1.0)
  • mobilenet_v2 (depth_multiplier=1.4)
  • mobilenet_v3_small
  • mobilenet_v3_small

$ cd examples/classification/
$ python3 convert.py --model=mobilenet_v2_0p5_224
$ python3 convert.py --model=mobilenet_v2_1p0_224
$ python3 convert.py --model=mobilenet_v2_1p4_224
$ python3 convert.py --model=mobilenet_v3_small
$ python3 convert.py --model=mobilenet_v3_small_224


ベンチマーク


ベンチマーク実施はMax power&CUIモードで実行する。

$ sudo systemctl set-default multi-user.target
$ sudo reboot
$ sudo jetson_clocks

Object detection model


  • ssdlite_mobilenet_v3_small / largeは前回(JetPack4.3+TF1.15)と比べると推論時間が改善している。
  • ssdlite_mobilenet_v2はNMSのCPU実行ができない分、推論時間が遅い。
  • ssdlite_mobilenet_v2_mnasfpnは想定よりかなり遅い。
    CPUに最適化しているからだろうか?
  • 精度(mAP)と推論時間のトータルを考慮するとssdlite_mobiledet_edgetpuが一番。
  • ssdlite_mobilenet_v3_small or largeも使える。

初回の推論時間

Model
Input size
Inference time [ms]
TF-TRT (FP16)
ssdlite_mobilenet_v2300x30013025
ssdlite_mobilenet_v3_small320x3208375
ssdlite_mobilenet_v3_large320x3207366
ssdlite_mobilenet_v2_mnasfpn320x32017502
ssdlite_mobiledet_cpu320x3208117
ssdlite_mobiledet_edgetpu320x3207332
ssdlite_mobiledet_dsp320x3208203
ssdlite_mobilenet_edgetpu320x32023643


2回目以降の100回の推論の平均時間

Model
Input size
Inference time [ms]
TF-TRT (FP16)
ssdlite_mobilenet_v2300x30088
ssdlite_mobilenet_v3_small320x32056
ssdlite_mobilenet_v3_large320x32068
ssdlite_mobilenet_v2_mnasfpn320x320223
ssdlite_mobiledet_cpu320x32073
ssdlite_mobiledet_edgetpu320x32070
ssdlite_mobiledet_dsp320x32080
ssdlite_mobilenet_edgetpu320x320186



Image classification model


  • mobilenet_v2 (depth_multiplier=0.5)とmobilenet_v3_smallがほぼ同等の推論時間。
  • mobilenet_v2 (depth_multiplier=1.0)とmobilenet_v3_largeがほぼ同等の推論時間。
  • FP32での精度を考えるとmobilenet_v3を使うのが良いかも?


初回の推論時間

Model
Input size
Inference time [ms]
TF-TRT (FP16)
mobilenet_v2_0.5_224224x2243477
mobilenet_v2_1.0_224224x22411354
mobilenet_v2_1.4_224224x2249356
mobilenet_v3_small_224224x2244187
mobilenet_v3_large_224224x2245387


2回目以降の100回の推論の平均時間

Model
Input size
Inference time [ms]
TF-TRT (FP16)
mobilenet_v2_0.5_224224x22410
mobilenet_v2_1.0_224224x22416
mobilenet_v2_1.4_224224x22423
mobilenet_v3_small_224224x22410
mobilenet_v3_large_224224x22417



最後に


Jetson Nano(JetPack 4.4 Developer Preview)でTF-TRT モデルの推論時間をベンチマークしてみた。

結果からMobileNet V3ベースまたは、SSDLite MobileDets Edge TPUが良い結果に思える。
FP16にした場合の制度の低下がどれぐらいかは気になるところではあるが、FP32とほぼ同等(の低下)と考えれば、MobileNet V3、MobileDetsは良い選択肢になると思う。
(あとはどのぐらい学習しやすさによるか?)

Object detection modelはモバイルCPU、Edge TPU、DSPに特化したモデルが登場した(MnasFpn、MobileDets)。
これらのモデルの中にはJetson NanoのGPUではそれほど良い結果は得られなかった。
これはモデルがHWに特化していることも要因の一つに思える。

あとは、Efficientnet、EfficientDetも試してみたい。