Coral Edge TPU Dev Board で TensorFlow Lite GPU Delegate V1(OpenGLES) を試す

# 1. 概要
　Coral EdgeTPU Devboard 上で、TensorFlow Lite **GPU Delegate** に挑戦します。（EdgeTPU Delegate じゃありません）

　題材として Posenet を動かし、下記のような結果表示を OpenGLES で行います。
　つまり、認識にも表示にも GPU を使います。
　
　「EdgeTPU Devboard なのに EdgeTPU を使わないなんて理解不能」という声も聞こえてきそうですが、これがうまくいけば、既存の組込機器 (AIアクセラレータ非搭載、CPU+GPU 構成) へのAI展開可能性を評価できるなぁ、というモチベーションもあって作業しました。

![gl2posenet_m.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265135/c1ce4f8c-420f-9470-8246-d44190c3eb8a.png)

### 1.1 先に挑戦結果
　この記事を書いている段階では、
　　１）無理くり GPU Delegate を有効にして Posenet を動かすことは出来た
　　２）~~TensorFlow Lite からの戻り値が正しくなく、変な場所に関節が描画される~~
　~~という状態です。原因解析が困難で頓挫していますが、今回行った作業手順がどなたかのお役にたてるかも、という思いと、解析を進めるために皆様のお知恵をお借りしたい、という両方の思いから、いったん記事としてまとめます。~~

　~~※EdgeTPU Devboard に搭載されている GPU (Vivante GC7000 lite) は、OpenGLES 3.1 対応なので、TensorFlow Lite の GPU Delegate が求める OpenGLES 要件を満たしています。なので動いてほしいのですが。。~~


#### (2019/11/30 追記)
　TensorFlow r2.0 ブランチを最新版に更新すると、元記事記載内容の手順を踏むことで、正しく姿勢推定ができるようになっていました。
　念のため、動作が確認できたコミットIDは下記です。

```
$ git show 1cf0898dd4331baf93fe77205550f2c2e6c90ee5
commit 1cf0898dd4331baf93fe77205550f2c2e6c90ee5 (HEAD -> r2.0, origin/r2.0)
Merge: 64c3d382ca c4604a3265
Author: Goldie Gadde <ggadde@google.com>
Date:   Tue Oct 8 09:40:53 2019 -0700

    Merge pull request #32474 from ROCmSoftwarePlatform/r2.0-rocm-upstream-squashed

    [ROCM] Patch to enable rocm for r2.0 release branch
```

　GPU Delegate V1 による処理速度改善効果は下記となりました。

|計測実機      |GPU Delegate(v1)なし|GPU Delegate(v1)あり|
|:------------:|:-----------------:|:------------------:|
|Coral DevBoard|    280[ms]        | 800 [ms]           |
| Jetson Nano  |    180[ms]        |  80 [ms]           |


　Coral DevBoard は GPUDelegate を使うことで逆に遅くなってしまいましたが、**JetsonNanoでは２倍以上の高速化効果** が得られました。

　上記計測結果は GPU Delegate v1 (OpenGLES)のものですが、Coral DevBoard のように **OpenCL が使える環境であれば、GPU Delegate v2 (OpenCL) を使うのが良い** と思われます。[(別記事参照)](https://qiita.com/drafts/9f096149efbc6e3ac3d2/edit)）



　以降、GPUDelegate対応版 TensorFlow Lite ライブラリを作るための手順を説明していきますが、本記事最後にお手軽ビルドスクリプトを用意していますので、お急ぎの方はそちらもご活用ください。



# 2. Ubuntu PC (x86_64 + GeForce GTX1080) での実験
　EdgeTPU Devboard で作業する前に、GPU Delegate の動きを頭に入れるため、Linux PC で動かしました。（PCではちゃんと動きました）
　
前提として、TensorFlow Lite C++ (Delegate なし) のアプリがすでに動いている状態から作業を始めます。
　
### 2.1 Delegate ライブラリのビルド
　GPU Delegate を使用するには、Tensorflow Lite 本体ライブラリのビルドに加えて、Delegateライブラリを追加でビルドする必要があります。[公式ドキュメント](https://www.tensorflow.org/lite/performance/gpu_advanced) には、Android/iOS 向けの手順しか記載されていませんが、そこからの類推で、下記手順で Linux PC 向けに Delegateライブラリをビルドすることができました。

```shell

# TensorFlow ソースコードを clone 
git clone https://github.com/tensorflow/tensorflow.git 
cd tensorflow
git checkout r2.0

# TensorFlow Lite ライブラリのビルドに必要な関連ライブラリのダウンロード
./tensorflow/lite/tools/make/download_dependencies.sh

# TensorFlow Lite ライブラリ本体のビルド
> make -j 4  -f ./tensorflow/lite/tools/make/Makefile BUILD_WITH_NNAPI=false EXTRA_CXXFLAGS="-march=native"

# GPU Delegate ライブラリのビルド
> bazel build -s -c opt --copt="-DMESA_EGL_NO_X11_HEADERS" tensorflow/lite/delegates/gpu:libtensorflowlite_gpu_gl.so

```
　公式ドキュメントには記載がありませんが、Delegate ライブラリビルド時に `MESA_EGL_NO_X11_HEADERS` オプション付与が必要でした。これがないと、X11ヘッダが global な namespace で定義する型が、TensorFlowの local namespace での型定義と猛烈に衝突してしまいます。

 上記手順により、X86_64 Linux 用の libtensorflow-lite.a および libtensorflowlite_gpu_gl.so が出来上がります。これをアプリとリンクします。

```shell
$ ls -l libtensorflow-lite.a
-rw-rw-r-- 1 terryky terryky 4615918  9月  7 22:54 tensorflow/lite/tools/make/gen/linux_x86_64/lib/libtensorflow-lite.a

$ ls -l libtensorflowlite_gpu_gl.so
-r-xr-xr-x 1 terryky terryky 2185432  9月  7 22:55 bazel-bin/tensorflow/lite/delegates/gpu/libtensorflowlite_gpu_gl.so*

```









### 2.2 アプリ側のGPU Delegate対応
　GPU Delegate を使うためには、アプリ側にも若干修正が必要です。
　Delegate を使わないアプリがすでに動いているなら、Interpreter 構築時に、下記おまじないを追加すればOKです。

```C
/* 追加するヘッダファイル */
#include "tensorflow/lite/delegates/gpu/gl_delegate.h"

/* 通常通りInterpreter構築 */
unique_ptr<FlatBufferModel> model;
unique_ptr<Interpreter> interpreter;
ops::builtin::BuiltinOpResolver resolver;

model = FlatBufferModel::BuildFromFile(POSENET_MODEL_PATH);
InterpreterBuilder(*model, resolver)(&interpreter);

/* ----- GpuDelegate 用に追加するブロックここから ----- */
const TfLiteGpuDelegateOptions options = {
    .metadata = NULL,
    .compile_options = {
        .precision_loss_allowed = 1,  // FP16
        .preferred_gl_object_type = TFLITE_GL_OBJECT_TYPE_FASTEST,
        .dynamic_batch_enabled = 0,   // Not fully functional yet
    },
}  
auto* delegate = TfLiteGpuDelegateCreate(&options);
interpreter->ModifyGraphWithDelegate(delegate);
/* ----- GpuDelegate 用に追加するブロックここまで ----- */

interpreter->AllocateTensors();
```

### 2.3 描画用OpenGLESとのコンテキスト衝突回避
　描画にもOpenGLESを使っていると、描画用とTFLite用とでOpenGLESコンテキスト設定が衝突するのでは、との思いから、GPU Delegate の実装を確認しておきます。

#### 2.3.1 OpenGLES, EGL初期化



　TensorFlow Lite 内の GPU Delegate 構築処理を見ると、まず最初に、その時点までに OpenGLES 初期化が完了しているか (既に `EGLContext` 生成済みかどうか) を確認しています。

　・OpenGLES 初期化済みなら、その`EGLContext`をGPU Delegate用途にも再利用して以降の処理を実行します。
　・OpenGLES 未初期化なら、GPU Delegate が自ら OpenGLES, EGL の初期化を行います。


```C:tensorflow/lite/delegates/gpu/gl/egl_environment.cc
Status EglEnvironment::Init() {
  /* 略 */

  // すでに OpenGLES 初期化済みなら、EGLDisplay, EGLContext を再利用する
  if (eglGetCurrentContext() != EGL_NO_CONTEXT) {
    display_ = eglGetCurrentDisplay();
    context_ =
        EglContext(eglGetCurrentContext(), display_, EGL_NO_CONFIG_KHR, false);
  } else {
    // 未初期化なら GPU Delegate が自ら EGL 初期化を行う
    RETURN_IF_ERROR(InitDisplay(&display_));
    Status status = InitConfiglessContext();
    if (!status.ok()) {
      status = InitSurfacelessContext();
    }
    if (!status.ok()) {
      status = InitPBufferContext();
    }
    if (!status.ok()) {
      return status;
    }
```

　上記に特に問題は無いように思えますが、後者の初期化には注意が必要です。というのは、GPU Delegate による EGL の初期化の実装が、教科書的な `eglGetDisplay(EGL_DEFAULT_DISPLAY)` での実装となっているからです。残念ながら、この実装では、初期化に失敗して動かない環境が出てきてしまいます。

　例えば、EdgeTPU Devboard は初期化に失敗する典型例です。 EdgeTPU Devboard は手順書通りにROM焼きするとWindowSystemが Wayland となり、`eglGetDisplay(EGL_DEFAULT_DISPLAY)` による EGL の初期化はエラーとなります。じゃぁどうするかというと、あらかじめ wayland プロトコルに従って wayland_display 初期化云々をした後、`eglGetDisplay(wayland_dipslay)` としてEGLの世界に渡す必要があります。
　OpenGLESを描画目的で使わない人でも Wayland を叩く必要があるのはなんだかなぁ、と思いますが、OpenGLESは描画用途で進化してきたので仕方ないとも思います。

　このように、OpenGLES, EGLの初期化は WindowSystem を意識する必要があるのですが、GPU Delegate 内に Window System に依存した実装を入れ込むのはナンセンスなので、GPU Delegate に OpenGLES, EGL の初期化を任せるのは無理があると思います。あらかじめアプリで OpenGLES, EGL の初期化を済ませておき、GPU Delegate にはそれを再利用させるだけのほうが良いと思います。


#### 2.3.2 EGLContext の衝突
　前節で、EGLContext は再利用したほうが良いという話をしましたが、コンテキストを共有するので、衝突には注意が必要です。
　例えば、TensorFlow Liteは GPU Delegate 初期化時に下記関数を呼び出しますが、この関数はOpenGLESコンテキストを変更したまま復帰せずそのままアプリへ返ります。具体的には `glBindFramebuffer()`で描画先フレームバッファを変更したままアプリへ返るので、**アプリは自分で描画先フレームバッファを元に戻さないといけません**。デフォルトフレームバッファを期待しているアプリだといくら描画しても画面に何も表示されない状態となります。はい。私もこれに気づくまでにえらい苦労しました。

```C:tensorflow/lite/delegates/gpu/gl/egl_environment.cc
void EglEnvironment::ForceSyncTurning() {
  glGenFramebuffers(1, &dummy_framebuffer_);
  glBindFramebuffer(GL_FRAMEBUFFER, dummy_framebuffer_); /* ★コンテキスト変更 */

  glGenTextures(1, &dummy_texture_);
  glBindTexture(GL_TEXTURE_2D, dummy_texture_);
  glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 4, 4);
  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                         dummy_texture_, 0);

  GLenum draw_buffers[1] = {GL_COLOR_ATTACHMENT0};
  glDrawBuffers(1, draw_buffers);

  glViewport(0, 0, 4, 4); /* ★コンテキスト変更 */
  glClear(GL_COLOR_BUFFER_BIT);
}
```
#### 2.3.3 GPUアーキに依存した実装
　GPU Delegate の初期化処理を見ていると、動かす対象のGPUアーキ種別が何なのか事細かく調べている個所が見つかります。特に Adreno に対しては執拗にマイナ番号まで調査しており、Googleの力の入れようを感じます。

　そして GPU Delegate 本体処理で、GPUアーキ毎に実装を変えている個所がいくつか見つかります。理想的にはOpenGLESで規格化されているので GPUアーキを意識する必要はないはずなんですが、現実にはそうはいかないんでしょうね(GPUごとにOpenGLESの挙動に差異がある)。

　なお、この時点で、**if文の羅列にVIVANTE が考慮されてない** ことに気づき、若干不安を覚えました。。。テストされてないんだろうなぁ。ちゃんと動くかな。。。。
　が、気にしないことにして作業を続けます。。。

```C:tensorflow/lite/delegates/gpu/common/gpu_info.cc
GpuType GetGpuType(const std::string& renderer) {
  if (renderer.find("mali") != renderer.npos) {
    return GpuType::MALI;
  }
  if (renderer.find("adreno") != renderer.npos) {
    return GpuType::ADRENO;
  }
  if (renderer.find("powervr") != renderer.npos) {
    return GpuType::POWERVR;
  }
  if (renderer.find("intel") != renderer.npos) {
    return GpuType::INTEL;
  }
  if (renderer.find("nvidia") != renderer.npos) {
    return GpuType::NVIDIA;
  }
  return GpuType::UNKNOWN;
}

GpuModel GetGpuModel(const std::string& renderer) {
  auto found_model = [&](std::string model) -> bool {
    return renderer.find(model) != renderer.npos;
  };
  // Adreno 6xx series
  if (found_model("640")) return GpuModel::ADRENO640;
  if (found_model("630")) return GpuModel::ADRENO630;
  if (found_model("616")) return GpuModel::ADRENO616;
  if (found_model("615")) return GpuModel::ADRENO615;
  if (found_model("612")) return GpuModel::ADRENO612;
　さらにつづく
```

### 2.4 アプリのビルド
　アプリビルド時に、TenslorFlow Lite ライブラリ本体のリンクに加えて、Delegateライブラリもリンクするように Makefile を追記します。

```Makefile
# TensorFlow Lite ライブラリ本体のリンク
LDFLAGS  += -L$(TENSORFLOW_DIR)/tensorflow/lite/tools/make/gen/linux_x86_64/lib/
LIBS     += -ltensorflow-lite

# GPU Delegate ライブラリのリンク
LDFLAGS  += -L$(TENSORFLOW_DIR)/bazel-bin/tensorflow/lite/delegates/gpu
LIBS     += -ltensorflowlite_gpu_gl
```

### 2.5 LinuxPCでの動作結果
　見事動きました！
　そして、速くなりました！

　描画込みの１フレーム当たりの時間は下記でした。

|Delegateなし|GPU Delegateあり|
|:----------:|:--------------:|
|57[ms]      |16[ms]<br>※SwapBuffer上限|

　TensorFlowLite は ARM 向け最適化が進んでいるので、X86での速度は参考値程度かと思いますが、少なくとも GPU Delegate により、SwapBuffer時のVSYNC同期で頭打ちになるまで高速化できたので、その効果は確認できたと思います。

# 3. EdgeTPU Devboard への移植
　Linux PC 上で動作確認できたので、いざ、EdgeTPU Devboard へ移植します。

　移植作業は下記方針としました。
　・**[TensorFlow Lite ライブラリのビルド]** 時間がかかるので、**ホストPCで aarch64 向けにクロスビルド**
　・**[アプリのビルド]** 大して時間がかからないのと、リンクするライブラリが EdgeTPU Devboard 上にしかないものがあるので、**Devboard 上でネイティブビルド**

### 3.1 TensorFlow Lite ライブラリのビルド (ホストPCでの作業)
 　LinuxPC で作業した時と同様、TensoFlow Lite 本体ライブラリと、GPU Delegate ライブラリの両方のビルドが必要です。

#### 3.1.1 TensorFlow Lite 本体ライブラリのビルド
　TensorFlow Lite 本体は、make 時に **TARGET指定するだけ** で難なく aarch64 向けにクロスビルドできます。

```shell
cd tensorflow
make -j 4  -f ./tensorflow/lite/tools/make/Makefile BUILD_WITH_NNAPI=false TARGET=aarch64
```

#### 3.1.2 GPU Delegate ライブラリのビルド
　LinuxPC 向けには bazel でビルドできたので、同じように bazel で aarch64 向けクロスビルドするだけでしょ、と軽い気持ちで作業していていました。が、Webに記載のあった何通りかの方法で試してみたのですが、私のbazelスキルが低く、どれもうまくいきませんでした（ライブラリのビルドがこけるとか、アプリとのリンク時に関数が足りないとか）。

　で、どうしたかというと、bazel をあきらめ、**bazel がやってることと同じことを Tensorflow Lite 本体の Makefile に書く**、という力業を試しました。
　具体的には、`bazel build -s hoge` のように `-s` オプションをつけることで、実際に行われているコマンドを見える化し、それを再現させました。

bazel がやってることは、3つありました。
　　1) `flatc -c` で何かのヘッダファイルを作る
　　2) 120個くらいのccファイルをコンパイルする
　　3) libtensorflowlite_gpu_gl.so の形にまとめる
以下、一つずつ具体作業を書きます。
　
##### 3.1.2.1 flatc 
　1) の FlatBuffer 処理については、LinuxPC向け作業の過程で生成済みのものがあったので、下記コマンドで、それらしい場所へコピーしました。

```
cd tensorflow
cp bazel-out/k8-opt/genfiles/tensorflow/lite/delegates/gpu/gl/* tensorflow/lite/delegates/gpu/gl/
cp bazel-out/k8-opt/genfiles/tensorflow/lite/schema/* tensorflow/lite/schema/
```

##### 3.1.2.2 CCソースのコンパイル
 2) は、bazel がコンパイルしているファイルをビルドログから洗い出して、TensorFlow Lite 本体をビルドするための Makefile にチマチマ追記するという作業を行いました。
　はっきりいって、とんでもなく泥臭い手作業でした。AIがちやほやされている現代にやる作業とは思えない。。。他にスマートな方法が絶対あるはず。。。

```Makefile:tensorflow/lite/tools/make/Makefile

# -------------------------------------------------------------------------------------------
# for GPU Delegate
# -------------------------------------------------------------------------------------------
CXXFLAGS += -I$(MAKEFILE_DIR)/downloads/fp16/include
CXXFLAGS += -DMESA_EGL_NO_X11_HEADERS

CORE_CC_ALL_SRCS += tensorflow/lite/tools/make/downloads/absl/absl/time/internal/cctz/src/time_zone_impl.cc
CORE_CC_ALL_SRCS += tensorflow/lite/tools/make/downloads/absl/absl/types/bad_optional_access.cc
CORE_CC_ALL_SRCS += tensorflow/lite/tools/make/downloads/absl/absl/types/bad_any_cast.cc
CORE_CC_ALL_SRCS += tensorflow/lite/tools/make/downloads/absl/absl/hash/internal/hash.cc

＃以下同様に120個くらい。。。
```

##### 3.1.2.3 *.so の生成
　3) は何もしません。GPU Delegate だけの単体ライブラリを作らないからです。
　2) でMakefileを修正したのち、TensorFlow Lite 本体をビルドする手順を再度行えば、TensorFlow Lite 本体と GPU Delegate とが一体化した大きなライブラリが１つ出来上がります。

```shell
$ ls -l libtensorflow-lite.a
-rw-rw-r-- 1 terryky  terryky 11619104  9月  7 23:50 tensorflow/lite/tools/make/gen/aarch64_armv8-a/lib/libtensorflow-lite.a

```

以上のようにして出来上がった libtensorflow-lite.a を EdgeTPU Devboard にコピーして、これ以降は EdgeTPU Devboard 上でアプリとリンクします。

### 3.2 アプリのビルド（EdgeTPU Devboard 上での作業)
　次に作業場所を EdgeTPU Devboard 実機上に変え、ホストPCで作ったライブラリを使ってアプリをビルドしていきます。

#### 3.2.1　アプリビルド作業
　アプリビルドに必要な作業は、libtensorflow-lite.a を GPU Delegate 一体化版に差し替えて、下記コマンドを叩くだけです。

```
make TARGET_ENV=edgetpu_devboard
```


　アプリのソースコードは LinuxPC で動作確認済のものと同じです。

　WindowSystem に関して、LinuxPC が X11 だったのに対し EdgeTPU Devboard は Wayland なので、それにあわせて OpenGLESの初期化処理を変える必要があります。ですが、この対応は、上記make時に　`TARGET_ENV` を指定することで、wayland用のOpenGLES初期化ルーチンが自動で選択されるようにしています。

　また、アプリのフローとして、OpenGLESをあらかじめ初期化してから GPU Delegate の構築を行うようにしているので、GPU Delegate 内での OpenGLES 初期化処理において Wayland を意識する必要はありません。


### 3.3 動作確認
　さて、いよいよアプリの動作確認を行ってみると。。。

　GPU Delegate の構築は無事に通過したものの、その後の推論実行時に `interpreter->Invoke()` 関数から戻ってこないまま、ハング状態となりました。
　悲しみをこらえつつ、解析してみます。

#### 3.3.1 どこでハングしていたか
　GPU Delegate 実装の中を掘り進むと、ハングしていたのは下記関数でした。
　`glGetSynciv()` の結果がいつまでたっても `GL_SIGNALED` にならず、while() 文から抜けれていない状態となっていました。

```C:tensorflow/lite/delegates/gpu/gl/gl_sync.cc
Status GlActiveSyncWait() {
  GlSync sync;
  RETURN_IF_ERROR(GlSync::NewSync(&sync));
  // Since creating a Sync object is itself a GL command it *must* be flushed.
  // Otherwise glGetSynciv may never succeed. Perform a flush with
  // glClientWaitSync call.
  GLenum status = glClientWaitSync(sync.sync(), GL_SYNC_FLUSH_COMMANDS_BIT,
                                   /* timeout ns = */ 0);
  switch (status) {
    case GL_TIMEOUT_EXPIRED:
      break;
    case GL_CONDITION_SATISFIED:
    case GL_ALREADY_SIGNALED:
      return OkStatus();
    case GL_WAIT_FAILED:
      return GetOpenGlErrors();
  }

  // Start active loop.
  GLint result = GL_UNSIGNALED;
  while (true) {   /* ★このwhile()ループから抜けない */
    glGetSynciv(sync.sync(), GL_SYNC_STATUS, sizeof(GLint), nullptr, &result);
    if (result == GL_SIGNALED) {
      return OkStatus();
    }
#ifdef __ARM_ACLE
    // Try to save CPU power by yielding CPU to another thread.
    __yield();
#endif
  }
}
```

 要は、`glDispatchCompute()` 関数でGPUに投げた計算処理の完了待ちをしてるんだけど、いつまでたっても終わらない状態になっているようです。

　ひょっとして GPU ハードがハングしているのかも、とも思いましたが、他のプロセスの描画は元気に動いているので、ソフト的にOpenGLESがうまくGPU処理完了を検知できてないように見えます。

　こういう良くわからない不具合が起こったとき、まず人のせいにするのが得意な私は、「`glGetSynciv()` の実装がバグってんじゃないの？」と疑って、GPU実行完了待ちをシンプルに下記に変えてみました。

```C:tensorflow/lite/delegates/gpu/gl/gl_sync.cc(変更後)
Status GlActiveSyncWait() {
  glFinish(); /* がっつり全完了待ち */
  return OkStatus();
}
```

すると、glFinish()は (1000[ms]くらい待たされるものの) 無事に返ってきました。
そして、アプリのループ（認識→結果描画）はハングすることなく回るようになりました。


#### 3.3.2 結果
　~~アプリはハングすることなく動くようになりましたが、残念ながら正しい認識結果はえられていません。各関節の認識scoreも低く信頼できる値ではありません。~~

　~~`glFinish()` に変えるとGPU処理完了待ちから戻ってくるようになったのですが、単にタイムアウトで返って来るようになっただけかもしれません。よくわかりません。~~

　~~かといって、特定の入力画像に対して特定の結果が返ってくるので、ランダム値を入出力しているのではなく、何らかの動きはしているように見えますが、よくわかりません。~~

##### (2019/11/30 追記)
　冒頭追記の通り、TensorFlow r2.0 を更新することで、正しい認識結果が得られるようになっています。


#4. 最後に
　~~ここまで結構苦労したのですが、まだ動かせてなくてもどかしいです。~~

　~~「EdgeTPU Devboard で GPU Delegate 成功したよ」とか、「お前ここ間違ってるよ」とか、なんでもWelcomeですので、諸先輩方のアドバイスを頂けると幸いです。~~

　※Vivante じゃなくて Mali とかで試したほうがよかったかなぁ。
　※ラズパイ4 の GPU (Broadcom VideoCore VI) は OpenGLES 3.0 止まりなので、GPU Delegate 試せないですね。


## 4.1 2019/11/30 追記
　元記事記載時点では正しく動かすことはできていませんでしたが、冒頭追記の通り、本日時点で、TensorFlow r2.0 を更新することで、正しい認識結果が得られるようになりました。

　GPU Delegate を有効にすることで、PCでは３倍以上速くなりましたが、EdgeTPU DevBoard では残念ながら３倍近く遅くなってしまいました。この理由としては、
　　１）もともと TensorFLow Lite は ARM CPU 向け最適化が進んでいる。
　　　　(Delegate使わなくてもそこそこ速い)
　　２）Edge TPU DevBoard に搭載されている GPU 性能がそれほど高くない
　　３）GPU Delegate を使うことにともなう、CPU－GPU 間のデータ移動オーバーヘッドが高い
ということが考えられますが、それにしても遅くなりすぎだと思います。

　３）については、[公式ドキュメント](https://www.tensorflow.org/lite/performance/gpu_advanced) に、SSBOを使ったチューニング手法が言及されているので、いずれ試そうと考えています。

　なお、本記事記載内容は、TensorFlow r2.0 で実装されている GPU Delegate V1 であり、バックエンドは OpenGLES の Compute Shader です。
　一方で、TensorFlow r2.1 では GPU Delegate V2 となり、バックエンドは OpenCL に変更となっています。こちらはすでに [(別記事参照)](https://qiita.com/drafts/9f096149efbc6e3ac3d2/edit) にまとめている通り、OpenGLES 版ほど遅くなっていません。
　TensorFlow Lite の GPU Delegate の進化の方向性として、 OpenGLES ではなく、OpenCL に注力していくのでしょうか。（端末普及率は OpenGLES のほうが圧倒的に高いと思うんですけど。。。）
　いずれにせよ、継続ウォッチしていこうと思います。




#5. ソースコード 
GitHubで公開しています。
https://github.com/terryky/tflite_gles_app/tree/master/gl2posenet


#6. GPU Delegate 有効版 TensorflowLite ライブラリのビルドスクリプト
　手っ取り速く GPUDelegate を有効にした TensorFlow Lite ライブラリを作るためのビルドスクリプトを用意しました。
　Ubuntu18.04ホストPC上で下記を実行することで、```aarch64 Linux``` 用の```libtensorflow-lite.a``` をビルドすることができます。
　Coral DevBoard と JetsonNano で動作確認できています。ご参考になさってください。

```sh
# (前準備) 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

# GPUDelegate 有効版 TensorFlow Lite ライブラリをビルド
$ cd ~/work 
$ git clone https://github.com/terryky/tflite_gles_app.git
$ ./tflite_gles_app/tools/scripts/tf2.0/build_libtflite_r2.0_with_gpu_delegate_aarch64.sh
```