前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Jetson TX1上安装Tensorflow Serving遇到的问题总结

Jetson TX1上安装Tensorflow Serving遇到的问题总结

原创
作者头像
ericzli
修改2021-08-21 13:43:19
2.6K0
修改2021-08-21 13:43:19
举报
文章被收录于专栏:ericzliericzli

本文的目的是分享在TX1上安装Tensorflow Serving时遇到的主要问题,避免重复踩坑。

Jetson TX1是一块带GPU的板子,预装了ubuntu系统,ARM架构,详情可参考NVidia官网。对于Tensorflow训练出来的模型,工程部署一般都采用Tensorflow Serving。目前在网络上暂时没找到Jetson系列板子上成功安装Tensorflow Serving的案例,而本人在安装过程中遇到的很多问题都搜不到解决方法,只能自己摸索,最终成功安装并运行。当前TX1环境是用的JetPack 3.2.1(CUDA9 + cuDNN7)。

这里安装步骤主要参考官网的指导:https://www.tensorflow.org/serving/setup,但有些细节会不一样,比如安装命令是bazel build -c opt --config=cuda tensorflow_serving/... --action_env=PYTHON_BIN_PATH=/usr/bin/python,这里config=cuda是必要的,否则即使在bazel.rc中指定了gpu,生成出的Tensorflow Serving也不能用GPU。

原本想尝试交叉编译的,因为板子上编译很慢,但考虑板子上编译坑会少点,所以这里只尝试了在板子上编译的方式。安装遇到的问题这里只列出主要的。

问题1:

ERROR: no such target '@org_tensorflow//third_party/gpus/crosstool:crosstool': target 'crosstool' not declared in package 'third_party/gpus/crosstool' defined by /data/rootcache/bazel/_bazel_root/46688ad2577b25fcaed4521437622fa6/external/org_tensorflow/third_party/gpus/crosstool/BUILD

分析与解决:

这个报错的含义是:crosstool的定义找不到,而根据bazel.rc中的配置,crosstool期望在下面列出的那个BUILD文件里定义,而那个BUILD文件里没定义crosstool。

事实上,这个BUILD文件是空的。网上能搜到的解决方案比较多,下面的方案至少是验证可行的

(1) 修改serving/tools/bazel.rc文件,将@org_tensorflow//third_party/gpus/crosstool替换成@local_config_cuda//crosstool:toolchain。

(2) 执行bazel clean --expunge && export TF_NEED_CUDA=1

(3) 再执行bazel query 'kind(rule, @local_config_cuda//...)',这样相当于在定义local_config_cuda这个rule

问题2:

磁盘满

分析与解决:

嵌入式设备存储往往并不大,而Tensorflow安装过程需要的存储空间非常大,很容易遇到磁盘空间不够。目前能想到的办法就是用NFS来扩展存储空间,至于NFS具体操作不难搜到。这里使用NFS时会出现如下告警

root@tegra-ubuntu:/data/serving# /data/bazel/output/bazel build -c opt --config=cuda tensorflow_serving/... --action_env=PYTHON_BIN_PATH=/usr/bin/python

WARNING: Output base '/data/rootcache/bazel/_bazel_root/46688ad2577b25fcaed4521437622fa6' is on NFS. This may lead to surprising failures and undetermined behavior.

曾经在网上搜过这个问题,但能找到的答复大意是“既然明确提示了,就不要用NFS”。因为没有别的选择,所以我还是用的NFS,并安装成功了,且能跑通GoogLeNet和Resnet,说明NFS并非不能用。实际可以在编译完成后,把需要的可执行文件保留,其它中间文件都可以删掉,这样就不需要太多存储(放NFS上运行也没问题,就是加载会很慢)。

问题3:

提示大意是编译失败,cc1被kill了

分析与解决:

其实被这个问题难到了一段时间,后来偶然在串口终端上发现了内存不足并kill进程的打印,于是立即就明白了问题的原因,就是内存不足导致编译进程被kill。

(1) 一般直接重试就好,会继续编译,而不是老失败在一个地方

(2) 如果经常编译没多久就出现这个问题,说明内存很不够用,可以考虑加swap,我加的2GB(TX1本身有4GB的内存)。这个加太多也不好,因为大量的swap IO会导致编译很慢

(3) 多次尝试发现这个问题都出在同一个地方时,可以在bazel编译时加个--jobs=2的选项,来限制同时编译任务数量,默认是用CPU核数(TX1上是4)。建议挺过那几个比较耗内存的编译任务后,再停掉编译并重新用之前的方式编译,这样会快一些,不然一天一夜都编译不完。

问题4:

找不到cudnn的报错;提示找不到nccl

分析与解决:

这个两个问题其实是独立的,但比较类似,所以放一起了。cudnn的问题只需要编译前执行下面的命令

export CUDNN_INSTALL_PATH=/usr/lib/aarch64-linux-gnu

nccl的问题需要先安装nccl,然后在编译前执行下面的命令

export TF_NCCL_VERSION='1.3'

export NCCL_INSTALL_PATH=/data/nccl/build

需要说明的是,安装nccl只能用源码安装,因为安装包没有编译aarch64架构的。源码可从github上下载。

问题5:

unrecognized command line option '-mfpu=neon'

分析与解决:

修改报错时提示的那个BUILD文件,将里面包含'-mfpu=neon'的代码行都删掉。

这个问题不难找到解决方法,只是主要针对gcc而不是bazel的,所以这里还是给出针对bazel的解决方法。

问题6:

ERROR: /data/serving/tensorflow_serving/model_servers/BUILD:308:1: Linking of rule '//tensorflow_serving/model_servers:tensorflow_model_server' failed (Exit 1)

bazel-out/arm-opt/bin/external/aws/libaws.a(ClientConfiguration.o): In function `Aws::Client::ComputeUserAgentString()':

ClientConfiguration.cpp:(.text._ZN3Aws6ClientL22ComputeUserAgentStringEv+0x170): undefined reference to `Aws::OSVersionInfo::ComputeOSVersionString[abi:cxx11]()'

bazel-out/arm-opt/bin/external/aws/libaws.a(DateTimeCommon.o): In function `Aws::Utils::DateTime::ToLocalTimeString[abi:cxx11](char const*) const':

大量AWS相关的错误

分析与解决:

先给解决方法:修改_bazel_root/46688ad2577b25fcaed4521437622fa6/external/aws/BUILD.bazel,把conditions:default后面的内容换成glob([ "aws-cpp-sdk-core/source/platform/linux-shared/*.cpp", ])。

这里也给下分析的过程。

分析直接原因,是链接tensorflow_model_server用到了libaws.a,此库里确实没定义ComputeOSVersionString,期望要定义。

进一步分析,libaws.a对应的目标文件在目录_bazel_root/46688ad2577b25fcaed4521437622fa6/execroot/tf_serving/bazel-out/arm-opt/bin/external/aws/_objs/aws/external/aws/aws-cpp-sdk-core/source中,而此目录中找不到OSVersionInfo.o。而ComputeOSVersionString这个函数就是在OSVersionInfo.cpp中定义的。

再分析进一步原因,对于aws组件,aws官方提供的是cmake编译,编译只输出动态库,不会出现libaws.a。这个.a文件其实是_bazel_root/46688ad2577b25fcaed4521437622fa6/external/aws/BUILD.bazel控制生成的。分析BUILD.bazel源码发现,有对平台的判断,针对不同平台会拷贝不同目录的代码进行编译,而平台里不包含aarch64相关的,从而不会拷相应的代码。这些没拷的代码就包含了OSVersionInfo.cpp,这就是问题的根因。

问题7:

链接tensorflow_model_server、testing等目标时,出现 external symbol `__stack_chk_guard@@GLIBC_2.17' can not be used when making a shared object; recompile with -fPIC 的错误

ERROR: /data/serving/tensorflow_serving/util/net_http/socket/testing/BUILD:21:1: Linking of rule '//tensorflow_serving/util/net_http/socket/testing:ev_fetch_client' failed (Exit 1)

/usr/bin/ld: bazel-out/arm-opt/genfiles/external/com_github_libevent_libevent/libevent/lib/libevent.a(buffer.o): relocation R_AARCH64_ADR_PREL_PG_HI21 against external symbol `__stack_chk_guard@@GLIBC_2.17' can not be used when making a shared object; recompile with -fPIC

/usr/bin/ld: bazel-out/arm-opt/genfiles/external/com_github_libevent_libevent/libevent/lib/libevent.a(buffer.o)(.text+0x14): unresolvable R_AARCH64_ADR_PREL_PG_HI21 relocation against symbol `__stack_chk_guard@@GLIBC_2.17'

/usr/bin/ld: final link failed: Bad value

分析与解决:

这个问题是解决时间最长的。尝试过各种加-fPIC,还尝试fno-stack-protector等方法,发现都没用。后来发现,其实不是没用,而是没生效。因为一重新编译,之前改的Makefile之类的就又被刷回来了。

这时大致解释一下bazel编译的运作机制,在执行bazel编译后,会执行BUILD文件里定义的目标;这里我们关注的是生成libevent.a的目标,即third_party/libevent.BUILD中的libevent目标;这个目标里会执行多条shell命令,大致是先将libevent工程拷到/tmp/libevent.xxx目录,然后再在/tmp/libevent.xxx目录里编译,并将结果install到bazel-genfiles/external/com_github_libevent_libevent/libevent目录,最后删除/tmp/libevent.xxx目录;在libevent编译时,Makefile等文件是动态生成的,这也是修改Makefile等文件不生效的原因;bazel在执行每个目标(如libevent)前,会先把此目标的输出(如libevent.a)都删除,然后在执行后,再检测输出的文件是否存在(如libevent.a),如果不存在是会报错的。

所以解决方法如下

(1) 修改third_party/libevent.BUILD中的libevent目标,删除删临时文件的那一行,避免编译完后中间文件被删。然后重编译,当然,仍会失败。

(2) 进到临时文件的目录,在/tmp/libevent.*这样的目录中,修改Makefile,找到CFLAGS的定义,追加一个-fPIC选项,再make install

(3) 此时,生成的文件会输出到bazel-genfiles/external/com_github_libevent_libevent/libevent目录,通过cp -r bazel-genfiles/external/com_github_libevent_libevent/libevent /data命令将输出文件暂存一下,避免下次尝试编译时被删

(4) 然后再次修改third_party/libevent.BUILD中的libevent目标,将cmd全删掉,再加一条cp -r命令将暂存的libevent内容拷回原输出目录(注意用绝对路径),再重新编译就成功了

当然这里也可能有更简单的方法,但这里重点还是弄清楚问题原因和bazel的运作机制。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档