18

我正在尝试找出在 Android 上训练和部署 Tensorflow 模型的工作流程。我知道 StackOverflow 上与此类似的其他问题,但它们似乎都没有解决我遇到的问题。

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

  1. 在 Python 中构建和训练 TensorFlow 模型。
  2. 创建一个新图,并将所有相关节点(即不是负责训练的节点)转移到这个新图上。经过训练的权重变量作为常量导入,以便 C++ API 可以读取它们。
  3. 在 Java 中开发 Android GUI,使用 native 关键字来存根对 Tensorflow 模型的调用。
  4. 运行 javah 为 Tensorflow 本机调用生成 C/C++ 存根代码。
  5. 使用 Tensorflow C++ API 填写存根,以读取和访问经过训练/序列化的模型。
  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 立即从 SDK 链接到各种外部库以制作 GUI(我知道是因为我的Bazel 编译运行在找不到这些资源时失败)。我能找到强制 Bazel 交叉编译 .so 文件的唯一方法是使其成为 Android 规则的依赖规则。直接交叉编译原生库是我更喜欢将我的 AS 代码移植到 Bazel 项目的方式。

    我该如何解决这个问题?Bazel 应该会编译 Android 代码,但 Android Studio 会生成 Bazel 无法编译的代码。来自 Google 的所有示例都只是为您提供了来自 repo 的代码,而没有任何关于它是如何生成的线索的。据我所知,作为 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 似乎对承认本机库的存在非常挑剔。

4

2 回答 2

10

在您的 WORKSPACE 文件中设置 Android NDK 后,Bazel 可以为 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 希望所有 Java 应用程序代码都位于“WORKSPACE”顶级目录中(在 Tensorflow 存储库中)

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

于 2016-01-20T14:30:31.047 回答
1
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,您可以直接从项目中使用。

将 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 通过类 TensorFlowInferenceInterface 公开了所有必需的方法。

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

我在这里写了一个完整的博客。

于 2017-03-11T13:21:33.510 回答