ORNEW

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

Share on Facebook
Pocket

概要

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

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

これ以前のバージョンでは、バグ回避のため手順が異なります。こちらの記事をご参照ください。

TensorFlowをAndroidで実行するためのビルド方法(〜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
TENSORFLOW_TAG=v0.11.0rc0

これらのツールをインストールします。

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 {}"

Bazelワークスペース作成

任意のロケーションにBazelワークスペースを作成します。これがプロジェクトそのものとなります。ここでは、例としてexampleというワークスペースを作成します。

ワークスペースにしたいディレクトリに移動したら、WORKSPACEという名前のファイルを作成します。

cat << EOS > WORKSPACE
workspace(name = "example")

android_sdk_repository(
    name = "androidsdk",
    api_level = "${SDK_API_VERSION}",
    build_tools_version = "${BUILD_TOOLS_VERSION}",
    path = "${ANDROID_HOME}",
)

android_ndk_repository(
    name="androidndk",
    path="${ANDROID_NDK_HOME}",
    api_level=21)

git_repository(
    name = "org_tensorflow",
    remote = "https://github.com/tensorflow/tensorflow.git",
    init_submodules = 1,
    tag = "${TENSORFLOW_TAG}"
)

load("@org_tensorflow//tensorflow:workspace.bzl", "tf_workspace")
tf_workspace()
EOS

一行目以外は、全て必須となります。TensorFlowはorg_tensorflowという名前で参照しないと、今後のバージョンでエラーになると警告が出ます。android_sdk_repositoryandroid_ndk_repositoryは見ての通りSDKとNDKの設定を行っています。これを忘れると、SDKやNDKの関係なさ気な意味不明な場所でエラーが発生します(おそらく、protocol buffersのビルドでコケます。何故か。)

@org_tensorflow//tensorflow:workspace.bzlというクエリで、TensorFlowのBazel設定ファイルを読み込みます。tf_workspaceという関数を使うとTensorFlowのWORKSPACEに必要な依存関係が自動で設定されます。

次に、Bazelパッケージを定義したBUILDファイルを作ります。目的に応じて自由にかけますが、ここではlibexample.soという名前で、Androidに組み込むためのJNI動的ライブラリをビルドすることとします。ここでは、WORKSPACEと同じディレクトリに作成したものとして考えます。

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

cc_binary(
    name = "libexample.so",
    srcs = glob([
        # ビルド対象のソースファイルをglob指定
    ]),
    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 = ["@org_tensorflow//tensorflow/core:android_tensorflow_lib"],
)

BazelのBUILDファイルの説明はドキュメントを読んで下さい。TensorFlowを使うにあたり、必要な点を説明していきます。

このオプション群の意味がわからない場合は変更しないことをおすすめします。TensorFlowはGNU拡張ありのC++11以外はサポートしていません。linkoptsにてリンカオプションを指定しています。ここで、Androidライブラリ等をリンクしています。最後に、deps = ["@org_tensorflow//tensorflow/core:android_tensorflow_lib"]で、TensorFlowのコアライブラリのandroid_tensorflow_libに依存していることを定義しています。尚、このandroid_tensorflow_libは単純にAndroidに必要な各モジュールへの依存を定義しただけのパッケージです。不要なモジュールを減らしたり、逆に追加モジュールを増やす場合は直接指定します。

ビルド

ビルド方法について説明します。Bazelの仕様上、カレントディレクトリにWORKSPACEがあるディレクトリ(つまり、ワークスペースのルートディレクトリ)でないとbazel buildを実行できません。最初にWORKSPACEを作成したディレクトリに移動してから、以下のコマンドを実行します。

bazel build libexample.so --crosstool_top=//external:android/crosstool --cpu=armeabi-v7a --host_crosstool_top=@bazel_tools//tools/cpp:toolchain

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

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

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

さいごに

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