この記事は Microsoft Student Partners Japan アドベントカレンダーの24日目の記事です.
みなさま,クリスマスイブをいかようにお過ごしでしょうか.私はビルドに1時間以上かかるものをビルドし,それを眺めながら非リアの楽しくも悲しいTwitterを眺めています.
さて,本記事タイトルのTensorFlowSharpをXamarinで動かしたい!ということですが,経緯としては
- Xamarinで機械学習を試すにはAzure MLとかを使うしかない
- Azure ML触ってみた感じ楽しかったのですが,込み入ったことをことをするには少し物足りない(気がする)
- 最近になって iOSのCoreMLが出たけど,Androidも同様に,ということが出来ない
- 機械学習界隈が積み上げてきた資産をそのまま使いたい
といった自分の中での思いや何やらがあったためです.
さて現時点の進捗としては,
というところが確認できています.
成果物としてはこちらになります.
今回の記事では実際に学習したモデルを使って評価まで行きたかったのですが,準備が終わらず修論に追われ間に合いませんでした...っ! 次回のちゅうこ先生の作品にご期待ください.
それでは現時点進めているのはモデルの評価が可能なのかどうなのかの確認?と思われるかも知れませんが,今進めているのはiOSのサポートとなっています. ビルドするソースを2,3個忘れてシンボルが出力されていない,という状況なので,あと数時間もすればサポートできるんじゃないかなぁとは思っていますが,さてさてどうなるか...
ということで,とりあえずAndroidだけでもという方向けにビルド方法をちょこっと解説する記事にしたいと思います.
実験環境
- Mac (Linuxは自分がDocker環境で出来ていたので問題ないことを確認済み,Windowsは試していません)
- Bazel 0.81 (0.9だとビルドが通りません)
- Android NDK r14b (Bazel 0.81未満だとr12bが良かったのですが,完全に上手く言った組み合わせが今回の環境です)
Android向けにTensorFlowをビルドしてみよう
さて,Android向けにTensorFlowをビルドということで検索してみると色々と解説記事が出てきます. しかし内容としてはJavaで使うことしか書かれておらず(当たり前)C# で使うときに行う必要があるC-apiとしてのexportだったりが書かれていません.
ということで,そこから解説していきたいと思います.
- (1) TensorFlowの準備
当たり前ですね.ここから始めて行きましょう. 現時点で公開されているTensorFlowはr1.5が最新バージョンですが,TensorFlowSharpが対応しているAPIは現状r1.4となっています. そのためcloneしたらr1.4にcheckoutしましょう.
- (2) Android NDKの準備
実験環境に欄あるようにr14bをダウンロードし配置します.
その後TensorFlowのrootディレクトリにあるWORKSPACE
ファイルを編集し,Android NDKのPATHを指定します.
この時64bit対応にするためにapi_levelを21に引き上げておきましょう.
- (3) Android向けのTensorFlowの共有ライブラリの出力シンボル指定
ここが一番重要だったりします. Java向けのJNIのシンボルだけを出力するように設定されているシンボル出力を,C-api向けに変更します.
方法としては,tensorflow/contrib/android/jni/version_script.lds
ファイルを以下のように編集します.
VERS_1.0 { # Export JNI symbols. global: Java_*; JNI_OnLoad; JNI_OnUnload; + *TF_*; + *TFE_*; # Hide everything else. local: *; };
TF_
から始まるシンボルをTensorFlowSharpでコールしているため,ここでExportするようにしてあげます.
- (4) ビルドする
今回は一般的なAndroid Smartphoneのアーキテクチャのarmeabi-v7aとarm64-v8aを対象にビルドしていきます.
まずはarmeabi-v7a
CC_OPT_FLAGS="-march=armv7-a -std=c++11" ./configure bazel build -c opt //tensorflow/contrib/android:libtensorflow_inference.so --crosstooltop=//external:android/crosstool --host_crosstool_top=@bazel_tools/cpp:toolchain --cpu=armeabi-v7a
configureを叩くとcuda使うか?など色々聞かれますが,とりあえず全部 No でいいと思います(自分はモバイル向けだしどれも No かなと思ったため).
ビルドが終わるとbazel-bin/tensorflow/contrib/android
以下にlibtensorflow_inference.so
ファイルが生成されていると思います.
このファイルは各アーキテクチャ毎に必要なので別途保存しておいてください.
次にarm64-v8a
CC_OPT_FLAGS="-std=c++11" ./configure bazel build --config=android_arm64 //tensorflow/contrib/android:libtensorflow_inference.so --crosstooltop=//external:android/crosstool --host_crosstool_top=@bazel_tools/cpp:toolchain --cpu=arm64_v8a
だいたいおんなじですね. 他にx86_64とかx64とかもありますが,大体こんな感じです. tensorflowディレクトリのBUILDファイルを見るとどのように指定すればそのアーキテクチャのものもビルドできるかわかると思うので,上記のアーキテクチャ以外の物が必要であればそのようにビルドしてみてください.
- (5) TensorFlowSharpで使う
これで必要なファイルが揃いました.
ここからはもう楽をしちゃいましょう.上記のPRを引っ張ってきてビルドしたライブラリを配置してdotnet pack
しちゃいましょう.
えっ,解説は?という感じですが,古いバージョンのcsprojのマイグレートをして,Android向けにネイティブライブラリを配置してということを書くのは非常に...
ということで,その辺のネイティブライブラリ関連のことが気になる場合は
NuGet でプラットフォームごとにdllimportするネイティブライブラリを同梱してそれを使うラッパーのパッケージを作る方法 - 窓を作っては壊していた人のブログ
や
DllImport クロスプラットフォーム nuget パッケージ作成方法(unitypackage もあるよ!) - Qiita
このあたりを御覧ください.
最後に
さて,今回ちゃんとした成果物が出来ていないので記事としてもぺらいものになってしまいました. 最初Bazelを使わずにmakefileを叩いてビルドしていたのですが,完全に無駄でした.bazelを使いましょう.
あとネイティブライブラリをC# で使うのはそこまで厳しい茨の道ではないので,ぜひともネイティブの資産を上手く使えるC# erが増えてくれることを期待しています.