运行 Android 上的 Tensorflow 模型
Running a Tensorflow model on Android
我正在尝试找出在 Android 上训练和部署 Tensorflow 模型的工作流程。我知道 Whosebug 上的其他问题与此类似,但其中 none 似乎解决了我 运行 遇到的问题。
研究了 Tensorflow 存储库中的 Android 示例后,我认为工作流程应该是这样的:
- 在 Python 中构建和训练 Tensorflow 模型。
- 创建一个新图,并将所有相关节点(即不负责训练的节点)转移到这个新图。经过训练的权重变量作为常量导入,以便 C++ API 可以读取它们。
- 在 Java 中开发 Android GUI,使用 native 关键字来停止对 Tensorflow 模型的调用。
- 运行 javah 为 Tensorflow 本机调用生成 C/C++ 存根代码。
- 通过使用 Tensorflow C++ API 读取并访问 trained/serialized 模型来填写存根。
- 使用 Bazel 构建 Java 应用程序、原生 Tensorflow 接口(作为 .so 文件),并生成 APK。
使用 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 的某些方面我不理解。任何帮助表示赞赏。
谢谢!
编辑:
我最终做的几件事可能有助于图书馆的成功建设:
- 我升级到最新的 Bazel。
- 我从源代码重建了 TensorFlow。
我在下面实现了推荐的 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。
我正在尝试找出在 Android 上训练和部署 Tensorflow 模型的工作流程。我知道 Whosebug 上的其他问题与此类似,但其中 none 似乎解决了我 运行 遇到的问题。
研究了 Tensorflow 存储库中的 Android 示例后,我认为工作流程应该是这样的:
- 在 Python 中构建和训练 Tensorflow 模型。
- 创建一个新图,并将所有相关节点(即不负责训练的节点)转移到这个新图。经过训练的权重变量作为常量导入,以便 C++ API 可以读取它们。
- 在 Java 中开发 Android GUI,使用 native 关键字来停止对 Tensorflow 模型的调用。
- 运行 javah 为 Tensorflow 本机调用生成 C/C++ 存根代码。
- 通过使用 Tensorflow C++ API 读取并访问 trained/serialized 模型来填写存根。
- 使用 Bazel 构建 Java 应用程序、原生 Tensorflow 接口(作为 .so 文件),并生成 APK。
使用 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 的某些方面我不理解。任何帮助表示赞赏。
谢谢!
编辑:
我最终做的几件事可能有助于图书馆的成功建设:
- 我升级到最新的 Bazel。
- 我从源代码重建了 TensorFlow。
我在下面实现了推荐的 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。