LoginSignup
193
169

More than 3 years have passed since last update.

Raspberry Pi4 単体で TensorFlow Lite はどれくらいの速度で動く?【2020年12月版】

Last updated at Posted at 2020-12-12

1. はじめに

 今から半年前の2020年6月、ラズパイ4上でTensorFlow Liteを使った様々なAI認識アプリを動かしてみて、その動作速度をまとめました
 当時のTensorFlowはバージョン2.2でしたが、現在は 2.4(rc4) へと進んでいます。進化が極めて速いDeepLearningの世界において、この半年間でTensorFlow Liteはどう変化したでしょうか。もし「手持ちのアプリは何も変えてないのに、TensorFlow Liteを新しくするだけでめっちゃ速く動くようになったぜ」というのだと嬉しいですよね。
 本記事では、前回計測に用いたアプリを再び最新版のTensorFlow Lite環境で動かしてみて、この半年間で動作速度にどれくらい変化があったのかを比較検証していきます。

1.1 速度評価の観点

 速度計測を行うにあたって、まず前回同様に
  1)演算精度をfp32からint8に量子化することで速くなるか
    (演算精度を妥協することで高速化できるか)
  2)CPUではなくGPUで演算することで速くなるか
    (一般的にDeepLearningが得意とされるGPUを使えばラズパイでも高速化できるか)
という観点で計測します。さらに今回は
  3)TensorFlow Lite に新規導入された XNNPACK (後述) を使うことで速くなるか
という観点を加えた3つの評価軸で検証を行いました。

 今回の計測条件をまとめると下記のようになります。

条件 前回 (2020/6) 今回 (2020/12)
フレームワーク TensorFlow Lite 2.2 C++ TensorFlow Lite 2.4 C++
ターゲット環境 ラズパイ4 (Raspberry Pi OS 64bit) 同左
(1) 推論精度 (a) fp32 (GPU Delegate時は fp16) 同左
(b) int8 同左
(2) GPU使用 (a) GPU 使う (GPU Delegate) 同左
(b) GPU 使わない 同左
(3) XNNPACK 未導入 (a) XNNPACK使う
(b) XNNPACK使わない

2. XNNPACKについて

 前回計測時からの大きな変化として、TensorFlow 2.3 で導入されたXNNPACK 1 があります。
 TensorFlowの公式ブログによれば、XNNPACK
 ・CPUアーキテクチャ毎の専用命令セット2 による最適化
 ・tfliteモデルを構成する複数の演算子を融合(fusion)することによる最適化
の効果により、「floatでの推論を平均で2.3倍高速化できる」とのことです。
 はたしてラズパイでもこの宣伝文句通りに高速化されるのか、実際に使って評価してみましょう。

2.1 XNNPACKを使うには

 XNNPACK はTensorFlow Liteに導入はされているものの、現在デフォルトでは有効になっていません3リリースノートにも opt-in support for XNNPACK と記述があるように 、使用するためには明示的に「opt in」する必要があります。

 具体的には、公式ドキュメントに従い下記2つの作業を行う必要があります。4

  作業1) TensorFlow Liteライブラリビルド時に --define tflite_with_xnnpack=true オプションを追加
  作業2) アプリのTensorFlow Lite初期化処理で XNNPackDelegate が使用するスレッド数を指定

 作業1)については、ラズパイ4(aarch64)用のTensorFlow Liteライブラリをお手軽にビルドするスクリプトファイルがありますのでご参考になさってください。

$ git clone https://github.com/terryky/tflite_gles_app.git
$ ./tflite_gles_app/tools/scripts/tf2.4/build_libtflite_r2.4_with_xnnpack_aarch64.sh

(ご参考:XNNPACKを有効化しない場合のライブラリビルドスクリプト)
$ ./tflite_gles_app/tools/scripts/tf2.4/build_libtflite_r2.4_aarch64.sh

 作業2)の具体コード例としては、今回計測に用いたアプリの該当コードをご参考になさってください。このコードはアプリビルド時にTFLITE_DELEGATE=XNNPACK を追加することで有効になります。

$ git clone https://github.com/terryky/tflite_gles_app.git
$ cd tflite_gles_app
$ make TFLITE_DELEGATE=XNNPACK

(ご参考:XNNPACKを有効化しない場合のアプリビルドコマンド)
$ make

3. TensorFlow Lite 速度計測

 それではTensorFlow Liteの性能計測に移ります。計測対象は前回計測時と全く同じ7種類のモデルを用い、冒頭記載の条件で性能計測を行います。
 計測結果は下図のような棒グラフで示していきますが、はじめにグラフの見方を説明しておきます。

intro.png

 横軸として、青/赤/橙/緑 の4色棒グラフが2セットあります。これらはそれぞれ下記条件での計測結果であり、棒が長いほうが速度性能が高く(=短時間で処理が完了する)、フレームレートが高いことを示します。グラフ左半分には前回計測結果をそのまま再掲し、右半分に今回の計測結果を並べることで、速度差を比較しやすいようにしています。

説明
オリジナルのfloatモデルで推論実行したときの速度[fps]
int8量子化モデルで推論実行したときの速度[fps]
XNNPACK有効にした時の速度[fps]。モデルはオリジナルのfloatモデルを使用
GPU Delegate有効にしたときの速度[fps]。モデルはオリジナルのfloatモデルを使用

 ※前回計測に XNNPACK はないので、グラフ左側に橙色の棒グラフはありません。

 なおここでいうfpsは、純粋に TensorFlow Lite の invoke() にかかった時間の逆数で求まるフレームレートです。実際の推論動作には、TensorFlow Lite 実行前に画像リサイズ等の前処理や、推論結果の描画処理が必要となりますが、このグラフには含まれていません。前処理や後処理は合計で 10ms 程度で完了することが多く、計測結果の大勢に影響はないと判断したためです。

 次節からは各モデルごとの計測結果を書いていきます。各モデルの説明文や画像は前回記事と重複もありますがご了承下さい。

3.1 BlazeFace

 mediapipeで公開されている、高速動作を目的とした顔検出モデルです。
 顔のバウンダリ領域だけでなく、目・鼻・口のキーポイント位置も検出できるので、およその顔の向きもあわせて推定することができます。後述の Facemesh のような他のモデルを実行するための前処理として顔領域を切り出す目的でも使用されます。

gl2blazeface.jpg

 このモデルをTensorFLow2.4で動かした時の処理速度を前回(TensorFlow2.2)と比較すると、下記グラフとなります。

1_blazeface

  • 計測結果から読み取れること
    • TensorFlow Liteを2.2→2.4に更新するだけで、floatモデルの推論速度が 1.8倍速くなる
    • 加えてXNNPACKを有効にすると、そこからさらに1.9倍速くなる。
    • GPU Delegateの速度は前回と変化なし

 なんとXNNPACKは前回最速チャンピオンだった int8量子化モデルよりも速いという鮮烈なデビューを果たしました。すなわち、演算精度を落とすことなく高速化を達成できていることになります。これまで処理速度と演算精度はバーター関係にあると思っていましたが、この結果はその反例になります。TensorFlowの公式ブログにあるような「XNNPACKにより2.3倍の高速化」にせまる劇的な速度改善がみられました。
 なお、XNNPACKを使わない場合でも、TensorFlow Liteを 2.2→2.4 に更新するだけで 1.8倍の高速化効果が得られるようです。アプリを一切変更せずとも、いわばフリーランチで高速化の恩恵にあずかれるのは嬉しいですね。
 
 Blazefaceはもともと軽量モデルである上に、XNNPACKを使うことで驚異の200fpsというスコアをたたき出すことができました。前処理や描画結果も含めても 一般的なDisplayの表示周波数である60fps上限に余裕で到達 させることができます。

ラズパイ4で実際に動いている様子(前回計測時のものを再掲):

3.2 Facemesh

 上記Blazefaceで切り出した顔画像を入力として、さらに表情を構成する468個のキーポイント位置を推定することができるモデルです。mediapipeで公開されています。
 キーポイント位置にあわせて他の顔画像とさしかえて別人に変身したり、表情の動きに合わせてキャラクタを動かすようなエフェクトが実現できるようになります。
 

↓左側の顔画像上に表示されているメッシュが推論結果です。右側は別人の顔画像をこのメッシュに合わせて変形させたものになります。これを左側の顔画像の位置に重ねて描画すれば別人に変身できる、というわけです。
facemesh.jpg

 処理速度の計測結果は下記となりました。
 グラフの数値はBlazefaceによる顔領域検出とFacemeshによるメッシュ推定の2つモデルの実行時間合計値です。

2_facemesh

  • 計測結果から読み取れること
    • TensorFlow Liteを2.2→2.4に更新するだけで、floatモデルの推論速度が 1.8倍速くなる
    • XNNPACKを有効にするとそこからさらに1.1倍速くなる。
    • GPU Delegateの速度は前回と変化なし

 Blazeface ほどのインパクトはないものの、このモデルでもXNNPACKが最速となりました。XNNPACKを使わない場合でもTensorFlow Liteを更新するだけでfloatモデルは1.8倍速になります。
 一方前回最速だったint8量子化モデルは、性能改善比率が floatモデルと比べて緩やかであり、その結果 floatモデルとint8量子化モデルとで速度逆転が発生しています。本来int8量子化モデルは、「演算精度が多少低下しても良いから高速動作をめざす」ことが目的であるにもかかわらず、速度でもfloatモデルに勝てないという意外な結果となっています。

ラズパイ4で実際に動いている様子(前回計測時のものを再掲):

3.3 3D Handpose

 引き続きmediapipeで公開されているモデルです。手・指の姿勢形状を3次元的に推定することができます。比較的安定して手形状を推測できるので見た目にも楽しいです。
 実際のユースケースでは、このモデルで手の姿勢形状推定する前に、Palm検出用の別モデルを使って手領域画像の切り出しを行うことが多いですが、ここでは手領域切り出しが終わった前提で(手がカメラに大写しになるように位置調整して)Handpose単体モデルの性能をみることにします。

ラズパイ4で実際に動いている様子(前回計測時のものを再掲):
gl2handpose_mov.gif

 処理速度の計測結果は下記となりました。

3_handpose

  • 計測結果から読み取れること
    • TensorFlow Liteを2.2→2.4に更新するだけで、floatモデルの推論速度が 1.6倍速くなる
    • int8量子化モデルはfloatモデルに対し1.5倍速い
    • XNNPACKを有効にしても速度改善せず逆に遅くなる
    • GPU Delegateの速度は前回と変化なし

 これまでと傾向が異なります。XNNPACKが振るわず、int8量子化が最速となりました。モデルによってXNNPACKが得意とするものや苦手なものがあるのでしょうか。あるいはモデルにXNNPACKがサポートしていない演算子が含まれているのでしょうか。 (改めて確認しましたが、XNNPACKサポート外の演算子は含まれていないように見えます)
 int8量子化が最速ではあるものの、TensorFlow Lite 2.2からの性能向上比率でみれば、floatモデルが 1.6倍速になっているのに対し int8量子化モデルは1.2倍速に留まっており、floatモデルとint8量子化モデルとの速度差は減少しているといえます。

3.4 Posenet

 TensorFlow Lite のチュートリアルで公開されているモデルです。人間の各関節位置を推定することができます。複数の人間の姿勢を同時に推定することもできます。

ラズパイ4で実際に動いている様子(前回計測時のものを再掲):
gl2posenet_mov.gif

 処理速度の計測結果は下記となりました。

4_posenet

  • 計測結果から読み取れること
    • TensorFlow Liteを2.2→2.4に更新するだけで、floatモデルの推論速度が 1.7倍速くなる
    • XNNPACKを有効にしても速度改善せず逆に遅くなる
    • GPU Delegateの速度は前回と変化なし

 Handposeと同じ傾向です。TensorFlow Liteを更新するだけでfloatモデルが1.7倍に高速化される一方、XNNPACKによる速度改善効果は見られません。なお、あいにくこのモデルは量子化モデルで動かせておらず5計測結果がありません。

3.5 Hair segmentation & recoloring

 mediapipeで公開されている、髪の毛領域を画素単位で抽出することができるモデルです。抽出した領域に任意の色を乗算することで、仮想的に髪の毛をカラーリングして遊ぶことができます。

gl2hair_segmentation.jpg

 処理速度の計測結果は下記となりました。

hair_segmentation_hikaku.png

  • 計測結果から読み取れること
    • TensorFlow Liteを2.2→2.4に更新すると、floatモデルの推論速度は 1.1倍だけ速くなる
    • XNNPACKを有効にするとそこからさらに 5.5倍速く なる
    • GPU Delegateの速度は前回と変化なしだが、CPUによる演算(XNNPACKはOFF)よりも速い

 このモデルは他のモデルと速度傾向が大きく異なります。TensoFlow Lite 2.2→2.4更新にともなってフリーランチ的に高速化される比率は 1.1倍と少ない一方で、XNNPACKを有効にするとさらに5.5倍にもなります。その要因はこのモデル内部に含まれるカスタムOperationがXNNPACKにピンポイントに効いているのではないかと思います。
 またこのモデルは、ラズパイ4においてGPU Delegate有効時の性能がCPUによる演算(XNNPACKはOFF)よりも向上する珍しいモデルですが、その要因も同じくカスタムOperationがGPU Delegateに効いているのかもしれません。

ラズパイ4で実際に動いている様子(前回計測時のものを再掲):

3.6 3D Object detection

 これもmediapipeで公開されているモデルです。物体の位置姿勢を3次元的に検出することができるという、にわかには信じがたいことが出来てしまいます。
 今回使ったモデルでは「椅子」の3次元位置姿勢を検出することができるように学習されています。

ラズパイ4で実際に動いている様子(前回計測時のものを再掲):
gl2objectron_mov.gif

処理速度の計測結果は下記となりました。

6_3d_object_detection

  • 計測結果から読み取れること
    • TensorFlow Liteを2.2→2.4に更新するだけで、floatモデルの推論速度は 1.5倍速くなる
    • XNNPACKを有効にするとそこからさらに 1.4倍速く なる
    • int8量子化モデルはfloatモデルより 2.0倍速い
    • GPU Delegateの速度は前回と変化なし

 int8量子化モデルがfloatモデルの2倍速となり、XNNPACKを抑えて最速となりました。
 一方int8量子化モデル同士で比較するとTensorFlow Lite 2.2時代と比較して1.1倍にしか速くなっていません。これに対しfloatモデルは1.5倍速、XNNPACKでさらに1.4倍速になっていますので、floatモデルとint8量子化モデルとの速度差は相対的に小さくなっています。

3.7 Artistic styletransfer

 TensorFlow Lite models で公開されているモデルです。好きな写真を、ムンクなどの特徴的な画家の画風に変換することができるというモデルです。普通に撮影した写真が全く異なる印象に変化するので見ていて楽しいです。

gl2style_transfer.png

 処理速度の計測結果は下記となりました。

7_styetransfer

  • 計測結果から読み取れること
    • TensorFlow Liteを2.2→2.4に更新すると、floatモデルの推論速度は 0.4倍に遅くなる
    • XNNPACKを有効にするとそこから 4.1倍速く なる
    • int8量子化モデルはfloatモデルより 2.5倍速い
    • GPU Delegateの速度は前回と変化なし

 TensorFlow Lite 2.2→2.4 へ更新することで floatモデル, int8量子化モデルともに遅くなるという不可解な結果となりました。要因として考えられるのは、このモデルがGPUで動作させることを前提に重みをfp16精度で保持しているからかもしれません。一方で、XNNPACKによる挽回でなんとかTensorFlow Lite 2.2よりも高速動作させることが出来るようです。

4. まとめ

 前節で示したグラフを、縦軸のスケールを揃えて一覧表示してみるとこのようになります。

all.png

 モデルによって処理速度はまちまちですが、TensorFlow Liteを2.2→2.4に更新すれば全てのモデルで高速化(1.3~3.9倍速)されることがわかりました。とりわけ、XNNPACKの導入によりfloatモデルの処理速度が大きく改善され、

  モデルによってはint8量子化するよりもfloatモデルのまま処理したほうが速い

という逆転現象が起こっていることは注目です。事実、上記7つのモデルのうちint8量子化モデルが最速になるもの(上図でfpsが赤色文字で表示されているもの)は、前回計測時は5個を占めていたのに対し、今回2個に減っています。さらにその2個においても、int8量子化モデルのfloatモデルに対する速度優位性は前回よりも小さくなっています。XNNPACKのFuture Workとして量子化モデルの改善が挙げられているので、int8量子化モデルに対する最適化はこれからなのかもしれませんが、ひょっとすると今後、floatモデルの処理速度が十分に速いとなれば、量子化に伴う精度劣化に頭を悩ますことが減るかもしれません6

 ひとつ注意が必要なのは、「XNNPACK を使えば全てのfloatモデルが速くなる」というわけではないということです。handposeのように逆に遅くなるモデルも存在します。現時点7において XNNPACK がデフォルトで有効になっていないのはこのような速度デグレがあるからかもしれません。XNNPACKを使うかどうかはモデルごと/ターゲットプラットフォームごとに実測して判断する必要がありそうです。

 今回殆ど話題にあがらなかったGPU Delegateに関しては、TensorFlow Lite2.4 においてもラズパイ4では性能向上に寄与しないという結果となりました。GPU Delegateにはver1(OpenGLESバックエンド)とver2(OpenCLバックエンド)がありますが、この半年で進化を続けているのはver2のみのようです。ラズパイは残念ながらOpenCLをサポートしておらず GPU Delegate ver1を使わざるをえないのですが、ver1は実質的に進化が止まっているため、今後もラズパイにおいて GPU Delegate による性能改善は期待薄だと思われます8

5. 最後に

 最新のTensorFlow Liteを使っていろいろなモデルをラズパイ4で動かしてみました。前回計測結果との比較を通して「アプリに何も手を入れなくても勝手に速くなっている」という状況をまのあたりにして、TensorFlow Liteの進化の速さに驚きました。

 個人的にはラズパイのGPU Delegateがいっこうに振るわないのが残念なのですが、(ハードウェアを変更することなく)ソフトウェアの進化だけでここまで処理速度が改善されていくさまを見ると、この先もまだまだ伸びしろがあるのではと期待してしまいます。私自身も知識のアップデートを怠らないように、引き続き定期的にTensorFlow Liteの進化を見届けようと思います。

Appendix: 測定環境・計測アプリのソースコード・その他

Appendix1: 測定環境の情報

今回計測を行ったラズパイ4のOSおよびOpenGLESのバージョンは下記のとおりです。

pi@raspberrypi:~$ uname -a
Linux raspberrypi 5.4.51-v8+ #1327 SMP PREEMPT Thu Jul 23 11:11:34 BST 2020 aarch64 GNU/Linux

pi@raspberrypi:~ $ lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description:    Debian GNU/Linux 10 (buster)
Release:        10
Codename:       buster

pi@raspberrypi:~ $ cat /proc/device-tree/model
Raspberry Pi 4 Model B Rev 1.1

GL_VENDOR:     Broadcom
GL_RENDERER:   V3D 4.2
GL_VERSION:    OpenGL ES 3.1 Mesa 19.3.2

Appendix2: 計測プログラムについて

 今回計測に用いたソースコードは下記にあります。
  https://github.com/terryky/tflite_gles_app

 このレポジトリは、世の中に公開されている学習済みモデルをラズパイやJetson nano等のシングルボードコンピュータで動かすことを目的にしたアプリ集となっています。今回性能計測対象としたアプリの他にも下記のようなアプリが含まれています。全てラズパイ4で動作確認済みですので興味のあるかたはご覧になってみてください。なお、モデルのtflite化には@PINTOさんの https://github.com/PINTO0309/PINTO_model_zoo にお世話になっています。いつもありがとうございます。

Appendix2.1 DBFace

 こちらで公開されている高精度な顔検出モデルです。blazefaceでは検出できないような小さい顔領域も検出することが出来ます。入力画像の解像度を変更することで検出精度と速度のバランスをとることもできます。

Appendix2.2 Age Gender Estimation

 画像にうつる人物それぞれの年齢と性別を判定することができます。こちらで公開されている学習済みモデルを利用させて頂いています。(the IMDB-WIKI datasetライセンスにご留意ください)
age_gender_estimation

Appendix2.3 Iris Detection

 画像にうつる人物の瞳の位置を検出することが出来ます。1)blazefaceで顔領域切り出し 2)facemeshで目領域切り出し 3)瞳位置検出 と3つのtfliteモデルを直列に動かすことで瞳検出を実現しています。mediapipeで公開されているモデルです。
iris_detection

Appendix2.4 Blazepose

 上体の回転角度を補正してから姿勢推定することで、ヨガのように複雑なポーズをとった人物に対しても安定して姿勢推定することができます。1)上体検出, 2)姿勢検出 と2つのtfliteモデルを動かす必要があるので posenet よりは計算負荷が高いです。これもmediapipeで公開されているモデルです。
blazepose

Appendix2.5 3D Human Pose Estimation

 画像にうつる人物の姿勢を3次元的に推定することができます。3次元情報が取得できるのでお気に入りのキャラクタを動かすこともできるようになります(このアプリは棒人間ですが)。こちらのモデルを利用させて頂いています。
 gl2pose_estimation_3d_mov.gif

Appendix2.6 Depth Estimation (DenseDepth)

 室内画像を入力すると画素毎の深度情報を推定することができます。ポイントクラウドのようにぐりぐりと見渡すことができます。こちらで公開されているモデルです。
styetransfer_hikaku.png

Appendix2.7 Face Segmentation

 顔領域を検出したのち、さらに「目」「鼻」「口」「眉」「頭髪」といった顔パーツを画素単位で分類することができます。こちらで公開されているモデルです。
face_segmentation

Appendix2.8 Selfie to Anime

 顔画像をアニメ調に変換することができます。見た目のインパクトは大きいですがラズパイには処理負荷が高いです。こちらのモデルを利用させて頂いています。

Appendix2.9 U^2-Net portrait drawing

 顔画像をマンガのような線画に変換することができます。このモデルもラズパイだと1コマ10秒程度と負荷が高いです。こちらで公開されているモデルです。
u2net

Appendix2.10 Text Detection

 TensorFlow Hubで公開されているモデルです。画像内の文字領域を検出することができます。OCR(文字起こし)の前処理として使えそうです。

text_detection

上記で紹介したモデルについてもいずれ性能評価記事としてまとめようと考えています。

Appendix3: インターフェース誌による性能計測の記事

 インターフェース誌2020年10月号にて、今回計測に用いたソースコードと同じものを使った性能評価の記事が掲載されています。インターフェース誌の記事では、私が計測をすっ飛ばしたTensorFlow Lite 2.3 環境で計測されていますので9、行間を埋める情報として有用だと思います。また、ラズパイだけでなく Jetson nano, Jetson Xavier NX, Coral Dev Board といったデバイスごとの性能比較も実施されていますので、興味のある方はご覧になってみてください10

interface

 本記事の内容が皆さんのお役に立てれば幸いです。
 引き続きラズパイ&AIライフを楽しんでいきましょう!


  1. https://github.com/google/XNNPACK 

  2. ARMの場合は NEON, x86-64の場合は SSE2, SSE4, AVX, AVX2, AVX512 

  3. TensorFlow 2.4 rc4時点。将来的にはデフォルトで有効になると思われます。 

  4. 公式ドキュメントには、「作業1を行えばTensorFlow LiteはデフォルトでXNNPACKを使うようになる。作業2は非推奨」と記述されています。ですが、私がラズパイで試した限り、作業2で明示的にスレッド数を指定をしないとXNNPACKがシングルスレッド動作となり高速動作しませんでした。 

  5. 正確には、int8量子化モデルで動かしてみたものの精度劣化が激しく比較対象として適切でない、と判断しました。 

  6. あくまでラズパイ単体動作を考えた場合の話です。Coral Edge TPU など整数演算しかサポートしないAIアクセラレータを活用する場合には引き続きint量子化が必要となります。また、(演算器の性能観点とは異なり)メモリ容量やメモリバス帯域の観点からint量子化するほうが優位になる場合もあるでしょう。 

  7. TensorFlow 2.4-rc4 

  8. TensorFlow Lite側の進化が止まっていたとしても、ラズパイのOpenGLESライブラリ本体の進化により高速化する可能性はゼロではないです。なお、熱や消費電力の観点からあえてGPUにオフロードするという使い方は想定されると思います。 

  9. TensorFlow Lite 2.3 環境での計測ですが、XNNPACKへの言及はないようです。 

  10. 私はinterface誌の関係者ではありません。念のため。 

193
169
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
193
169