vhdl编译正确但模拟运行时报错_使用 TVM 在 Jetbot(Jetson Nano) 运行 Yolov3-tiny

16c9102ee1851e30541d9b90646ed92a.png

0. 前言

  • 之前在 V100服务器上稍微尝试了一下TVM。
  • 目前好像没有什么 TVM + Jetson Nano 的资料,花了一周差不多把坑填平了,希望能给大家一点帮助吧。贴个代码,供大家参考吧。
    • server_rpc_tune.py:远程Auto-tuning
    • server_cross_compile.py:交叉编译,并导出 lib/graph/params(传到Jetbot后直接调用模型)
    • jetson_detect_video.py:Jetbot本地测试代码。
  • 想强调三点:
    • 本人C++与TVM的水平非常有限,很可能有错误。
    • 本文只是跑通了整个流程,对于TVM本身如何更好的优化模型并没有研究。
    • 得到比较好的性能,主要是大力出奇迹,猜测了解更多TVM内容后性能还有上升空间
  • 进行了两次Auto-tuning
    • v1(耗时2-3小时):n_trial=200, early_stopping=100
    • v2(耗时65小时):n_trial=8000, early_stopping=4000
  • 放个试验结果吧,可以看到,TVM Auto-tuning的结果比DarkNet的检测时间快了将近30%。

8510434b05e7c2552aeea5a0e9d992c1.png
  • 本文结构
    • Jetbot上TVM初体验:包括在Jetbot上编译TVM,尝试进行远程Auto-tuning,记录遇到的一些坑。
    • Jetbot上使用TVM运行Yolov3-tiny:通过交叉编译成功在Jetbot上运行Yolov3-tiny,并在Jetbot运行Tuned Model,记录一些坑。
    • 试验结果与自己的感想:试验结果,其他的一些坑,下一步想做的事

1. Jetbot上TVM初体验

1.1. Jetbot中TVM的编译

  • 参考资料:
    • 官方文档总是最有用的。
    • 网上资料只找到了这篇,帮助很大。
  • 编译流程:
    • 第一步:在Jetbot上下载TVM源码,安装各种依赖。具体参考官方文档。
    • 第二步:下载LLVM源码并编译。
      • 在普通服务器上,可以直接下载LLVM的预编译包。
      • 但官网能下的预编译包放到Jetbot上没用,所以要自己下载源码编译。
      • 编译流程参考这篇,编译过程花了5-6小时。
    • 第三步:按照官方文档中写的,修改 config.cmake,设置USE_CUDAUSE_LLVM
    • 第四步:执行编译,即执行cmakemake命令。
    • 第五步:添加PYTHONPATH,以及安装一些Python库。
  • 采坑一: 最开始尝试只安装 runtime (这种方法不需要编译llvm)而不是整体编译。
    • 其实我不太懂 runtime 和 完整版的区别,猜测runtime版应该是用于部署的。
    • 仅使用 runtime 版在交叉编译的时候碰到一些问题,搞不清楚原因,所以干脆就完整编译了,之后当时碰到的问题就不见了。
  • 采坑二:在运行 make 命令时报错 /usr/bin/ld: cannot find -l/usr/lib/aarch64-linux-gnu/libxml2.so
    • 这问题我都傻了,字面意思就是找不到 /usr/lib/aarch64-linux-gnu/libxml2.so 这个文件。
    • 这问题搞了仨小时:确定文件存在,添加了 LD_LIBRARY_PATH,仔细研究了 libxml2.so 的版本,搞了很久,问题还在。
    • 解决思路:
      • 运行 make VERBOSE=1看看是哪条命令除了错。出错的命令太长了,就不复制了。
      • 查看这条命令,发现-l就出现了libxml2.so这一次,而且我也确定把这文件添加到 LD_LIBRARY_PATH中,所以可以单独运行这条命令,且把 -l/usr/lib/aarch64-linux-gnu/libxml2.so 这个删除就行了。
      • 还好make命令执行失败后,再次执行make时会从上次失败的地方开始。
      • 所以手动执行出错的这条命令(去掉-l相关内容),然后再执行make,最终终于编译成功了。
  • 采坑三:利用 pip3 install 时失败。
    • 安装scipy失败,参考这里。
    • 使用 sudo apt-get install python3-scipy 这样成功了。
    • Jetbot上安装一些Python库很奇怪,经常要用到 apt-get 这样来安装。
  • 复制粘贴一下用到的命令
# 下载源码
git clone --recursive https://github.com/apache/incubator-tvm tvm

# 安装依赖
sudo apt-get update
sudo apt-get install -y python3 python3-dev python3-setuptools gcc libtinfo-dev zlib1g-dev build-essential cmake libedit-dev libxml2-dev

# LLVM编译命令
git clone https://github.com/llvm/llvm-project llvm-project
cd llvm-project
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS=lld -DCMAKE_INSTALL_PREFIX=/usr/local ../llvm
make -j3 && make install

1.2. TVM 远程 Auto-tune

  • 目标:利用官方样例,测试下Jetbot上的TVM是否安装成功了。
  • 远程 Auto-tuned 结构在 这篇博客 中已经有说明,推荐大家看看。
  • 简单说下自己对Auto-tune的理解:
    • 存在一个中心点(即 rpc_tracker)专门用于记录所有需要Tune的设备。
    • 需要Tune的设备(即 rpc_server)都需要主动连接到中心点上。
      • 注意,如果中心点本身就有设备,则中心点也需要在执行一条命令连接自己。
      • 每个设备都有一个自己独立的名称。
    • Auto-tuning 程序连接中心点,通过设备名称来选择对应的设备。
    • Auto-tuning需要大量CPU资源,性能瓶颈可能在CPU上。(我的试验,性能瓶颈在公司网络是否稳定上……)
  • 思路:
    • 现有设备:一台服务器和一台Jetbot。
    • 在服务器上运行中心点,Jetbot主动连接服务器,运行Server。
    • 利用服务器CPU资源执行Auto-tuning。
  • 实现过程:
    • 在服务器上建立中心点,即 python -m tvm.exec.rpc_tracker --host=0.0.0.0 --port=9190
    • 将Jetbot连接到服务器,即 python3 -m tvm.exec.rpc_server --tracker=10.0.10.56:9190 --key=jetbot
    • 查看中心点连接的设备:python -m tvm.exec.query_rpc_tracker --host=0.0.0.0 --port=9190
    • 在服务器上运行 Auto-tuning 程序,即这个官方样例。
  • 采坑一重要!重要!重要!
    • 在Jetbot上运行,需要设置 CUDA ARCH,否则会报错
    • 如何知道是 sm_53 :在 Nvidia 算力查看页面,选择对应的算力即可。
      • 这个是我猜的,最后发现猜对了。
      • 查资料的时候看到有人在Jetson TX2中设置 CUDA ARCH 为 sm_62,对照上面的链接,那Nano应该就是 sm_53
    • 在官方样例的开头添加下面的内容
from tvm.autotvm.measure.measure_methods import set_cuda_target_arch
set_cuda_target_arch('sm_53')
  • 采坑二:
    • 官方样例中的Auto-tuning的参数如下。
    • autotvm.LocalBuilder 指定需要使用谁的CPU资源进行 Auto-tuning。
    • autotvm.RPCRunner 指定需要对谁的算力进行 Auto-tuning。
    • 由于网络连接不稳定,所以需要加大 timeout 的数值,否则会报错
    • 公司迷之网络,难受……
tuning_option = {
    'log_filename': log_file,

    'tuner': 'xgb',
    'n_trial': 2000,
    'early_stopping': 600,

    'measure_option': autotvm.measure_option(
        builder=autotvm.LocalBuilder(timeout=1000),
        runner=autotvm.RPCRunner(
            '1080ti',  # change the device key to your key
            '0.0.0.0', 9190,
            number=20, repeat=3, timeout=1000, min_repeat_ms=150),
    ),
}
  • 采坑三:
    • 中间一直Auto-tuning失败,可以看到,连接是成功的,但是会有各种看不懂的 error_no=1error_no=4 的错误。
    • 社区某帖子提到,如果服务器的CUDA版本和Jetbot的CUDA版本不一致,可能会报错。
    • 还有要注意 采坑一 中提到的CUDA ARCH。
    • 最终处理流程是,先在Jetbot上执行本地Auto-tune,解决运行过程中的一些小问题,然后再次远程Auto-tune就成功了……
    • 然后 在Jetson上执行本地Auto-tune是个大坑,不推荐这么做,建议进行交叉编译,后面会提到。

2. Jetbot上使用TVM运行Yolov3-tiny

2.1. :在Jetbot上直接建Relay计算图

  • 首先给出结论:
    • 在Jetbot上直接建Relay计算图是个大坑,非常不推荐这么做!
    • 推荐使用交叉编译!
    • 前面提到过,为了顺利执行远程Auto-tune,我先在Jetson上执行本地Auto-tune,所以为了执行Yolov3-tiny,我也这么做了……太惨了……
  • Jetbot执行官方darknet样例失败的经历
    • 在前一篇文章中,我直接运行官方样例在V100服务器上成功运行了DarkNet,所以在Jetbot上也是信心满满……
    • 该样例通过一个 libdarknet2.0.so 构建计算图,然而该文件在 Jetbot 上并不能用,Jetbot毕竟是arm架构。
    • 尝试通过 darknet 源码构建的 libdarknet.so 文件替代,但失败了。
    • 想尝试解决一下,发现是各种C语言问题,看不懂,不想深入。
  • Jetbot直接转换TensorFlow模型失败的经历
    • 目标:将TensorFlow的PB模型转换为TVM计算图。在V100上其实我已经成功实现了,所以也拿到Jetbot上试试。
    • 结论:在Jetbot上转换TensorFlow模型完全行不通,原因是:
      • TVM支持的最高版本Tensorflow是 1.12.0,而Jetson Nano支持的最低版本是 tensorflow 1.13.1
      • 尝试之后会报 TypeError: resize() got an unexpected keyword argument 'half_pixel_centers' 的错误。
      • 在社区各种帖子中了解到,这个问题就是因为tensorflow版本太高。
    • 在使用tensorflow pb转TVM之前,需要先安装 antlr4,否则会报错。
      • 社区某帖子中提到,需要在运行前安装 sudo pip3 install antlr4-python3-runtime

2.2. TVM-DarkNet 交叉编译

  • 资料:
    • 我的源码
    • 交叉编译官方Tutorials:这个sample我运行的时候存在一点问题,CPU环境下没问题,GPU环境(V100和Jetson Nano)下不行。
      • 这个样例也说清楚了TVM如何进行交叉编译,想了解相关内容的话建议大家仔细看
      • 下面内容最多是对官方样例的一点注释。
  • 实现的功能:
    • 在服务器上编译模型,并直接部署到Jetbot上。
    • 各种构件Relay的过程都是在服务器中进行,与Jetbot无关(2.1.中的所有问题都不存在),美滋滋。
  • 交叉编译代码流程(注意,在服务器上/在Jetbot上)
    • 准备:在Jetbot上运行本地Server,即 python3 -m tvm.exec.rpc_server --port 9090 --host 0.0.0.0
    • 第一步:服务器任务,构建Relay计算图,即获取 mod, params 参数。
    • 第二步:服务器任务,编译模型,即执行 relay.build 命令,特别要注意 target_host 参数,后面会提到。
    • 第三步:服务器任务,通过 lib.export_library 导出lib文件。
      • 编译好的模型,主要包括三个参数 lib/graph/params
      • 其中,与运行平台相关的只有 lib,另外两个在哪里都一样。
    • 第四步:服务器任务,将导出的lib文件上传到Jetbot中。
    • 第五步:Jetbot任务,通过 remote.load_module(lib_name) 命令导入刚刚传入的lib文件。
    • 第六步:Jetbot任务,通过 graph/lib 以及Jetbot中的context信息,创建 runtime module,并导入 params
    • 第七步:Jetbot任务,通过创建好的runtime module运行程序。
  • 采坑一:需要重点关注 target_host 参数
    • 如果该参数设置有误,会报错 error adding symbols: File in wrong format
    • 对于Jetbot来说,该参数的正确取值是 llvm -target=aarch64-linux-gnu
    • 如何查询该数值:在Jetbot上运行 gcc -v,查找 target 开头的那行内容。
    • 交叉编译时,target_host 指的是Jetbot的环境。
  • 采坑二,别忘记设置 CUDA ARCH
    • 前文中提到的 set_cuda_target_arch('sm_53') 不能忘,这里就不细说了。

2.3. Jetbot本地运行DarkNet

  • 目标:将Auto-tuned完成的模型部署到Jetbot上,并测试性能。
  • 资料:官方文档-模型导入导出和我的完整代码
  • 基本流程流程:
    • 第一步:在服务器上导出TVM-DarkNet的 lib/graph/params
      • 有一点不同,就是要导入Auto-tuned结束的模型。
      • 做法就是用 with autotvm.apply_history_best(log_file) 修饰 relay.build,然后再保存lib/graph/params
    • 第二步:在Jetbot上导入第一步生成的 lib/graph/params
    • 第三步:之后就是普通TVM程序运行了,就不说了。
  • 采坑(技术无关,没兴趣的掉过):
    • 聪明如我,当然能从官方文档中找到怎么保存与导入模型啦。
    • 聪明如我,当然还能从官方文档中找到怎么使用Auto-tuned完的模型啦。
    • 那要怎么在Jetbot上导入已经Auto-tuned完的模型呢?太简单了,把上面两个例子结合一下不就行了
    • 结局:检测一张图片耗时20s,我的测试代码完美无缺,肯定是TVM Auto-tuning出了问题
    • 当时写的代码
with autotvm.apply_history_best(log_file):
    loaded_json = open(graph_path).read()
    loaded_lib = tvm.module.load(path_lib)
    loaded_params = bytearray(open(params_path, "rb").read())

3. 试验结果与感想

3.1. 试验结果

  • 与我前一篇文章《TVM 初体验 - DarkNet 性能测试》类似,测试内容主要是:
    • 测试的是Yolov3-tiny,全部通过Python实现。
    • 通过cv2读取视频同一段视频,并利用cv2, np进行数据预处理。
    • 通过 DarkNet 的Python接口以及 TVM-DarkNet 的Python接口进行检测。
  • DarkNet性能比较结果
    • DarkNet中的nms已经集成在模型内部,而TVM-DarkNet的nms需要通过np实现。
    • DarkNet(Jetbot):在Jetbot编译DarkNet源码,执行这个Python脚本进行测试。
    • Un-tuned TVM-DarkNet:尚未进行Auto-tuning的TVM-DarkNet模型。
    • Tuned(v1) TVM-DarkNet(cross-compilation):经过Auto-tuning的TVM-DarkNet模型,在服务器通过交叉编译测试。
    • Tuned(v1) TVM-DarkNet(jetbot):经过Auto-tuning的TVM-DarkNet模型,在Jetbot本地测试。
    • Tuned(v2) TVM-DarkNet(jetbot):经过Auto-tuning的TVM-DarkNet模型,在Jetbot本地测试。

8510434b05e7c2552aeea5a0e9d992c1.png
  • 比较结果的一些自己的理解:
    • 大力出奇迹
    • 交叉编译时,视频读取以及数据预处理都是服务器CPU实现,所以明显快很多。
    • 在Jetbot上运行DarkNet和TVM-DarkNet时:
      • 图片预处理的工作其实不完全一样,所以有一定差距稍微可以理解。
      • 视频读取时间差距有点大,不知道为啥。
      • 检测的性能基本上在同一数量级了,如果有更长时间进行Auto-tune,很可能能获取更好的结果。
    • 交叉编译的检测时间这么大,应该是网络传输的关系吧。

3.2. 感想

  • 关于相关资料获取:TVM资料只能在社区里找,TVM+Nano的就更少了,可以看看TX2的(与Nano类似)。
  • 现在全部做完了回过头看,感觉自己主要就是采坑了,也做啥实事。
  • 其他的一些坑
    • 公司网络问题,经常连接失败。
    • Jetbot要注意功率,否则很容易电流过大自动关机,可以通过这里,执行 sudo nvpmodel -m1 选择最高功率5W(我猜是这个意思)。
    • 就算设置了5W,电源线不给力,也经常自动关机……换了两根线,终于OK了。
  • 后续想要尝试的工作:
    • 利用C++运行DarkNet
    • 利用C++部署TVM-Yolov3-tiny
    • 将NMS添加到TVM计算图中。
      • tensorflow提供了NMS op,但TVM中并不支持tensorflow的这个OP。
      • TVM官方DarkNet实例中,TVM计算图没有包含NMS,后续是通过numpy实现的。
      • 发现其实存在API tvm.relay.op.vision.nms.non_max_suppression
      • 后续想把样例利用C++跑一次,很显然,利用C++实现NMS对于我来说可能有点费劲。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值