ORNEW

TensorFlowをAndroidで実行するためのビルド方法(〜v0.10.0版)

Share on Facebook
Pocket

概要

この記事では、TensorFlowをAndroidで実行するための動的ライブラリのビルド手順を解説しています。

TensorFlow バージョンv0.10.0を対象としています。

環境構築

インストールする必要があるものは以下の通りです。この3つのインストール方法は省略します。

私は、DockerでクリーンなUbuntuコンテナを作成し、その中で環境構築を行っています。TensorFlowは開発速度が非常に早く、数日間で大幅にコードが修正されることもあります。ですので、pullしたら数日前は動いたコードが動かなかったりする、なんてことは日常茶飯事です。逆もまた然りです。とはいえバグがたくさんあるので、どの道どうしても最新状態にしたくなると思います。その時、Dockerで環境構築していれば、作って試して、だめならすぐ捨てられます。その場合も、今動いている環境を壊しません。当面はDockerで動かすのが賢いと私は考えています。

SDKとNDKのパスを通しておきます。

export ANDROID_HOME=...
export ANDROID_NDK_HOME=...
export PATH=${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools
export PATH=${PATH}:${ANDROID_NDK_HOME}

簡単のため、シェル変数を定義しておきます。ツールバージョンを変える場合はここで読み替えてください。ただし、以下のバージョン以外で動くかは不明です。

SDK_API_VERSION=23
BUILD_TOOLS_VERSION=24.0.3

ツールをインストールします。

echo y | android update sdk -u -t "android-${SDK_API_VERSION}"
android list sdk -a | grep "Android SDK Build-tools, revision ${BUILD_TOOLS_VERSION}" | awk '{sub(/.$/,"",$1);print $1}' | xargs -I{} sh -c "echo y | android update sdk -a -u -t {}"

TensorFlowのセットアップを行います。クローンしたら中へ移動します。サブモジュールを再帰的にクローンすることを忘れないでください。

git clone --recursive https://github.com/tensorflow/tensorflow.git
cd tensorflow

WORKSPACEを修正し、Android SDKとNDKのパスを設定します。

patch -up0 WORKSPACE <<EOS
--- WORKSPACE
+++ WORKSPACE
@@ -1,18 +1,18 @@
 workspace(name = "org_tensorflow")

 # Uncomment and update the paths in these entries to build the Android demo.
-#android_sdk_repository(
-#    name = "androidsdk",
-#    api_level = 23,
-#    build_tools_version = "23.0.1",
-#    # Replace with path to Android SDK on your system
-#    path = "<PATH_TO_SDK>",
-#)
-#
-#android_ndk_repository(
-#    name="androidndk",
-#    path="<PATH_TO_NDK>",
-#    api_level=21)
+android_sdk_repository(
+    name = "androidsdk",
+    api_level = ${SDK_API_VERSION},
+    build_tools_version = "${BUILD_TOOLS_VERSION}",
+    # Replace with path to Android SDK on your system
+    path = "${ANDROID_HOME}",
+)
+
+android_ndk_repository(
+    name="androidndk",
+    path="${ANDROID_NDK_HOME}",
+    api_level=21)

 # Please add all new TensorFlow dependencies in workspace.bzl.
 load("//tensorflow:workspace.bzl", "tf_workspace")
EOS

これで、構築は完了です。

Bazelパッケージ作成

パッケージを作成しますが、ここで注意点があります。バグにより、TensorFlowのワークスペース内のパッケージが外部ワークスペースから参照できません。r0.11.0rcで改善されているのが確認できています。v0.11.0では手順が変わる(改善される)ため、別記事にしてまとめてあります。

ensorFlowをAndroidで実行するためのビルド方法(v0.11.0rc0版)

v0.10.0まででTensorFlowのライブラリを作りたい場合、TensorFlowワークスペース内部にパッケージを作成する必要があります。例としてexampleパッケージを作成します。

cd tensorflow/third_party/
mkdir example
cd example

ディレクトリを作ったら、Bazelパッケージを定義したBUILDファイルを作ります。

package(default_visibility = ["//visibility:public"])

load("//tensorflow:tensorflow.bzl", "tf_copts")

cc_binary(
    name = "libexample.so",
    srcs = glob([
        # ビルド対象のソースファイルをglob指定
    ]) + [],
    copts = tf_copts() + [
            "-std=gnu++11",
            "-fno-exceptions",
            "-DEIGEN_AVOID_STL_ARRAY",
            "-DSELECTIVE_REGISTRATION",
            "-mfpu=neon",
            "-DMIN_LOG_LEVEL=0",
            "-DTF_LEAN_BINARY",
            "-O2",
            "-fPIE",
            ],
    linkopts = [
        "-landroid",
        "-ljnigraphics",
        "-llog",
        "-lm",
        "-z defs",
        "-s",
        "-Wl,--icf=all",
        "-Wl,--exclude-libs,ALL",
    ],
    linkshared = 1,
    linkstatic = 1,
    tags = [
        "manual",
        "notap",
    ],
    deps = ["//tensorflow/core:android_tensorflow_lib"],
)

BazelのBUILDファイルの説明はドキュメントを読んで下さい。

TensorFlowを使うにあたり、必要な点を説明していきます。

まず、load("//tensorflow:tensorflow.bzl", "tf_copts")によって、TensorFlowのBazel設定を読み込みます。コンパイルオプションにて、copts = tf_copts()と指定しますが、これはTensorFlowの基本的なデフォルトのビルド設定を指定しているだけで、Android用の設定が含まれていません。ですので、+ [に続けてオプションを追加しています。尚、このオプション群の意味がわからない場合は変更しないことをおすすめします。TensorFlowはGNU拡張ありのC++11以外はサポートしていません。linkoptsにてリンカオプションを指定しています。ここで、Androidライブラリ等をリンクしています。最後に、deps = ["//tensorflow/core:android_tensorflow_lib"]で、TensorFlowのコアライブラリのandroid_tensorflow_libに依存していることを定義しています。尚、このandroid_tensorflow_libは単純にAndroidに必要な各モジュールへの依存を定義しただけのパッケージです。不要なモジュールを減らしたり、逆に追加モジュールを増やす場合は直接指定します。

ここまで設定できたら、BUILDファイルのあるディレクトリ下にソースコードを作成します。Androidで利用するため、JNIに準じたコードを作成するとよいでしょう。具体的な実装は割愛します。

ビルド

ビルド方法について説明します。

まず、クローンしたTensorFlowリポジトリのルートディレクトリに移動します。Bazelの仕様上、カレントディレクトリがWORKSPACEファイルのあるディレクトリでないとビルドできません。移動したら、bazelコマンドでビルドします。

cd <TensorFlow dir>
bazel build //tensorflow/third_party/example:libexample.so --crosstool_top=//external:android/crosstool --cpu=armeabi-v7a --host_crosstool_top=@bazel_tools//tools/cpp:toolchain

パッケージパスは、今回の例の場合は//tensorflow/third_party/example:libexample.soと指定します。Bazelのクエリについては公式ドキュメントを読んで下さい。重要なのはオプションです。--crosstool_top=//external:android/crosstool --cpu=armeabi-v7a --host_crosstool_top=@bazel_tools//tools/cpp:toolchainは必ず指定してください。クロスコンパイラツールとABIを指定しないとコンパイルエラーになります。これら以外のオプションに関しては、必要に応じて指定してください。

ビルドに成功したら、<TensorFlow dir>/bazel-bin/tensorflow/third_party/example/libexample.soが作成されているはずです。あとは、この動的ライブラリをAndroidアプリケーションから実行するだけとなります。

Android StudioでJNIに準じた動的ライブラリを実行する場合、まずAndroidManifest.xmlのあるディレクトリ下にjniLibs/<abi-name>というディレクトリ作成し、その中に動的ライブラリをコピーします。あとはJavaのパッケージでJNIに準じた定義を行うだけです。JNIに関しては調べれば情報がたくさん出てきますので、省略します。

さいごに

さらっと解説してますが、ここまでの情報は公式ドキュメントには一切まとまっておりません。GitHubのIssue上に分散した情報を拾い上げて、多くのバグに悩まされ、1ヶ月以上かかってここまでやっていたりします。開発途中のライブラリであるため、ここに書いてある情報もいつまで有効かは不明ですので、ご注意ください。