TensorFlow Lite on Unity やってることメモ
これ経由で仕事をもらったりしていているので、かれこれ2019年12月から長めにメンテナンスを続けているライブラリ、何やってるか忘れるので、メモ。
とりあえず、このイシューはパッチリリースをすればいいのでやる。
TensorFlowh本体のバージョンと合わせてリリースしてたのでど、こういったパッチリリースがあるので、バージョン合わせなきゃよかったな。
このIssue はM1 macで試したけど、再現しない.
Git LFSの罠だった。Git LFSはもっと目立つようにREADMEに書いとかないとだめだな…。
人類にGit LFSは早すぎる…。
WindowsはVisual C++ redistributable x86 and x64をインストールしないと動かないという、TensorFlow Lite側のバグがあるっぽい。
v2.10.0
ブランチで TensorFlow Lite Library for Unityをビルド中。
出くわした問題
- macOS Metal Delegateでは, cpuinfoの判定が正常に動作しない問題が未だにあり、パッチをあてて対処していたものの、cpuinfoライブラリをソース梱包から、Bazelが依存解決して外部からとってくるようになってしまった。この問題に本格的に取り組まなくては行けない気がする。
- Signature RunnerのC APIに名称変更があった。
- Androidでは
UnsatisfiedLinkError: dlopen failed: library “libc++_shared.so” not found
エラーが出るな…。むむむ
GitHub上でAndroid GPU Delegateに違うエントリーポイントがあることを教えてもらった。こっちはOpenGLES のSSBO BindingのAPI付き。自分でメンテナンスするよりも楽かもしれない。BazelのBUILDファイルに存在しないので、TensorFlowチームが内部的に使ってるだけな気もするが、試してみても良いかもしれない。
とりあえずv2.10.0のブランチはマージ。
最低限の機能だけ用意するのがいいや…と思ってつけてなかった、目次シーン戻るボタン。どう考えても必要だろと思い直して、つけることにした。すいません。
Androidで最新バージョンが以前より遅くなっているっというイシュー結構お時間かけて調べたけど、私の端末ではむしろ早くなっているな…。端末に振り回されるAndroidはつらい。
origin/gpu-api-delegate
ブランチで新しいGPU delegate for Android試しているが、SSBOバインディングの動かしかたがわからん。全然ドキュメントない上にUnity経由でやるから、やってる人が私しかいない…。ひとまず棚上げするか…。
すごい小ネタだが、Unity Androidの Build & Runは失敗することもあり、Buildだけのほうが成功率高いので、ビルドしてから以下のスクリプトでadb 経由で立ち上げている。
adb install -r tf-lite-unity-sample.apk
adb shell am start -n com.asus4.tfliteunitysample/com.unity3d.player.UnityPlayerActivity
adb logcat Unity:V tflite:V "*:S"
C# でネイティブの可変長引数 va_arg がパースできないので、TensorFlow Liteからのログをちゃんとパースできてない問題。
Unity 2021 LTSにアップデートするとなんかTFLiteの中のフレームワークが見つからないってエラー。Unity LTSバージョンってより、Burstとかのアップデートに影響されてるのかなって思っている。優先度高いが、ちょっと時間をとって見てみたいとわからん問題。
これもUnity 2021 LTSにバージョンすると起きる問題。特定端末でおきるってことなので、2021 LTSで2020 LTSから何が変わったのかちゃんと調べたいが、身近にこの辺の相談できる人がほしい〜〜。
フリーランスとしての依頼が来たので、Android Neural Networks APIの機能追加を優先対応した。
対応した内容詳細はこちら↓
以前も何社かコミッションワークとして機械学習 x Unityなプロジェクトへ参加したことがあるが、プロジェクトを離れると私がメンテナンスできなくなり、だんだんと使われなくなってしまうので、こういったOSSへの追加を依頼してもらえるのはありがたい限り。
LTS 2021 x iOSの組み合わせでビルド通らなくなる件、直してみた。
ユーザー数が増えてきたので、本来ならば時間をかけて、全プラットフォームのUnityバージョンの組み合わせをテストできるようなCIを用意すると良い気がするが、メンテコストが上がるので、積極的に、古い端末のサポートを切って、最新LTSのみで試してます。みたいな方が良いかなあ。
本来ならば、
Linux, Windows, macOS, Android, iOS
Unity 2020 LTS, 2021 LTS, 2022 Tech Stream
くらいの全組み合わせを順にCI.
みたいなのが理想。
新しいAndroid appをGoogle Play StoreにsubmitするにはAPI level 31にするのが必要なので、これも対応したほうが良さそう。メンテ頑張る。
むっちゃ突かれてるけど、お金くれるコミッションワークの方を優先して、こっちは後回しになっておる、フリーランスの辛いところ。オープンソースプロジェクトが上手く行かない要因じゃ。時間を見つけて対応したい。
SSDはTensorFlow examplesのやつが、デフォルトでint8 なquantizedなやつなんだけど、floatのモデルを読み込むときに動かないってissueが4,5 回来てるのでデフォルトをfloat16とかに変えても良いかもな。Teachable Machineから書き出すのもfloat出し。こっちのほうがシンプル。
Android API level 31で動かん問題しらべた。手元の環境で問題を再現。色々手を入れて、修正を確認、
やはり原因は、Android API level 31から use-native-libraryタグを入れないとOpenCLにアクセスできなくなった、というセキュリティレベルの変更が影響しているぽい。
<uses-native-library android:name="libOpenCL.so" android:required="false" />
<uses-native-library android:name="libOpenCL-car.so" android:required="false" />
<uses-native-library android:name="libOpenCL-pixel.so" android:required="false" />
直し方詳細は以下に書いてみた。
IPostGenerateGradleAndroidProjectで、色々自動化できそうだけど、この辺 やったことないのでちょいと検証が必要。
あー、上記問題、Unity 2021 LTSのGradleのバージョンが 6.1.1
で 多分 <use-native-library> tagがGradleバージョン挙げないと、使えない?てことは、ビルド自動化するのに、 Gradleバージョン依存を解決しなくてはいけなくて、手動でやるのは簡単だけど、ライブラリとして提供するのは結構茨の道である。むむむ。
Array Instance を毎回生成することで起きる問題。
これは確かに間違いやすいので、どっかでコメントを書いたほうが良さそう。
最新版のAndroidは以前に比べてちょいちょい遅いって話を聞く。なんかあるのか?Unity側かTensorFlowLite側か切り分けが難しいな。
Windowsでは遅いってやつ。GPUアクセラレーションがないので、しょうがない。
GitHubにくるIssueしらべて返信するだけで平気で2時間くらい溶けるので、core-jsの作者の気持まじでわかる〜となる。
完全に気まぐれだけど、SSDのカスタムモデルが動かないってissueをみた。
ちょっと手直ししたら動いたが、これは確かに、なれてないと厄介なやつ。SSD系のNonMaxSupressionは TFLite_Detection_PostProcess
でカスタムの実装がTFLiteには組み込まれているが、TensorFlow のversion 1系と2系でoutputの順番が違うという。バージョン依存問題だった。こんなんソースコード見ないと気づかない。
なるべく全部のissueを平等に見たいが、そんな時間はないので、気まぐれになってすいません。
あといつまでも Unity LTS2020でやってたけど、新規もう2023年。Unityの2022 LTSは未だに来る気配がないが、一応Unity 2021 LTSにアップグレードした。
新しいAndroidでOpenCLがリンクされなくて遅くなる問題は、Niantec Lightshipでもう少しいい対応してるのを見つけたので、試してみてもいいかも。
TensorFlow Lite v2.13.0
をビルド開始。案の定いっぱいエラーが出る。ただのコンパイル屋さん。
macOS ビルドエラー
bazelで以下のようなエラーが出る。
unknown option: --no-undefined
これをPRをv2.13.0へcherry-pickで動きそう。CIもcherry pick対応させるか悩む。
iOS ビルドエラー
'any_cast<tflite::gpu::Convolution2DAttributes &>' is unavailable: introduced in iOS 12.0
Xcodeバージョンを14.2に落とせとのこと。えー。
→ その後masterブランチとdiffとって、Minumum iOS verisonを11.0から12.0に上げれば動くこと確認。
TFL_MINIMUM_OS_VERSION = "12.0"
Android GPU delegate
<uses-native-library>
が見つからない関係のエラー。
AndroidはOpenCL.soを使うのに、Android Manifestにこの辺を追記することが必須になった。Build Tools を31に上げると解決するのだが今度は、
build tools 30まで存在したdx.jar が31では消えてるのでコケる。stackoverflowにあるように手動で30から該当dx.jarをコピーすると通るのだが…。納得行かない。
一応手動で通してから再ビルドすると
AHardwareBuffer_acquire
が定義されてない的なエラー NDKのバージョンを最低26にあげないと駄目らしい。
これソースコードのコメントじゃなくて、見えるとこに書いてほしいぞ…。
ってことで、v2.13.0のGPU delegateをビルドするのに必要な構成はこんなかな?
- ANDROID_SDK_API_LEVEL: 23 (not changed)
- ANDROID_BUILD_TOOLS_VERSION: 30.0.2 → 31.0.0
- ANDROID_NDK_API_LEVEL: 21 → 26
CircleCIも通るようにしたいが、なのかの情報が見当たらない気がするな。実際は入ってみて確認するしかないのかな?イメージ一覧。もうDockerに移行するほうがいいのか。
Windows
An error occurred during the fetch of repository 'llvm-raw':
java.net.SocketTimeoutException: Read timed out
でタイムアウトでとまる。
GitHubが調子悪いっていってるけど、そんなことある?
→ その後何回もこころみて、llvmダウンロード失敗してもビルドは内部的なllvmかなにかにフォールバックしているのか、ビルドは成功する。
TimeOutのExcepitonが出るとそこでBazelのビルドプロセスがとまるのでllvmダウンロード自体をキャンセルすることで、通った。…CIは…また今度。
Linux
これは一番優秀。
パッチを適用すれば一応ビルドできる。
CIではまだ落ちるが一応ライブラリビルドしてPRマージした。
<uses-native-library>
OpenCLを使うときに以下をAndroidManifast.xmlに追加する必要があった。しかしUnity 2021までのバージョンでは対応gradleバージョンが低いので、<uses-native-library>
タグがあるとコンパイルエラーになる。回避するために色々面倒くさい対応をする必要があった。Unity 2022でgradleバージョンがあがったぽいので、対応できるか見てみる。
<uses-native-library android:name="libOpenCL.so" android:required="false" />
<uses-native-library android:name="libOpenCL-car.so" android:required="false" />
<uses-native-library android:name="libOpenCL-pixel.so" android:required="false" />
ちなみに、8/31からtarget API Levelは33になった。
Unity Gradle Versionは2022から7.1.2
が載っているのでビルドできるはず。
対応した!
いいですか、*.task
ファイルはただのzipです。
OpenCL対応することで、ARFoundaitonでも動くようになったはず。(Android AR FoundationはOpenGLES3のみ。Vulkan非対応。最新ARCoreの方は対応されたので、いつかは対応してくれそう。)
ちゃんとIssueに向き合うと発覚。URPだけ対応して、Built-in Render Pipeline忘れてた。めんどいけど、対応してあげたほうが良さげ。UnityのRender Pipeline3種類あるの、ライブラリ作る側からするとコストが高すぎる。
対応した。カメラコマンドバファーに介入するのをURPとBuilt-in render pipeline両対応。みたいなユースケースではARFoundationが参考になる。
Built-inとURPの設計方針が間逆すぎて、どっちか片方だけにしたすぎる。Built-inは楽だった。
DeNAで使ってくれているコントリビュートの方が、このUnity対応のTFLiteバイナリの各プラットフォームビルドを自動化してくれた。楽になるー。オープンソースの醍醐味。
これのおかげで、楽にv2.14.0とv2.15.0のリリースを出せた。
こっちでOnnxのAPI触っていたら、MemoryやSpanとかなどのモダンなAPI使っていたので、TensorFlow LiteのAPIのほうが古く見えるな〜。TensorFlow Liteもまだ使いそうなので、APIを新しくするかな…。
v2.16.1で、画像認識のためのクラス BaseImagePredictor
を obsolateにして、モダンなAPIに移行したクラス、 BaseVisionTask
を用意してみた。いくつかのタイプの画像認識モデルを扱ってきた経験から、大体のパターンに対応できるクラスができたと思う。また、プロファイラのマーカーをBaseVisionTask
の中に挟むことで、自動でプロファイラに処理が残るようにしてみた。
v2.17.0
v2.17.0をtflite-runtime-builderでビルドしてみたものの、エラーが沢山でたので1個ずつ潰すかという気持ち。メンテは大変
v2.17.0 続き
Android
Android CPU側はビルドできたものの、GPU delegateがビルドできないので、Mavenからビルド済のものを使わせてもらおうと思ったら、なにやらTFLiteがパッケージの更新が止まっていた。TensorFlow LiteはLiteRTに移行するらしい。LiteRTという名前のパッケージの中を見たら、中身はそのままTensorFlowLiteだったので、名前だけ変えたらしい。完全な移行が済むまでTFLiteもメンテしてほしいな。
Linux
Linuxも色々エラー出てるんだけど、Issue Commentを見ると、gcc12 じゃないとだめぽい。Ubuntu 22.04のgccバージョンは11 から 24.04 にアップグレードすることで内部的なバージョンが13.2.0
に上がったのでビルド通った。
Windows
tensorflow/lite/core/c/operator.cc(35): error C7555: use of designated initializers requires at least '/std:c++20'
というエラー。.bazelrcでcxxoptが/std:c++17に固定されているので、ビルド時に設定を上書きしてあげれば良さそう。
bazel build -c opt --cxxopt=/std:c++20 --host_cxxopt=/std:c++20 --define tflite_with_xnnpack=true tensorflow/lite/c:tensorflowlite_c
こういう広く使われるライブラリではおしゃれぶってc++20の機能使わず、古い文法で書いてほしいな。