#0. 更新履歴
(2020/06/27) Tensorflow r2.3用に記述内容を全面修正しました。
#1. はじめに
ラズパイ4の OpenGLES はリリース当初 ver 3.0 でしたが、2020/1月に ver 3.1 にアップデートされました。
OpenGLES 3.1 では、ComputeShader が使えるようになり、GPGPUプログラミングしやすくなります。超うれしいアップデートです。
一方 TensorFlow Lite には、推論にかかる演算処理を CPUからGPU にオフロードする GPU Delegate 機能があります。
この GPU Delegate は、さらに V1(OpenGLES)
と V2(OpenCL)
とがあり、V1 は OpenGLES 3.1 が使える環境であれば動かすことができます。V2は OpenCL が使える環境での動作を想定していますが、OpenCLが使えない環境では自動で V1 相当の OpenGLES で動作するようになっています。ラズパイ4 や Jetson nano には OpenCL 環境は提供されていませんが、この自動フォールバック機能があるので、GPU Delegate V2を使っておけば大丈夫 です。
今回のOpenGLESアップデートにより、この GPU Delegate をラズパイ4で動かせるようになったわけですが、果たして、GPUを活用することでTensorFlow Lite の推論時間を短縮することができるでしょうか。
本記事は、ラズパイ4の OpenGLES 3.1 環境で TensorFlow Lite GPU Delegate を動かす手順についてまとめます。その後、GPU Delegate 有無による性能差比較結果についてもふれます。
題材として Posenet による姿勢推定を用うこととし、結果表示も OpenGLES で行います。
手順説明については、その中身の技術解説というより、記事の通りにコマンド入力すればどなたでも動かせるようになることを目標に書いていきます。
■(ご参考)
・Coral Edge TPU Dev Board で TensorFlow Lite GPU Delegate V1(OpenGLES) を試す
・Coral Edge TPU Dev Board で TensorFlow Lite GPU Delegate V2 (OpenCL) を試す
#(2020/06/27追記)
本記事はもともと Tensorflow r2.0~r2.2 を前提に書いたものですが、Tensorflow r2.3 用に記述内容を全面更新しました。というのも、これまでラズパイやJetson nano用に GPUDelegateライブラリをクロスビルドするには、Tensorflow Lite 本体のソースや Makefile
にパッチをあてる必要があったものが、Bazel
コマンド一発でクロスビルドできるようになったからです。
以降、r2.2以前向けの記述を取り消し線で消しつつ、r2.3 以降向けの記述を追記しています。
さらに、64bit版のラズパイOSがリリースされたことに伴い、ラズパイ4を32bitではなく64bitで動かす想定で記述内容を更新しています。同じ aarch64 なので、ラズパイ4だけでなくJetson nano や Coral Devboard でも同じ手順を踏めば GPUDelegate を動かすことができます。
なお、GPUDelegate使用した場合と使用しない場合の性能変化は、別途この記事にまとめていますので、併せてご覧頂ければと思います。
#2. 作業手順
ラズパイで TFLite GPU Delegate アプリを動かすまでの手順として、大きく2つのステップを踏みます。
・(step1) 最初にホストPCで TFLite GPUDelegate ライブラリを armv7l aarch64 クロスビルドし、それをラズパイ実機にコピー
・(step2) ラズパイでサンプルアプリをコンパイル、上記 TFLite GPUDelegate ライブラリをリンク、実行
以下、このステップごとに説明します。
##2.1 ホストPCでの作業
ホストPCとして x86_64 Ubuntu 18.04 で作業しました。
ラズパイで使うための TensorFlow Lite ライブラリをビルドします。
###2.1.1 (前準備) Bazelのインストール
あらかじめ Bazel
をインストールしておきます。
どのバージョンの TensorFlow をビルドしたいかによって、インストールする Bazel
のバージョンも変える必要があります。今回は TensorFlow 2.0 2.3をベースに作業を行うため、Bazel 0.26.1
Bazel 3.1.0
をインストールします。
# (前準備) Bazel 3.1.0 のインストール
$ wget https://github.com/bazelbuild/bazel/releases/download/3.1.0/bazel-3.1.0-installer-linux-x86_64.sh
$ chmod 755 bazel-3.1.0-installer-linux-x86_64.sh
$ sudo ./bazel-3.1.0-installer-linux-x86_64.sh
~~Bazel 0.26.1 はこちら~~
# (前準備) Bazel 0.26.1 のインストール
$ wget https://github.com/bazelbuild/bazel/releases/download/0.26.1/bazel-0.26.1-installer-linux-x86_64.sh
$ chmod 755 bazel-0.26.1-installer-linux-x86_64.sh
$ sudo ./bazel-0.26.1-installer-linux-x86_64.sh
###2.1.2 TFLite GPUDelegate ライブラリのビルド
GPUDelegateライブラリをビルドする手順として、Android用とiOS用に関しては 公式サイトに記載があります。
ですが、ラズパイのように一般的なLinux用の GPUDelegateライブラリをビルドするのはいろいろと面倒 (詳細を知りたい方はこちら) です。
いろいろと試行錯誤した結果、コマンド一発でビルドできるようなスクリプトを用意しましたので、それを使います。
一方、ラズパイのように一般的なaarch Linux向けにビルドするには、公式サイトには記載されていませんが、Bazel
コマンドの引数として --config=elinux_aarch64
オプションを付与すればOKです。
# aarch64向けに Tensorflow Lite ライブラリをビルド (libtensorflowlite.so)
bazel build -s -c opt --config=elinux_aarch64 //tensorflow/lite:libtensorflowlite.so
# aarch64向けに GPU Delegate ライブラリをビルド (libdelegate.so)
bazel build -s -c opt --config=elinux_aarch64 --copt="-DMESA_EGL_NO_X11_HEADERS" tensorflow/lite/delegates/gpu:libtensorflowlite_gpu_delegate.so
シンブルな手順でビルドできるようになったので、上記コマンドを素直に入力してもそんなに負担にならないのですが、もっと気軽にコマンド一発で GPUDelegateライブラリをビルドできるようなスクリプトを用意しています。
$ cd ~/work
# TFLiteライブラリビルド用のスクリプトをGitHubから取得
$ git clone https://github.com/terryky/tflite_gles_app.git
# TFLiteライブラリビルド実行
$ ./tflite_gles_app/tools/scripts/tf2.3/build_libtflite_r2.3_aarch64.sh
~~Tensorflow r2.0はこちら~~
# GPUDelegate 有効版 TensorFlow Lite ライブラリをビルド
$ cd ~/work
# TFLiteライブラリビルド用のスクリプトをGitHubから取得
$ git clone https://github.com/terryky/tflite_gles_app.git
# TFLiteライブラリビルド実行
$ ./tflite_gles_app/tools/scripts/tf2.0/build_libtflite_r2.0_with_gpu_delegate_rpi.sh
スクリプトを実行すると、TensorFlow のソースコードを GitHub からダウンロードし、その次に configure
コマンドによるプロンプト待ちになります。
ここの設定はデフォルト設定で大丈夫だと思いますので、Enterを連射してください。
configure
設定が終わると、ビルドが始まります。しばらく待つと、 Tensorflow Lite 本体のライブラリである libtensorflow-lite.a
libtensorflowlite.so
と、GPUDelegateを使うためのライブラリである libtensorflowlite_gpu_delegate.so
が出来上がるはずです。
ビルドログの抜粋は下記のとおりです。
Cloning into '/home/yositeru/work/tensorflow_r2.3'...
remote: Enumerating objects: 44, done.
remote: Counting objects: 100% (44/44), done.
remote: Compressing objects: 100% (39/39), done.
remote: Total 931186 (delta 11), reused 37 (delta 5), pack-reused 931142
Receiving objects: 100% (931186/931186), 542.81 MiB | 9.07 MiB/s, done.
Resolving deltas: 100% (757135/757135), done.
Checking out files: 100% (22561/22561), done.
Branch 'r2.3' set up to track remote branch 'r2.3' from 'origin'.
Switched to a new branch 'r2.3'
Starting local Bazel server and connecting to it...
INFO: Options provided by the client:
Inherited 'common' options: --isatty=1 --terminal_columns=150
INFO: Reading rc options for 'clean' from /home/yositeru/work/tensorflow_r2.3/.bazelrc:
Inherited 'common' options: --experimental_repo_remote_exec
INFO: Reading rc options for 'clean' from /home/yositeru/work/tensorflow_r2.3/.bazelrc:
Inherited 'build' options: --apple_platform_type=macos --define framework_shared_object=true --define open_source_build=true --java_toolchain=//third_party/toolchains/java:tf_java_toolchain --host_java_toolchain=//third_party/toolchains/java:tf_java_toolchain --define=use_fast_cpp_protos=true --define=allow_oversize_protos=true --spawn_strategy=standalone -c opt --announce_rc --define=grpc_no_ares=true --noincompatible_remove_legacy_whole_archive --noincompatible_prohibit_aapt1 --enable_platform_specific_config --config=v2
INFO: Found applicable config definition build:v2 in file /home/yositeru/work/tensorflow_r2.3/.bazelrc: --define=tf_api_version=2 --action_env=TF2_BEHAVIOR=1
INFO: Found applicable config definition build:linux in file /home/yositeru/work/tensorflow_r2.3/.bazelrc: --copt=-w --define=PREFIX=/usr --define=LIBDIR=$(PREFIX)/lib --define=INCLUDEDIR=$(PREFIX)/include --cxxopt=-std=c++14 --host_cxxopt=-std=c++14 --config=dynamic_kernels
INFO: Found applicable config definition build:dynamic_kernels in file /home/yositeru/work/tensorflow_r2.3/.bazelrc: --define=dynamic_loaded_kernels=true --copt=-DAUTOLOAD_DYNAMIC_KERNELS
INFO: Starting clean (this may take a while). Consider using --async if the clean takes more than several minutes.
----------------------------------------------------
(configure) press ENTER-KEY several times.
----------------------------------------------------
You have bazel 3.1.0 installed.
Please specify the location of python. [Default is /usr/bin/python3]:
★ここでEnterを数回押してください★
(略)
----------------------------------------------------
build success.
----------------------------------------------------
-r-xr-xr-x 1 terryky terryky 3020032 6月 27 11:53 libtensorflowlite.so
-r-xr-xr-x 1 terryky terryky 4779264 6月 27 11:54 libtensorflowlite_gpu_delegate.so
~~Tensorflow r2.0 はこちら~~
Cloning into '/home/terryky/work/tensorflow_r2.0'...
remote: Enumerating objects: 11, done.
remote: Counting objects: 100% (11/11), done.
remote: Compressing objects: 100% (11/11), done.
remote: Total 817557 (delta 2), reused 9 (delta 0), pack-reused 817546
Receiving objects: 100% (817557/817557), 469.27 MiB | 5.79 MiB/s, done.
Resolving deltas: 100% (661446/661446), done.
Checking out files: 100% (19378/19378), done.
Branch 'r2.0' set up to track remote branch 'r2.0' from 'origin'.
Switched to a new branch 'r2.0'
patching file tensorflow/lite/delegates/gpu/gl/gl_buffer.cc
patching file tensorflow/lite/delegates/gpu/gl/gl_call.h
patching file tensorflow/lite/delegates/gpu/gl/gl_errors.cc
patching file tensorflow/lite/delegates/gpu/gl/gl_program.cc
patching file tensorflow/lite/delegates/gpu/gl/gl_shader.cc
patching file tensorflow/lite/delegates/gpu/gl/gl_sync.cc
patching file tensorflow/lite/delegates/gpu/gl/gpu_info.cc
patching file tensorflow/lite/delegates/gpu/gl/kernels/conv.cc
patching file tensorflow/lite/delegates/gpu/gl_delegate.cc
patching file tensorflow/lite/tools/make/Makefile
Starting local Bazel server and connecting to it...
INFO: Options provided by the client:
Inherited 'common' options: --isatty=1 --terminal_columns=161
INFO: Reading rc options for 'clean' from /home/terryky/work/tensorflow_r2.0/.bazelrc:
Inherited 'build' options: --apple_platform_type=macos --define framework_shared_object=true --define open_source_build=true --define=use_fast_cpp_protos=true --define=allow_oversize_protos=true --spawn_strategy=standalone -c opt --announce_rc --define=grpc_no_ares=true --define=PREFIX=/usr --define=LIBDIR=$(PREFIX)/lib --define=INCLUDEDIR=$(PREFIX)/include --copt=-w --config=v2
INFO: Found applicable config definition build:v2 in file /home/terryky/work/tensorflow_r2.0/.bazelrc: --define=tf_api_version=2 --action_env=TF2_BEHAVIOR=1
INFO: Starting clean (this may take a while). Consider using --async if the clean takes more than several minutes.
----------------------------------------------------
(configure) press ENTER-KEY several times.
----------------------------------------------------
WARNING: Running Bazel server needs to be killed, because the startup options are different.
WARNING: Waiting for server process to terminate (waited 5 seconds, waiting at most 60)
WARNING: --batch mode is deprecated. Please instead explicitly shut down your Bazel server using the command "bazel shutdown".
You have bazel 0.26.1 installed.
Please specify the location of python. [Default is /usr/bin/python]:
★ここでEnterを数回押してください★
(略)
----------------------------------------------------
build success.
----------------------------------------------------
合計 28696
-rw-rw-r-- 1 terryky terryky 19761050 2月 1 15:45 benchmark-lib.a
-rw-rw-r-- 1 terryky terryky 9618448 2月 1 15:45 libtensorflow-lite.a
###2.1.3 ビルドできたTFLiteライブラリをラズパイ実機へコピー
2.1.2 で作った libtensorflow-lite.a
libtensorflowlite.so
と libtensorflowlite_gpu_delegate.so
をラズパイ実機へ scp
します。
とりあえずラズパイ実機上のホーム直下にコピーしておきます。
$ scp ~/work/tensorflow_r2.3/bazel-bin/tensorflow/lite/libtensorflowlite.so pi@192.168.11.11:/home/pi/
$ scp ~/work/tensorflow_r2.3/bazel-bin/tensorflow/lite/delegates/gpu/libtensorflowlite_gpu_delegate.so pi@192.168.11.11:/home/pi/
~~Tensorflow r2.0 はこちら~~
$ scp ~/work/tensorflow_r2.0/tensorflow/lite/tools/make/gen/rpi_armv7l/lib/libtensorflow-lite.a pi@192.168.11.11:/home/pi/
##2.2 ラズパイ実機での作業
ここからはラズパイ実機上での作業となります。
ホストPCで作った libtensorflow-lite.a
libtensorflowlite.so
と libtensorflowlite_gpu_delegate.so
を使ってアプリをビルドしていきます。
###2.2.1 (前準備) OpenGLES ライブラリの更新
ラズパイにインストールされている OpenGLES ライブラリが ver 3.0 のままだと GPUDelegate は動かせないので、最新版に更新します。
$ sudo apt install libgles2-mesa-dev libegl1-mesa-dev xorg-dev
$ sudo apt update
$ sudo apt upgrade
念のため、OpenGLES 3.1 が使える状態になっていることを確認しておきましょう。
# サンプルアプリのソースをGitHubから取得
$ cd ~/work
$ git clone https://github.com/terryky/tflite_gles_app.git
$ cd tflite_gles_app/list_egl_configs
$ make TARGET_ENV=raspi4
$ ./list_egl_config
(略)
============================================
GL INFO
============================================
GL_VERSION : OpenGL ES 3.1 Mesa 19.3.2 # OpenGLES 3.1 が使える!
GL_SL_VERSION : OpenGL ES GLSL ES 3.10
GL_VENDOR : Broadcom
GL_RENDERER : V3D 4.2
(略)
###2.2.2 TFLiteヘッダファイルの取得
今回のサンプルアプリは TFLite の C++ APIを叩きます。これに必要なヘッダファイルをダウンロードします。
なお、今回のサンプルは、これらのヘッダが $HOME/work/tensorflow
以下にダウンロードされている前提で Makefile が書かれているのでご注意ください。(下記手順通りの場所に git clone
すれば大丈夫です)
# GitHub から TensorFlow のソースコードを取得
$ cd ~/work
$ git clone https://github.com/tensorflow/tensorflow.git
$ cd tensorflow
# r2.3 ブランチを使う
$ git checkout r2.3
# 依存ライブラリのヘッダをダウンロード
$ ./tensorflow/lite/tools/make/download_dependencies.sh
~~Tensorflow r2.0 はこちら~~
# GitHub から TensorFlow のソースコードを取得
$ cd ~/work
$ git clone https://github.com/tensorflow/tensorflow.git
$ cd tensorflow
# r2.0 ブランチを使う
$ git checkout r2.0
# 依存ライブラリのヘッダをダウンロード
$ ./tensorflow/lite/tools/make/download_dependencies.sh
###2.2.3_0 (Tensorflow r2.3 用に追記) TFLiteライブラリをパスの通った場所へ移動
ホストPCで作った libtensorflowlite.so
と libtensorflowlite_gpu_delegate.so
は、ラズパイのホームディレクトリに scp
しました。これを、パスの通った場所へ移動します。
今回のサンプルアプリでは、アプリビルドに必要なライブラリを探すのに ~/lib
を見に行くように設定していますので、ライブラリを ~/lib
へ移動させます。また、アプリ実行時にこの Shared ライブラリを発見できるよう、環境変数 LD_LIBRARY_PATH
にライブラリ検索パスを追加しておきます。
# ホストPCからラズパイのホームディレクトリにscpしたライブラリを ~/lib へ移動
$ mkdir ~/lib
$ mv ~/libtensorflowlite.so ~/lib
$ mv ~/libtensorflowlite_gpu_delegate.so ~/lib
# ライブラリ検索パスを登録
$ export LD_LIBRARY_PATH=~/lib:$LD_LIBRARY_PATH
###2.2.3 TFLiteサンプルアプリのビルド
準備が整ったので、いよいよアプリのビルドです。
# サンプルアプリのソースをGitHubから取得
$ cd ~/work
$ git clone https://github.com/terryky/tflite_gles_app.git
$ cd tflite_gles_app/gl2posenet
# ラズパイ4用にGPUDelegate有効にしてアプリをビルド
$ make clean
$ make TARGET_ENV=raspi4 TFLITE_DELEGATE=GPU_DELEGATEV2
# アプリ実行
$ ./gl2posenet
~~Tensorflow r2.0 はこちら~~
# サンプルアプリのソースをGitHubから取得
$ cd ~/work
$ git clone https://github.com/terryky/tflite_gles_app.git
$ cd tflite_gles_app/gl2posenet
# ホストPCでビルドした TFLite ライブラリをアプリと同じディレクトリにコピー
$ cp ~/libtensorflow-lite.a .
# ラズパイ4用にGPUDelegate有効にしてアプリをビルド
$ make clean
$ make TARGET_ENV=raspi4 TFLITE_DELEGATE=GL_DELEGATE
# アプリ実行
$ ./gl2posenet
2.2.4 実行結果
(2020/06/27追記)
GPUDelegateを使うことによる速度変化については、[こちらの記事] (https://qiita.com/terryky/items/243000fcfcb89af11510) に詳細をまとめていますので、併せてご欄頂ければと思います。
アプリを実行すると、下記のようなウィンドウが表示されると思います。各関節の位置が正しく認識できていますね。
GPUDelegate 有効にした状態で、posenet の推論にかかる処理時間は 1544[ms] でした。
(ご参考)
このサンプルアプリは、ラズパイにUSBカメラがささっていればカメラ映像で姿勢認識するように作っています。
ただ、対応ピクセルフォーマットが YUYV (YUV422) 決め打ちなので、カメラによっては動かないかもしれません。
その場合は USBカメラぬいてください。すみません。
2.2.5 (ご参考) GPUDelegate 使わない場合
アプリビルド時に、TFLITE_DELEGATE
を指定せずに make すると GPUDelegateを使わないバージョンとしてアプリをビルドすることができます。
# ラズパイ4用にアプリをビルド (GPUDelegateなし)
$ make clean
$ make TARGET_ENV=raspi4
# アプリ実行
$ ./gl2posenet
GPUDelegate を使わない場合のスクリーンキャプチャはこちら。こちらも関節位置は正しく認識できています。
一方で、推論にかかる処理時間は 214[ms] でした。ラズパイ4だと、GPUDelegate 使わないほうが速いようです。
3. 最後に
ラズパイ4で TFLite GPU Delegate を動かす具体的な手順について書きました。
ラズパイとして初となる OpenGLES 3.1 Compute Shader がハングせずに動いたことは素直に感動しましたが、残念ながらその速度性能はCPUより7倍も遅いという残念な結果となりました。
ラズパイの Compute Shader 性能については、ラズパイ用OpenGLESライブラリの開発者と思われる方の興味深い発言がありましたので引用しておきます。
https://blogs.igalia.com/itoral/2020/01/17/raspberry-pi-4-v3d-driver-gets-opengl-es-3-1-conformance/
Jochen:「TFLite GPU delegate が CPU より3~4倍も遅いんだけど」
Iago Toral:「(現状OpenGLESの実装は) WorkGroupが1個しか動かないようにハードコーディングしてるよ。この数を増やして並列度を高めれば性能あがるだろうから、そのうち調べないとね」
現状のOpenGLES実装は、GPUハードが持つ演算性能全てを引き出しているわけではないようです。開発者の方も OpenGLES の性能チューニングの必要性を認識されているとのこと。期待して待とうと思います。