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


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

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

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

|  条件          |  前回 (2020/6)  |  今回 (2020/12) |
|----------------|-----------|-----------|
|フレームワーク   | **TensorFlow Lite 2.2 C++** | **TensorFlow Lite 2.4 C++** 
|ターゲット環境   |**ラズパイ４** (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 で導入](https://github.com/tensorflow/tensorflow/releases/tag/v2.3.0-rc0)された```XNNPACK``` [^xnnpack] があります。
　TensorFlowの[公式ブログ](https://blog.tensorflow.org/2020/07/accelerating-tensorflow-lite-xnnpack-integration.html)によれば、```XNNPACK``` は
　・CPUアーキテクチャ毎の専用命令セット[^iss] による最適化
　・tfliteモデルを構成する複数の演算子を融合(fusion)することによる最適化
の効果により、**「floatでの推論を平均で2.3倍高速化できる」**とのことです。
　はたしてラズパイでもこの宣伝文句通りに高速化されるのか、実際に使って評価してみましょう。


[^xnnpack]: https://github.com/google/XNNPACK
[^iss]: ARMの場合は NEON, x86-64の場合は SSE2, SSE4, AVX, AVX2, AVX512

##2.1 XNNPACKを使うには
　```XNNPACK``` はTensorFlow Liteに導入はされているものの、現在デフォルトでは有効になっていません[^optin]。 [リリースノート](https://github.com/tensorflow/tensorflow/blob/v2.3.1/RELEASE.md#release-230)にも ```opt-in support for XNNPACK``` と記述があるように 、使用するためには明示的に「opt in」する必要があります。

[^optin]: TensorFlow 2.4 rc4時点。将来的にはデフォルトで有効になると思われます。

　具体的には、[公式ドキュメント](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/delegates/xnnpack/README.md)に従い下記２つの作業を行う必要があります。[^enable_xnnpack]

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

　**作業１）**については、ラズパイ４(aarch64)用のTensorFlow Liteライブラリをお手軽にビルドする[スクリプトファイル](https://github.com/terryky/tflite_gles_app/blob/master/tools/scripts/tf2.4/build_libtflite_r2.4_with_xnnpack_aarch64.sh)がありますのでご参考になさってください。

```sh
$ 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
```

　**作業２）**の具体コード例としては、今回計測に用いたアプリの[該当コード](https://github.com/terryky/tflite_gles_app/blob/a8bea1c84e0d0fd07d9c8cfac68be8503099f0d6/common/util_tflite.cpp#L209-L229)をご参考になさってください。このコードはアプリビルド時に```TFLITE_DELEGATE=XNNPACK``` を追加することで有効になります。

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

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


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





#3. TensorFlow Lite 速度計測
　それではTensorFlow Liteの性能計測に移ります。計測対象は[前回計測時](https://qiita.com/terryky/items/243000fcfcb89af11510)と全く同じ７種類のモデルを用い、冒頭記載の条件で性能計測を行います。
　計測結果は下図のような棒グラフで示していきますが、はじめにグラフの見方を説明しておきます。


<img width="400" alt="intro.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265135/28578770-0750-1218-8f44-6f16f50c9bd0.png">

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

|色 | 説明 | 
|---| --------- |
|青 |オリジナルのfloatモデルで推論実行したときの速度[fps]|
|赤 |int8量子化モデルで推論実行したときの速度[fps]|
|橙 |XNNPACK有効にした時の速度[fps]。モデルはオリジナルのfloatモデルを使用 |
|緑 |GPU Delegate有効にしたときの速度[fps]。モデルはオリジナルのfloatモデルを使用 |
　※前回計測に ```XNNPACK``` はないので、グラフ左側に橙色の棒グラフはありません。

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

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

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

<img width="400" alt="gl2blazeface.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265135/44357faf-268e-a409-2b43-cd9e18a8050b.jpeg">

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

<img width="400" alt="1_blazeface" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265135/8b37338e-debc-ab4a-88ce-7424c4eca0d9.png">


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

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

ラズパイ４で実際に動いている様子(前回計測時のものを再掲)：
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://twitter.com/PINTO03091?ref_src=twsrc%5Etfw">@PINTO03091</a> さんの量子化済みblazefaceモデルをお借りして、ラズパイ４で顔検出 60fps 到達できた！<br>（カメラ処理・描画処理込み）<br><br>『スピードに生きる』 <a href="https://t.co/e4uZwy6zIv">pic.twitter.com/e4uZwy6zIv</a></p>&mdash; terryky (@terryky1220) <a href="https://twitter.com/terryky1220/status/1261544700688777216?ref_src=twsrc%5Etfw">May 16, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>



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

↓左側の顔画像上に表示されているメッシュが推論結果です。右側は別人の顔画像をこのメッシュに合わせて変形させたものになります。これを左側の顔画像の位置に重ねて描画すれば別人に変身できる、というわけです。
<img width="600" alt="facemesh.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265135/d120f68f-7dbd-a33e-354a-352df8d7386f.png">

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


<img width="400" alt="2_facemesh" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265135/4ff44263-35f2-5fd9-b0c6-e68be5b20449.png">


- **計測結果から読み取れること**
    - 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モデルに勝てないという意外な結果となっています。

ラズパイ４で実際に動いている様子(前回計測時のものを再掲)：
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">ラズパイ４単体で<br>　1) H264 Videoデコード<br>　2) TensorFlow Lite (facemesh) で表情姿勢推定<br>　3) 画像メッシュ変形でフェイスマスク描画<br>を一気通貫処理してみるテスト。<br>思ってたよりサクサク動きました。 <a href="https://t.co/4XTFzBggrk">pic.twitter.com/4XTFzBggrk</a></p>&mdash; terryky (@terryky1220) <a href="https://twitter.com/terryky1220/status/1265663436697948160?ref_src=twsrc%5Etfw">May 27, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>




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

ラズパイ４で実際に動いている様子(前回計測時のものを再掲)：
![gl2handpose_mov.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265135/8db45392-6531-dd78-d970-7fdc7df79b26.gif)

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

<img width="400" alt="3_handpose" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265135/5bb54396-24b6-57c3-7544-9a40a5a44b86.png">

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

　これまでと傾向が異なります。XNNPACKが振るわず、**int8量子化が最速**となりました。モデルによってXNNPACKが得意とするものや苦手なものがあるのでしょうか。~~あるいはモデルに[XNNPACKがサポートしていない演算子](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/delegates/xnnpack/README.md#limitations-and-supported-operators)が含まれているのでしょうか。~~　(改めて確認しましたが、XNNPACKサポート外の演算子は含まれていないように見えます)
　int8量子化が最速ではあるものの、TensorFlow Lite 2.2からの性能向上比率でみれば、floatモデルが 1.6倍速になっているのに対し int8量子化モデルは1.2倍速に留まっており、floatモデルとint8量子化モデルとの速度差は減少しているといえます。


##3.4 Posenet
　[TensorFlow Lite のチュートリアル](https://www.tensorflow.org/lite/models/pose_estimation/overview)で公開されているモデルです。人間の各関節位置を推定することができます。複数の人間の姿勢を同時に推定することもできます。

ラズパイ４で実際に動いている様子(前回計測時のものを再掲)：
<img width="400" alt="gl2posenet_mov.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265135/b2dc0257-4e7e-da9d-b66d-81b3aca98d6a.gif">


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

<img width="400" alt="4_posenet" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265135/6499bdf7-15f1-9ea9-1d36-a4a29d989e7c.png">

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

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

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


##3.5 Hair segmentation & recoloring
　[mediapipe](https://github.com/google/mediapipe/tree/master/mediapipe/models)で公開されている、髪の毛領域を画素単位で抽出することができるモデルです。抽出した領域に任意の色を乗算することで、仮想的に髪の毛をカラーリングして遊ぶことができます。

<img width="600" alt="gl2hair_segmentation.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265135/e163c66e-34d8-e965-a46e-cecf4ddcd36d.jpeg">

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

<img width="400" alt="hair_segmentation_hikaku.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265135/975ca476-23e0-3b3a-eab9-be35a05f23fe.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にピンポイントに効いているのではないかと思います。
　またこのモデルは、ラズパイ４においてGPU Delegate有効時の性能がCPUによる演算(XNNPACKはOFF)よりも向上する珍しいモデルですが、その要因も同じくカスタムOperationがGPU Delegateに効いているのかもしれません。


ラズパイ４で実際に動いている様子(前回計測時のものを再掲)：
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">この hair segmentation をラズパイ４の TFLite GPUDelegate で試してみた。<br>秒３コマくらいの速度だけど、GPU性能が Jetson Nano の 1/10 しかないから妥当かな。<br>ちなみに、ラズパイ４で GPUDelegateのほうが CPU より速いモデルはこれが初体験。(約2倍速)<br>それだけカスタムOpのCPU実行は重いのね。 <a href="https://t.co/Ah0Jewc3Q3">https://t.co/Ah0Jewc3Q3</a> <a href="https://t.co/cyTy1GTbG8">pic.twitter.com/cyTy1GTbG8</a></p>&mdash; terryky (@terryky1220) <a href="https://twitter.com/terryky1220/status/1267786065605062656?ref_src=twsrc%5Etfw">June 2, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


##3.6 3D Object detection
　これも[mediapipe](https://github.com/google/mediapipe/tree/master/mediapipe/models)で公開されているモデルです。物体の位置姿勢を３次元的に検出することができるという、にわかには信じがたいことが出来てしまいます。
　今回使ったモデルでは「椅子」の３次元位置姿勢を検出することができるように学習されています。

ラズパイ４で実際に動いている様子(前回計測時のものを再掲)：
<img width="400" alt="gl2objectron_mov.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265135/f292b384-9b08-a632-b6e9-97657a129a23.gif">


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

<img width="400" alt="6_3d_object_detection" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265135/8f8ac8ec-e5f0-0d8f-b33b-165f21524fb6.png">

- **計測結果から読み取れること**
    - 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](https://www.tensorflow.org/lite/models/style_transfer/overview) で公開されているモデルです。好きな写真を、ムンクなどの特徴的な画家の画風に変換することができるというモデルです。普通に撮影した写真が全く異なる印象に変化するので見ていて楽しいです。

<img width="600" alt="gl2style_transfer.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265135/42845bb0-e823-cc4d-6601-34cc57db39b2.png">

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

<img width="400" alt="7_styetransfer" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265135/10ad19f5-f0a4-8c49-b581-f7184f7dd681.png">

- **計測結果から読み取れること**
    - 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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265135/ecb6a8c2-abb0-830f-f48e-b7f1dadfd2af.png)


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

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

という逆転現象が起こっていることは注目です。事実、上記７つのモデルのうちint8量子化モデルが最速になるもの(上図でfpsが赤色文字で表示されているもの)は、前回計測時は５個を占めていたのに対し、今回２個に減っています。さらにその２個においても、int8量子化モデルのfloatモデルに対する速度優位性は前回よりも小さくなっています。XNNPACKの[Future Work](https://blog.tensorflow.org/2020/07/accelerating-tensorflow-lite-xnnpack-integration.html)として量子化モデルの改善が挙げられているので、int8量子化モデルに対する最適化はこれからなのかもしれませんが、ひょっとすると今後、floatモデルの処理速度が十分に速いとなれば、量子化に伴う精度劣化に頭を悩ますことが減るかもしれません[^int_quant]。

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

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

[^tf2.4]: TensorFlow 2.4-rc4

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

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


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

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

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

## Appendix1: 測定環境の情報

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


#### Appendix2.1 DBFace
　[こちら](https://github.com/dlunion/DBFace)で公開されている高精度な顔検出モデルです。blazefaceでは検出できないような小さい顔領域も検出することが出来ます。入力画像の解像度を変更することで検出精度と速度のバランスをとることもできます。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Jetson nano の TensorRT で DBFace(640x480) を動かせました。描画込みで1コマ約100[ms]。<br>TFLite GPUDelegate だと約180[ms] だったので、TensorRT のほうが2倍弱速いようです。 <a href="https://t.co/uVVtEib06q">https://t.co/uVVtEib06q</a> <a href="https://t.co/s7XtaxMztm">pic.twitter.com/s7XtaxMztm</a></p>&mdash; terryky (@terryky1220) <a href="https://twitter.com/terryky1220/status/1301519745401974787?ref_src=twsrc%5Etfw">September 3, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

#### Appendix2.2 Age Gender Estimation
　画像にうつる人物それぞれの年齢と性別を判定することができます。[こちら](https://github.com/yu4u/age-gender-estimation)で公開されている学習済みモデルを利用させて頂いています。([the IMDB-WIKI dataset](https://data.vision.ee.ethz.ch/cvl/rrothe/imdb-wiki/)ライセンスにご留意ください)
<img width="300" alt="age_gender_estimation" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265135/c731b5c5-e722-c0f9-ff9f-98028602181e.jpeg">

#### Appendix2.3 Iris Detection
　画像にうつる人物の瞳の位置を検出することが出来ます。1)blazefaceで顔領域切り出し 2)facemeshで目領域切り出し 3)瞳位置検出　と3つのtfliteモデルを直列に動かすことで瞳検出を実現しています。[mediapipe](https://github.com/google/mediapipe)で公開されているモデルです。
<img width="600" alt="iris_detection" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265135/009510cd-d5a8-073f-b03d-68ab612b526c.png">

#### Appendix2.4 Blazepose
　上体の回転角度を補正してから姿勢推定することで、ヨガのように複雑なポーズをとった人物に対しても安定して姿勢推定することができます。1)上体検出, 2)姿勢検出 と2つのtfliteモデルを動かす必要があるので posenet よりは計算負荷が高いです。これも[mediapipe](https://github.com/google/mediapipe)で公開されているモデルです。
<img width="600" alt="blazepose" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265135/29fe00a3-0ea8-abd7-4665-e1098fad0f20.png">

#### Appendix2.5 3D Human Pose Estimation
　画像にうつる人物の姿勢を3次元的に推定することができます。3次元情報が取得できるのでお気に入りのキャラクタを動かすこともできるようになります（このアプリは棒人間ですが）。[こちら](https://github.com/openvinotoolkit/open_model_zoo/tree/master/demos/python_demos/human_pose_estimation_3d_demo)のモデルを利用させて頂いています。
　![gl2pose_estimation_3d_mov.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265135/3edc6860-9558-21c7-3e9d-4031251ec43c.gif)

#### Appendix2.6 Depth Estimation (DenseDepth)
　室内画像を入力すると画素毎の深度情報を推定することができます。ポイントクラウドのようにぐりぐりと見渡すことができます。[こちら](https://github.com/ialhashim/DenseDepth)で公開されているモデルです。
<img width="600" alt="styetransfer_hikaku.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265135/743c3762-d3a9-e6d9-98a8-0f320d3a43de.jpeg">


#### Appendix2.7 Face Segmentation
　顔領域を検出したのち、さらに「目」「鼻」「口」「眉」「頭髪」といった顔パーツを画素単位で分類することができます。[こちら](https://github.com/MaybeShewill-CV/bisenetv2-tensorflow)で公開されているモデルです。
<img width="600" alt="face_segmentation" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265135/880866e4-0cf6-5aa6-e5e1-51fbdf251313.jpeg">


#### Appendix2.8 Selfie to Anime
　顔画像をアニメ調に変換することができます。見た目のインパクトは大きいですがラズパイには処理負荷が高いです。[こちら](https://github.com/margaretmz/Selfie2Anime-with-TFLite)のモデルを利用させて頂いています。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">顔画像をアニメ調変換する「selfie2anime」の TFLite 実装があったので、JetsonNano で動かしてみた。<br>さすがに重い。。。1コマ10秒。（添付動画は早回しです）<br>何か高速化テクニックないかな。<a href="https://t.co/B7tS7cHF6j">https://t.co/B7tS7cHF6j</a> <a href="https://t.co/ypREkVPkHj">pic.twitter.com/ypREkVPkHj</a></p>&mdash; terryky (@terryky1220) <a href="https://twitter.com/terryky1220/status/1280099141687709696?ref_src=twsrc%5Etfw">July 6, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


#### Appendix2.9 U^2-Net portrait drawing
　顔画像をマンガのような線画に変換することができます。このモデルもラズパイだと１コマ１０秒程度と負荷が高いです。[こちら](https://github.com/NathanUA/U-2-Net)で公開されているモデルです。
<img width="600" alt="u2net" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265135/8ba60626-6196-fa9f-15ad-0c2c7f43e554.jpeg">


#### Appendix2.10 Text Detection
　[TensorFlow Hub](https://tfhub.dev/sayakpaul/lite-model/east-text-detector/int8/1)で公開されているモデルです。画像内の文字領域を検出することができます。OCR(文字起こし)の前処理として使えそうです。

<img width="400" alt="text_detection" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265135/de3de172-cc73-eb29-5a8e-1c705a23aa48.jpeg">


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


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

[^interface]: TensorFlow Lite 2.3 環境での計測ですが、XNNPACKへの言及はないようです。
[^interface2]: 私はinterface誌の関係者ではありません。念のため。

<img width="400" alt="interface" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265135/b66c92bf-c3d0-7d32-107c-e993e8899eec.png">

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