本篇主要作为一个操作手册来介绍怎么编译Tensorflow和记录编译过程中踩过的坑

建议在编译TFLite之前通读一遍本文,可以少走很多弯路。

在安卓上使用TFLite一般可以通过直接在gradle中引用dependencies的方式增加TFlite依赖。

但在一些自定义场景下需要我们手动去编译TFlite依赖库,比如在C++下开发了基于Tensorflow Lite的模型或者逻辑。

google预编译的TFLite so库的C++ 符号只有基本的几个JNI接口,如果我们想在安卓的C++ 层对TFLite进行调用,则需要通过修改Tensorflow Lite源码的方式来构建自定义的TFLite so。

以下默认是以Linux或者Mac平台,win平台类推

需要的环境和工具

· Tensorflow 2.0.0 源码,git下载

编译过程需要以下工具,这些版本是编译过程中坑最少的,其他版本虽然也可以编译但坑不是一般的多。

· Bazel - v0.26.0, 或者 < v2.0.0
· Android NDK - 大于v16的版本
· Python
· g++,gcc等常用的编译工具

其中Bazel和ndk是必备的,其他的工具在编译过程中报错的时候根据提示信息再下载对应工具就行。

Bazel版本要求
Bazel目前已经更新到3.0.0版本,而构建的时候还需要指定这么低的版本,是因为Tensorflow的配置脚本最高只支持0.26.0,而编译的话则可以支持到 v2.0.0(不包含2.0.0) 最高。v2.0.0版本的Bazel因为取消了android_library中对 aapt_vesion的支持,所以不能使用。虽然官方DOC说 aapt_version 在2.0.0中还支持,但实测是不支持的(tmd让我怀疑人生)。

Android NDK版本要求
坑最少的版本是 v16以上,实测v18可以一次性编译通过。其他版本的ndk编译中大部分的坑不是由Android NDK引起的,主要还是Bazel的bug。

配置

进入tensorflow下执行 configure 命令,配置你的系统环境,包括NDK,SDK,python等位置。其他选项默认值即可。
下载的Tensorflow根目录如下。

PhoenixdeMac-mini:~/…/tensorflow$ ll
total 432
drwxr-xr-x 36 phoenixzheng admin 1152 7 10 17:11 .
drwxr-xr-x 14 phoenixzheng admin 448 7 8 15:37 …
-rw-r–r-- 1 phoenixzheng admin 8196 7 9 14:10 .DS_Store
-rw-r–r-- 1 phoenixzheng admin 225 7 7 10:36 .bazelrc
drwxr-xr-x 3 phoenixzheng admin 96 11 12 2019 .github
-rw-r–r-- 1 phoenixzheng admin 776 11 12 2019 .gitignore
-rw-r–r-- 1 phoenixzheng admin 797 7 13 16:51 .tf_configure.bazelrc

-rwxr-xr-x 1 phoenixzheng admin 285 11 12 2019 configure
-rw-r–r-- 1 phoenixzheng admin 780 11 12 2019 configure.cmd
-rw-r–r-- 1 phoenixzheng admin 57803 11 12 2019 configure.py

不想配置环境也可以通过修改WORKSPACE文件内容,增加 android_ndk_repository()的方式指定NDK和SDK的位置。

执行完configrue之后会有 tf_configure.bazelrc 隐藏文件,所需要的配置信息在这里面有,而且可以修改。这些配置信息是从 .bazelrc 里抽取出来的。现在你可以手动修改 tf_configure.bazelrc 文件来指定不同的NDK路径。

build --action_env ANDROID_NDK_HOME="/Users/phoenixzheng/Library/Android/sdk/ndk-bundle-r18b/"

编译

在Tensorflow目录下执行以下命令构建CPU版本

bazel build --cxxopt=’-std=c++11’ -c opt
–fat_apk_cpu=armeabi-v7a,arm64-v8a
//tensorflow/lite/java:tensorflow-lite

在Tensorflow目录下执行以下命令构建GPU版本

bazel build --cxxopt=’-std=c++11’ -c opt
–fat_apk_cpu=armeabi-v7a,arm64-v8a
//tensorflow/lite/java:tensorflow-lite-gpu

严格按照上面的流程执行的话就可以正确地编出 tensorflow-lite.aar了。编译过程会需要从官方镜像拉取bazel的构建依赖工具,如果发生 IO 失败的话八成是你网络问题,建议梯子处理。

Android NDK版本的影响

C++函数签名

不同版本的NDK主要区别是C++标准的不同。v16以后的C++标准只支持 libc++ 标准库,v16之前的包括v16支持gnustl / libc++ / STLport 三个标准。

不同C++标准的影响主要是在动态库链接的时候,可能会导致 undefined reference 错误。

比如用了 libc++ 标准库构建了tensorflow lite so,但在安卓上构建的时候用的是另外一个标准的c++,例如gnu stl,那么就会引起链接错误的问题。

因为不同的C++标准的函数签名是不一样的。比如cmath 的 std::round() 函数,

gnu stl 签名

std::round()

libc++ 签名

std::__ndk1::round()

所以如果tflite so是用libc++标准编出来的,那么它应该要找 std::__ndk1::round() 签名的函数。但如果在安卓构建的时候用gnustl去链接 tflite so,因为在gnustl标准下,符号表里的函数签名是 std::round(),自然就找不到了。

和第三方库混编

另外一些经常引起问题的情况是和第三方库混编。比如特化的tflite 模型需要结合OpenCV库使用,C++代码既链接了tflite so,又链接了opencv so。

引起问题的原因是,opencv so是用一种C++标准,而tflite so又是用另外一种标准。甚至还存在opencv 和tflite用的是不同版本的ndk编译的情况。因为最终大家都需要编到同一个so里,他们需要链接同一个c++标准库,而如果opencv和tflite的so在编译的时候就依赖了不同的c++标准库,那么最终是没办法链接到同一个so里的。

因此比较复杂的构建依赖下,必须保证大家都是在同一个ndk版本和c++标准下编译不同的动态库,才能保证在最后的链接阶段没问题。

修改导出符号表

默认的TFLite只导出了部分C++函数符号,我们可以通过修改 version_script.lds 来增加需要导出的符号。
.../TFLite/tensorflow/tensorflow/lite/java/src/main/native/version_script.lds

修改后的如下

VERS_1.0 {# Export JNI symbols.global:Java_*;JNI_OnLoad;JNI_OnUnload;*FlatBufferModel*;*BuiltinOpResolver*;*Interpreter*;*MutableOpResolver*;*TfLite*;*ErrorReporter*;# Hide everything else.local:*;
};

疑难杂症

最经常遇到的问题是需要用低版本的ndk去构建 so。因为很多现有的工程会依赖大量已经编译好的第三方so,而他们通常是在低版本下用默认的 gnustl标准 编译的。如果用高于16的NDK版本编译tflite so的话,默认只有libc++标准库。libc++标准的函数签名跟低版本的默认c++库 gnustl 标准不一样,所以会有各种莫名其妙的问题。

这也就导致必须要用低版本的ndk去编译tflite。然而低版本的不止bazel本身有bug,ndk也有bug。

另外个办法是把现在工程中的所有第三方so库都用跟tflite so统一的ndk版本编一遍,这是下策。

需要注意的是低版本编译tflite so需要避免使用ndk-r11,bazel对ndk-r11的解析有bug,会导致编译错误,并且没法人工改正,可以参考TFLite - NDK11 构建问题。

常见的第二个问题是包含 armeabi-v7a 架构的编译失败,但单独编arm64-v8a是OK的。具体报错信息是std::round() not defined。std::round()是cmath下的标准接口,它的定义在cmath头文件里,经过分析发现它受到_GLIBCXX_USE_C99_MATH_TR1宏限制。所以暴力修改的话可以通过修改ndk的cmath头文件来解决。

路径:.../sdk/ndk-bundle-r14b/sources/cxx-stl/gnu-libstdc++/4.9/include/cmath
line: 925

#if __cplusplus >= 201103L#define _GLIBCXX_USE_C99_MATH_TR1 1 // << 增加#ifdef _GLIBCXX_USE_C99_MATH_TR1#undef acosh
#undef acoshf
#undef acoshl
...

进一步搜索这个宏会发现在armeabi-v7a下的c++config.h和arm64-v8a下的有差异,

目前没有仔细研究_GLIBCXX_USE_C99_MATH_TR1在32bit机器上为什么被关掉,但打开了这个宏之后实测也没有问题。所以如果非得要支持32位机型的话可以这么修改。

Tensroflow 手动编译TFLite相关推荐

  1. linux内核centos6.9,CentOS6.9手动编译并更新Kernel内核版本

    Kernel是Linux操作系统的核心部分.它由操作系统中用于管理存储器.文件.外设和系统资源的那些部分组成.Kernel是操作系统的核心,掌控着所有硬件设备的控制权. 内核就是系统上面的一个文件,这 ...

  2. CentOS6.9下手动编译并安装Python3.7.0

    CentOS6.9默认安装的python版本为2.6.6,若想安装python3以上版本,只能手工编译安装 下面介绍Python-3.7.0版本的手动编译并安装的步骤 1.下载Python-3.7.0 ...

  3. 图解命令行手动编译构建一个win32汇编程序

    首先安装masm32 V11.0:安装完后出现下列对话框: 图没截好:下面图的 k. 是 link. :ktopPath是DesktopPath: 此段代码的含义是用WScript创建了一个快捷方式, ...

  4. 搞定JSP第一个Servlet例子并且还是手动编译

    前提是,Java Web 开发环境配置好. 当前Tomcat配置指向的目录是 ...... <Context path="" docBase="D:/Users/s ...

  5. 用csc命令行手动编译cs文件

    一般初学c#时,用记事本写代码,然后用命令行执行csc命令行可以编译cs文件.方法有两种 1:配置环境,一劳永逸 一般来说在C:\Windows\Microsoft.NET\Framework\v4. ...

  6. 手动编译 lombok_Lombok,一种编译时Java注释预处理器,可最大程度地减少代码大小...

    手动编译 lombok 在本文中,我们将看到如何在常规Java代码中使用lombok来最大程度地减少代码长度和冗余. 什么是Lombok? Lombok,一个编译时注释预处理器,有助于在编译时注入一些 ...

  7. $compile 手动编译

    angularjs里比较重要但又很少手动调用的要属$compile服务了,通常在写组件或指令时,都是angularjs自动编译完成的,但有时我们可能需要手动编译,比如封装一个table组件,根据参数实 ...

  8. linux 内核编译不能打字,linux系统升级后,手动编译的kernel无法启动问题

    linux系统升级后,手动编译的kernel无法启动问题 linux系统升级后,手动编译的kernel无法启动问题 做开发相关,需要编译3.18的kernel,x86_64的,但是我的deepin升级 ...

  9. 使用csc手动编译cs文件

    1.首先定位csc.exe文件路径:一般来说是C:\Windows\Microsoft.NET\Framework\v4.0.30319(v4.0.30319是版本号,具体版本号取决你实际安装的版本) ...

最新文章

  1. 我的第二故乡 – 广州
  2. Nginx教程系列五:Nginx+Keepalived搭建高可用主从架构
  3. 如何利用 gulp 压缩混淆 “上古”时期的项目文件
  4. 路飞学城-python爬虫密训-第三章
  5. isset、empty、var==null、is_null、var===null详细理解
  6. 电脑系统哪个最好用_袪痘袪痘印哪个产品最好 祛痘印最好用的产品十大推荐...
  7. vue2.0+ajax封装,vue2 封装 ajax插件
  8. HDU 1407 测试你是否和LTC水平一样高 枚举、二分、hash
  9. mysql 一对多约束条件_MySQL数据库/约束条件与表关系.md · 静谧之裳/python-learn - Gitee.com...
  10. uniapp苹果无法上架_uniapp无法上架IOS包怎么办
  11. .net实现串口通信
  12. 如何提高FPGA的运行速度
  13. android 小米8底部黑色高度太高,小米8评测:众多黑科技加持,能否跃身高端行列?...
  14. VVC代码阅读(2)compressGOP函数(1)
  15. 大疆无人机自动避障技术盘点
  16. 【亲测有效】win10修改电脑系统字体大小
  17. linux 命令 root用户把某个文件权限给到普通用户
  18. nvcc编译器之编译内幕(chapter 23)
  19. CTO要越过的几道坎儿
  20. MemoryError: Unable to allocate 1.43 TiB for an array with shape (3700, 5300) and data type float64

热门文章

  1. 【SpringCloud】微服务笔记
  2. Error evaluating expression ‘xxxxx != null and xxxxxx!= ’
  3. 【Mask scoring RCNN】实现目标检测
  4. 红米5a手机html查看器,红米5A解锁BL教程_红米5A一键解锁的方法
  5. 计算机基础教学能力比赛教案,全国“XX杯”说课大赛计算机应用基础类优秀作品:Word图文混排教案...
  6. 方形图片转为圆形图片
  7. N1盒子单臂路由设置
  8. Spring事务管理案例
  9. Atitit 二维码和条码的历史发展 1.发展历程 编辑提及二维码的诞生,我们还得倒回至上个世纪 60 年代之后的日本,虽然 1945 年的第二次世界大战之中日本沦为战败国,可是在经济方面日本却进入
  10. 【测试开发】web 自动化测试 --- selenium4