运行 Android 上的 Tensorflow 模型

Running a Tensorflow model on Android

我正在尝试找出在 Android 上训练和部署 Tensorflow 模型的工作流程。我知道 Whosebug 上的其他问题与此类似,但其中 none 似乎解决了我 运行 遇到的问题。

研究了 Tensorflow 存储库中的 Android 示例后,我认为工作流程应该是这样的:

  1. 在 Python 中构建和训练 Tensorflow 模型。
  2. 创建一个新图,并将所有相关节点(即不负责训练的节点)转移到这个新图。经过训练的权重变量作为常量导入,以便 C++ API 可以读取它们。
  3. 在 Java 中开发 Android GUI,使用 native 关键字来停止对 Tensorflow 模型的调用。
  4. 运行 javah 为 Tensorflow 本机调用生成 C/C++ 存根代码。
  5. 通过使用 Tensorflow C++ API 读取并访问 trained/serialized 模型来填写存根。
  6. 使用 Bazel 构建 Java 应用程序、原生 Tensorflow 接口(作为 .so 文件),并生成 APK。
  7. 使用 adb 部署 APK。

    第6步有问题。 Bazel 会愉快地编译一个本地(到 OSX).dylib,我可以通过 JNI 从 Java 调用它。 Android Studio 同样会生成一大堆 XML 代码来生成我想要的 GUI。但是,Bazel 希望所有 java 应用程序代码都位于 'WORKSPACE' 顶级目录(在 Tensorflow 存储库中),并且 Android Studio 立即链接到来自用于制作 GUI 的 SDK(我知道是因为我的 Bazel 编译 运行 在找不到这些资源时失败了)。我能找到强制 Bazel 交叉编译 .so 文件的唯一方法是使它成为 Android 规则的依赖规则。直接交叉编译本机库是我更喜欢移植我的 A.S。代码到 Bazel 项目。

    我如何计算这个平方? Bazel 应该会编译 Android 代码,但 Android Studio 生成 Bazel 无法编译的代码。 Google 中的所有示例只是为您提供了来自存储库的代码,而没有任何关于它是如何生成的线索。据我所知,Android Studio 应用程序的一部分 XML 应该是生成的,而不是手工制作的。如果可以手工制作,我该如何避免使用所有这些外部库?

    也许我的工作流程有误,或者 Bazel/Android Studio 的某些方面我不理解。任何帮助表示赞赏。

谢谢!

编辑:

我最终做的几件事可能有助于图书馆的成功建设:

  1. 我升级到最新的 Bazel。
  2. 我从源代码重建了 TensorFlow。
  3. 我在下面实现了推荐的 Bazel BUILD 文件,并添加了一些内容(取自 Android 示例):

    cc_binary(
    name = "libName.so",
    srcs = ["org_tensorflowtest_MyActivity.cc", 
            "org_tensorflowtest_MyActivity.h",
            "jni.h",
            "jni_md.h",
            ":libpthread.so"],
    deps = ["//tensorflow/core:android_tensorflow_lib",
            ],
    copts = [
        "-std=c++11",
        "-mfpu=neon",
        "-O2",
    ],
    linkopts = ["-llog -landroid -lm"],
    linkstatic = 1,
    linkshared = 1,
    )
    
    cc_binary(
         name = "libpthread.so",
         srcs = [],
         linkopts = ["-shared"],
         tags = [
             "manual",
             "notap",
         ],
    )
    

我还没有验证这个库是否可以在 Android 中加载和使用; Android Studio 1.5 似乎对承认原生库的存在非常挑剔。

在您的 WORKSPACE 文件中设置 Android NDK 后,Bazel 可以 cross-compile 为 Android 创建 .so,如下所示:

cc_binary(
    name = "libfoo.so",
    srcs = ["foo.cc"],
    deps = [":bar"],
    linkstatic = 1,
    linkshared = 1,
)

$ bazel build foo:libfoo.so \
    --crosstool_top=//external:android/crosstool --cpu=armeabi-v7a \
    --host_crosstool_top=@bazel_tools//tools/cpp:toolchain
$ file bazel-bin/foo/libfoo.so
bazel-bin/foo/libfoo.so: ELF 32-bit LSB  shared object, ARM, EABI5 version 1 (SYSV), dynamically linked (uses shared libs), not stripped

Bazel wants all of the java app code to be inside the 'WORKSPACE' top-level directory (in the Tensorflow repo)

当 0.1.4 发布(立即推送)并且我们推送了一些对 TensorFlow 和 Protobuf 的修复时,您可以开始使用 TensorFlow 存储库作为远程存储库。在您的 WORKSPACE 文件中进行设置后,您可以使用 @tensorflow//foo/bar 标签参考 TensorFlow 规则。

git clone --recurse-submodules https://github.com/tensorflow/tensorflow.git

注意:--recurse-submodules 对于拉取子模块很重要。

从这里安装 Bazel。 Bazel 是 TensorFlow 的主要构建系统。 现在,编辑WORKSPACE,我们可以在我们之前克隆的TensorFlow的根目录中找到WORKSPACE文件。

# Uncomment and update the paths in these entries to build the Android demo.
#android_sdk_repository(
#    name = "androidsdk",
#    api_level = 23,
#    build_tools_version = "25.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=14)

像下面我们的 sdk 和 ndk 路径:

android_sdk_repository(
    name = "androidsdk",
    api_level = 23,
    build_tools_version = "25.0.1",
    # Replace with path to Android SDK on your system
    path = "/Users/amitshekhar/Library/Android/sdk/",
)
android_ndk_repository(
    name="androidndk",
    path="/Users/amitshekhar/Downloads/android-ndk-r13/",
    api_level=14)

然后构建.so文件。

bazel build -c opt //tensorflow/contrib/android:libtensorflow_inference.so \
   --crosstool_top=//external:android/crosstool \
   --host_crosstool_top=@bazel_tools//tools/cpp:toolchain \
   --cpu=armeabi-v7a

用我们想要的目标架构替换 armeabi-v7a。 图书馆将位于:

bazel-bin/tensorflow/contrib/android/libtensorflow_inference.so

构建 Java 副本:

bazel build //tensorflow/contrib/android:android_tensorflow_inference_java

我们可以在以下位置找到 JAR 文件:

bazel-bin/tensorflow/contrib/android/libandroid_tensorflow_inference_java.jar

现在我们有了 jar 和 .so 文件。我已经构建了.so文件和jar,你可以直接使用project.

将 libandroid_tensorflow_inference_java.jar 放入 libs 文件夹中,然后右键单击并添加为库。

compile files('libs/libandroid_tensorflow_inference_java.jar')

在主目录中创建 jniLibs 文件夹,并将 libtensorflow_inference.so 放入 jniLibs/armeabi-v7a/ 文件夹中。

现在,我们将能够调用 TensorFlow Java API.

TensorFlow Java API 已经通过 class TensorFlowInferenceInterface 公开了所有必需的方法。

现在,我们必须使用模型路径调用 TensorFlow Java API 并加载它。

我写了一个完整的博客here