Android NDK 编译 TensorflowLite 的笔记

Android NDK 编译 TensorflowLite 的笔记

互联网上有很多关于 TensorflowLite 的 Android JNI 编译方法,但关于 TensorflowLite 本身的编译却不太好找。因此,我把自己的相关经验做一个总结分享。分享前先感谢一下小雨同学和谷歌 TensorflowLite 团队的朋友,他们为我提供了宝贵的经验和建议。

官网方法

Tensorflow 官网在 2.0 版本发布后做出相应的更新,TensorflowLite (简称 TFLite) 的资料在 For Mobile & IOT 版块,Android 的官方推荐方法在相应的 Guide 里面。由于官网登录有些技术门槛,我把官方的 TFLite 编译方式贴在下面:

配置 WORKSPACE 和 .bazelrc

执行 ./configure 脚本,设置下面几个环境变量参数:

ANDROID_SDK_HOME
ANDROID_SDK_API_LEVEL
ANDROID_NDK_HOME
ANDROID_NDK_API_LEVEL

如果这些环境变量在 configure 阶段没有配置,可以打开 Tensorflow 主目录下的 .tf_configure.bazelrc 文件,参考下面的配置方式,设定环境参数。

build --action_env ANDROID_NDK_HOME="/usr/local/android/android-ndk-r17c"
build --action_env ANDROID_NDK_API_LEVEL="18"
build --action_env ANDROID_BUILD_TOOLS_VERSION="28.0.3"
build --action_env ANDROID_SDK_API_LEVEL="23"
build --action_env ANDROID_SDK_HOME="/usr/local/android/android-sdk-linux"
构建与安装

只要 Bazel 配置完成,我们就可以再 Tensorflow 主目录用下面的脚本编译 TensorFlow Lite AAR。

bazel build --cxxopt='-std=c++11' -c opt         \
  --fat_apk_cpu=x86,x86_64,arm64-v8a,armeabi-v7a \
  //tensorflow/lite/java:tensorflow-lite

最终的编译结果 TensorFlow Lite AAR 以及 libtensorflowlite_jni.so 都会在 bazel-genfiles/tensorflow/lite/java/ 相应的 CPU 架构目录中。

如果直接调用 TFLite 的 JNI 接口能满足 Android 应用的业务需求,到这里已经解决问题了。

其他方法

我的业务场景要求利用 TFLite 的 C 类接口封装推理引擎,支撑业务架构的算法实现。因此,libtensorflowlite_jni.so 的可见函数不足以解决我面临的问题。

下面是 TFLite_jni 的符号导出情况:

...
000076e5 g    DF .text	0000000c  VERS_1.0    Java_org_tensorflow_lite_nnapi_NnApiDelegate_createDelegate
00007b19 g    DF .text	0000003c  VERS_1.0    Java_org_tensorflow_lite_NativeInterpreterWrapper_getInputCount
0000ce8d g    DF .text	00000068  VERS_1.0    Java_org_tensorflow_lite_Tensor_buffer
00007cb5 g    DF .text	00000030  VERS_1.0    Java_org_tensorflow_lite_NativeInterpreterWrapper_allowFp16PrecisionForFp32
0000823d g    DF .text	00000078  VERS_1.0    Java_org_tensorflow_lite_NativeInterpreterWrapper_getOutputQuantizationZeroPoint
00008351 g    DF .text	000002c0  VERS_1.0    Java_org_tensorflow_lite_NativeInterpreterWrapper_resizeInput
0000d935 g    DF .text	00000050  VERS_1.0    Java_org_tensorflow_lite_TensorFlowLite_schemaVersion
00007d41 g    DF .text	00000034  VERS_1.0    
.....

解决思路有两种,一种是修改 tensorflow/lite/java 下的 exported_symbols.lds文件:

*Java_*
*JNI_OnLoad
*JNI_OnUnload

TfLite*
*tflite*
*TFL_*

另一种思路是,直接编译 tensorflow/lite 下的 libtensorflowlite.so:

bazel build --cxxopt='-std=c++11' -c opt         \
  --config=android_arm \
  //tensorflow/lite:libtensorflowlite.so

其中 --config=android_arm 表示兼容 armeabi-v7a 架构的 Android 交叉编译,详情可以参考 tensorflow 主目录下的 tensorflow/BUILD 文件:

....

config_setting(
    name = "android_arm",
    values = { 
        "crosstool_top": "//external:android/crosstool",
        "cpu": "armeabi-v7a",
    },  
    visibility = ["//visibility:public"],
)

....

这个 BUILD 文件中包含了很多官方文档不容易查到编译参数。其实,这个 config_setting 相当于封装 2.0 以前 tensorflow/contrib/lite 时代的 --cpu=armeabi-v7a --crosstool_top=//external:android/crosstool 参数。现在这些常用而又繁琐的编译选项组合,已经在 BUILD 文件中通过 config_setting 函数封装好了。我们可以仔细查阅这个文件,使用业务需要的编译参数即可。

最后,展示部分 TFLite 比较完整的导出符号:

.....
00cd155  w   DF .text	000000a0  VERS_1.0    _ZN6tflite3ops7builtin15maximum_minimum4EvalILNS2_10KernelTypeE0ENS2_9MinimumOpEEE12TfLiteStatusP13TfLiteContextP10TfLiteNode
0013ea65 g    DF .text	0000000c  VERS_1.0    _ZN6tflite8internal7MfccDctC1Ev
0004f7ed  w   DF .text	00000648  VERS_1.0    _ZN6tflite3ops7builtin11activations14LogSoftmaxEvalILNS2_10KernelTypeE1EEE12TfLiteStatusP13TfLiteContextP10TfLiteNode
000642bd  w   DF .text	0000029c  VERS_1.0    _ZN6tflite13reference_ops9ArgMinMaxIhixNSt6__ndk18functionIFbhhEEEEEvRKNS_12RuntimeShapeEPKT_PKT1_S8_PT0_RKT2_
00098673  w   DF .text	00000254  VERS_1.0    _ZN6tflite13optimized_ops24FloatDepthwiseConvKernelILb1ELi0ELi1EE3RunEiiiPKfiS4_Pf
000d101d g    DF .text	00000006  VERS_1.0    _ZN6tflite3ops7builtin3mul4InitEP13TfLiteContextPKcj
001323b1 g    DF .text	00000eb0  VERS_1.0    _ZN6tflite3ops7builtin28unidirectional_sequence_lstm26CheckInputTensorDimensionsEP13TfLiteContextP10TfLiteNodeiiib
0019bb28  w   DO .data.rel.ro	00000014  VERS_1.0    _ZTVN6tflite16cpu_backend_gemm6detail14CustomGemvTaskIffffLNS0_18QuantizationFlavorE0EEE
000f9c93  w   DF .text	00000008  VERS_1.0    _ZZN6tflite3ops7builtin6reduce8EvalTypeIhEE12TfLiteStatusP13TfLiteContextP10TfLiteNodePNS2_9OpContextENS2_10ReduceTypeEENUlhhE0_8__invokeEhh
000a990d  w   DF .text	00000998  VERS_1.0    _ZN6tflite3ops7builtin3div7EvalDivILNS2_10KernelTypeE2EEEvP13TfLiteContextP10TfLiteNodeP15TfLiteDivParamsPKNS2_6OpDataEPK12TfLiteTensorSG_PSE_
0011fa0d  w   DF .text	00000b02  VERS_1.0    _ZN6tflite3ops7builtin3sub13EvalQuantizedILNS2_10KernelTypeE0EEEvP13TfLiteContextP10TfLiteNodeP15TfLiteSubParamsPKNS2_6OpDataEPK12TfLiteTensorSG_PSE_
00048bc9 g    DF .text	00000028  VERS_1.0    _ZN6tflite3ops7builtin11activations11SoftmaxInitEP13TfLiteContextPKcj
00094afd  w   DF .text	000002a4  VERS_1.0    _ZN6tflite3ops7builtin14depthwise_conv23EvalQuantizedPerChannelILNS2_10KernelTypeE0EEEvP13TfLiteContextP10TfLiteNodeP25TfLiteDepthwiseConvParamsPNS2_6OpDataEPK12TfLiteTensorSF_SF_PSD_
00108941  w   DF .text	0000015c  VERS_1.0    _ZN6tflite13reference_ops13RankOneSelectIbaEEvRKNS_12RuntimeShapeEPKT_S4_PKT0_S4_SA_S4_PS8_
00104965  w   DF .text	00000462  VERS_1.0    _ZN6tflite13reference_ops15ReverseSequenceIsiEEvPKT0_iiRKNS_12RuntimeShapeEPKT_S7_PS8_
0010b70d  w   DF .text	0000019c  VERS_1.0    _ZN6tflite3ops7builtin5slice22GetBeginAndSizeVectorsIxEEviPK12TfLiteTensorS6_PNSt6__ndk16vectorIiNS7_9allocatorIiEEEESC_
000887b5  w   DF .text	00000004  VERS_1.0    _ZN6tflite16cpu_backend_gemm6detail14CustomGemvTaskIffffLNS0_18QuantizationFlavorE0EED0Ev
0011d101  w   DF .text	000005b8  VERS_1.0    _ZN6tflite13reference_ops12StridedSliceIxEEvRKNS_18StridedSliceParamsERKNS_12RuntimeShapeEPKT_S7_PS8_
000516db  w   DF .text	00000006  VERS_1.0    _ZNSt6__ndk110__function6__funcIZN6tflite3ops7builtin11activations11TanhPrepareILNS5_10KernelTypeE0EEE12TfLiteStatusP13TfLiteContextP10TfLiteNodeEUlfE0_NS_9allocatorISD_EEFffEEclEOf
......

有了这个符号完整的 TFLite 动态库,我们就可以封装自己的 JNI 推理引擎了。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值