NNVM/TVM嵌入式初探
NNVM/TVM
NNVM/TVM 是DMLC出品的又一利器,着眼于快速产生高效的,可以部署到多种target的DL库, 提高生产效率。众所周知,目前深度学习技术开始大量实用,除了在云上,可以使用nvidia的显卡以及cuDNN框架进行加速,其他平台,尤其是嵌入式平台,深度学习的应用还是受到了计算性能的限制,不同的部署平台的优化涉及到具体的底层细节, 优化工作耗时耗力。另外,现在深度学习训练框架也是多足鼎力的状态, 每个框架都去做一套优化也有些浪费。NNVM就为解决这个问题,提供了一整套工具栈。
距离NNVM/TVM的发布,已经过去几个月的时间。在这段时间里,一些教程、benchmark脚本也陆续开放。今天我就来在树莓派上和FireFly RK3288上测试一下。
测试平台
由于Android手机的系统性能的问题(多线程环境很不稳定), 我这次选用了树莓派和FireFly RK3288作为测试平台。搭建测试平台的过程简单过一下:
烧写合适的操作系统
* 树莓派:官方系统
* FireFly RK3288 :Ubuntu 16.04 ### 切换软件源,这样后续更新、安装软件速度都会快些
* 国内比较好的源:中科大, 清华。 注意是armhf的源。
* 树莓派的默认源速度还可以。 ### 更新系统,软件等。 #### 更新软件 ``` sudo apt update && sudo apt upgrade ``` #### 安装pip, numpy ### 下载NNVM的代码,编译,配置基本环境变量 #### 下载代码 ```shell git clone --recursive https://github.com/dmlc/nnvm.git ``` 注意: --recursive, 连同TVM,以及依赖的submodule,都一起下载。 #### 编译 TVM: ``` cd nnvm/tvm make build make runtime -j2 ``` 注:我们通过rpc进行测试, 所以嵌入式端只编译runtime即可。
NNVM:
cd ..
mkdir build
make -j2
环境配置
PYTHONPATH配置
echo export PYTHONPATH=${PYTHONPATH}:${NNVM_HOME}/python:${NNVM_HOME}/tvm/python:${NNVM_HOME}/tvm/topi/python >> ~/.bashrc
source ~/.bashrc
好啦,嵌入式端测试环境完成。 启动rpc server调用测试一下。
python -m tvm.exec.rpc_server
主机端
主机端系统我用的是Ubuntu 16.04。(Windows还是不稳定, 有时候退出会卡死。)
基本环境搭建过程一致,不再赘述。 有几点不同:
- 由于需要交叉编译,需要安装LLVM。 建议采用prebuilt, 这样省去了源码编译安装LLVM的时间。
- 编译TVM的时候,整体编译。(嵌入式端只需要编译runtime)
测试
嵌入式端
- 启动rpc服务
python -m tvm.exec.rpc_server
- 查看一下嵌入端的ip。(主机端rpc连接会用到)
主机端
目前提供了mobilenet1.0和resnet18的schedule了。我们就测试这两个网络。
树莓派
先看一下测试脚本的相关参数。
python examples/benchmark/rasp_imagenet_benchmark.py --help
测试
python examples/benchmark/rasp_imagenet_benchmark.py --model mobilenet --host your_device_ip --post 9090
your_device_ip 是指设备的ip, 端口号默认是9090, 如果要改变,启动rpc server的时候修改,主机端相应修改。
不出意外的话, mobilenet1.0 在树莓派3b上的时间是200ms左右。resnet-18 速度还是很不错的。
benchmark args: Namespace(host='192.168.0.105', model='mobilenet', num_iter=20, opt_level=3, port=9090)
ProfileResult(mean=0.20684018489999997)
ProfileResult(mean=0.20953467739999998)
ProfileResult(mean=0.2077883298)
benchmark args: Namespace(host='192.168.0.105', model='resnet', num_iter=20, opt_level=3, port=9090)
ProfileResult(mean=0.54514569005)
ProfileResult(mean=0.54719750785)
ProfileResult(mean=0.549796459)
FireFly RK3288
RK3288的cpu是cortex-a17, 树莓派的是cortex-a53, 临时的方案是修改target的参数,这样可以使用rasp的schedule。
以前是默认参数: tvm.target.rasp() 修改为 tvm.target.rasp(“-mcpu=cortex-a17”)
RK3288 主频1.8GHz, mobilenet1.0 可以跑到100ms左右,resnet-18 可以跑到280ms。速度还是很赞的。
汇总下速度
Device | Mobilenet1.0 | ResNetv-18 | SqueezeNet-v1.1* |
---|---|---|---|
Raspberry 3b | ~200 ms | ~550 ms | ~120 ms |
Firefly RK3288 | ~100 ms | ~280 ms | ~77 ms |
*
: Schedule work in progress.
后续
考虑到当下移动端的框架通常用SqueezeNet作为速度测试的模型,准备为SqueezeNet写个schedule, 然后测试下速度,以便和一些开源嵌入式端优化框架(ncnn, mdl等)作比较。
踩到的坑
- 树莓派电压不足会降频! 电压不足主频直接降到600MHz。我是从淘宝上买的树莓派,电源电压不足,找到问题之后我换了更强,更稳定的电源。如果性能跟我上述的结果差别很大, 可以确认下cpu的实时主频。(不要看百分比)。
- RK3288上烧不同的系统,居然cpu_max_frequency主频不一致, 有1.6GHz的。顺便吐槽一下RK3288的社区以及配套资源,真的不够好。