yolov4 init8量化下效果图:
(PS.原始视频取自B站,侵删。)
性能展示(FPS):
平台 | 网络 | FP32,B=1 | FP32,B=4 | FP16,B=1 | FP16,B=4 | INT8,B=1 | INT8,B=4 |
---|---|---|---|---|---|---|---|
Xavier AGX | yolov4 | 19.6 | 21 | 39.8 | 47.4 | 48.5 | 58.8 |
什么是tkDNN?
tkDNN是一个由cuDNN和TensorRT原语构建的深度神经网络库,专门用于NVIDIA Jetson开发板上。它已经在TK1(cudnn2分支),TX1,TX2,AGX Xavier,Nano和多个类型的GPU上进行了测试。该项目的主要目标是尽可能多地利用NVIDIA开发板,以获得最佳的推理性能。
所部署的Xavier环境
nvcc -V # 查看cudn版本
CUDA10.0.326
cat /usr/include/cudnn.h | grep CUDNN_MAJOR -A 2 # 查看Xavier上cudnn版本
cuDNN7.6.3
OpenCV3
yaml-cpp 0.5.2 (sudo apt install libyaml-cpp-dev)
如果需要安装opencv4 with contrib使用脚本下载编译安装
bash scripts/install_OpenCV4.sh
注意:当使用未与contrib联编的OpenCV时,需要在include/tkDNN/DetectionNN.h中注释掉OPENCV_CUDACONTRIBCONTRIB宏。注释时,网络的预处理将在CPU上运行,否则在GPU上运行(GPU上运行时这样端到端的延迟将节省几毫秒)
如何编译?
使用cmake >= 3.15编译
(如果不用cmake3.15版本或以上的在cmake时会报错cmake:
CUDA_cublas_device_LIBRARY variable set to NOTFOUND #181:
git clone https://github.com/ceccocats/tkDNN.git
cd tkDNN
sudo apt install libyaml-cpp-dev
mkdir build && cd build
cmake ..
make
运行流程(Steps)
- 使用所选的框架构建并训练模型
- 导出神经网络每一层权重和偏置并将它们保存至一个二进制文件(一个文件对应一层)
- 导出网络每一层的输出并保存至二进制文件(一个文件对应一层)
- 创建一个新测试,并使用图区的权重和输出来逐层定义网络并检查结果
- 开始推理
如何导出权重?
导出yolov4权重(darknet):
git clone https://git.hipert.unimore.it/fgatti/darknet.git
cd darknet
# Makefile里不需要设置,全为0直接编译就可以了
make
mkdir layers debug
./darknet export <path-to-cfg-file> <path-to-weights> layers
# 没有yolo4文件需要自己mkdir一下
# mkdir ${tkDNN_ROOT}/yolo4
mv layers ${tkDNN_ROOT}/build/yolo4
mv debug ${tkDNN_ROOT}/build/yolo4
在tkDNN/build文件夹中,对于每个测试,都需要按以下方式构建文件结构
yolo4
|---- layers/ (导出的每层的权重和偏置二进制文件)
|---- debug/ (导出的每层的输出的二进制文件)
Darknet解析器
tkDNN工具和darknet的cfg文件的简单解析器,可以使用
tk::dnn::darknetParser
example:
tk::dnn::Network *net = tk::dnn::darknetParser("yolov4.cfg", "yolov4/layers", "coco.names");
net->print();
支持的网络层:
卷积层,最大池化层,平均池化层,shrtcut,上采样层,route,reorg,region,yolo
支持的激活层:
relu层,leaky层,mish层
模型转换(生成RT文件)
export TKDNN_BATCHSIZE=4 # 使用最大可得batch_size.test将仍以batch_size=1进行,但是创建的TRT可以管理所需的批次大小
# 即demo的参数number-of-batches <=4, 默认为1
# build tensorRT files
# export TKDNN_MODE=FP16 # 使用fp16进行推理,默认为fp32
cd ${tkDNN_ROOT}/build
rm yolo4_fp32.rt # 先删除掉旧的trt文件,若没有可省去此行
./test_yolo4 # 运行生成trt文件,过程比较慢
最后输出以下内容表示转换成功,并且当前目录下生成yolo4_fp32.rt文件(fp指定多少会生成对应的模型,默认fp32)
=== OUTPUT 2 CHECK RESULTS ==
CUDNN vs correct
| [ 0 ]: 0.883185 0.662701
| [ 1 ]: 0.537623 0.447494
| [ 2 ]: 0.419132 0.250892
| [ 3 ]: 0.512993 0.84352
| [ 4 ]: 0.394617 0.248717
| [ 5 ]: 0.543686 0.205057
| [ 6 ]: 0.56079 0.859086
| [ 7 ]: 0.362244 0.754671
| [ 8 ]: 0.568176 0.775207
| Wrongs: 9748 ~0.02
TRT vs correct
| [ 0 ]: 0.883186 0.662701
| [ 1 ]: 0.537623 0.447494
| [ 2 ]: 0.419132 0.250892
| [ 3 ]: 0.512994 0.84352
| [ 4 ]: 0.394617 0.248717
| [ 5 ]: 0.543685 0.205057
| [ 6 ]: 0.560791 0.859086
| [ 7 ]: 0.362245 0.754671
| [ 8 ]: 0.568176 0.775207
| Wrongs: 9748 ~0.02
CUDNN vs TRT | OK ~0.02
注:使用int8量化需要设置更多的配置:
export TKDNN_MODE=INIT8
# 图片绝对路径组成的txt文件,每行代表一个图片,用于校准模型,使用训练集
export TKDNN_CALIB_IMG_PATH=/path/to/calibration/imge_list.txt
# 标签文件绝对路径组成的txt文件,每行表示一个文件
export TKDNN_CALIB_LABEL_PATH=/path/to/calibration/label_list.txt
# 使用此脚本下载数据集并创建上述所需的txt文件
bash scripts/download_validation.sh COCO
- 使用INT8和FP16推理过程中可能会有一些错误。
- ./test生成rt文件时速度会更慢,主要是在校准图像时非常耗时
- INT8要求TRT>=6.0
- 默认使用100张图像作为校准,可在代码中设置
注意:如果上述过程中遇到错误,则在编译tkDNN时使用debug模式,以便于打印错误信息。
cmake .. -DDEBUG=True
make
运行Demo
当成功创建trt文件时,即可运行demo:
./demo yolo4_fp32.rt xxx.mp4 y
./demo有以下7个参数(按序):
- network-rt-file:通过./test_yolo4生成的rt文件 (必要)
- path-to-video:待检测的视频文件或者摄像头 (必要)
- kind-of-network:网络结构类型。当前支持:y (yolo系列)、c(CenterNet系列)、m(MobileNet-SSD系列) (必要)
- number-of-classes:网络训练时的类别数
- n-batches:推理时用的batch_size(在导出模型时需要指定batch_size)
- show-flag:如果设置为0则不可视化显示推理效果,而是保存为mp4格式的视频(此时要求n-batches为1)
- conf-thresh:置信度阈值
测试模型的mAP
./map_demo <network rt> <net type [y|c|m]> <labels file path> <config file path>
# example
./map_demo dla32_cnet_FP32.rt c ../demo/COCO_val2017/all_label.txt ../demo/config.yaml
修改部分
demo.cpp
116 //inference
117 std::ofstream ofs ("result.csv");
118 detNN->update(batch_dnn_input, n_batch, true, &ofs);
将save_times 设置为true,并指定一个输出流将平均每帧预处理时间(preprocess)、推理时间(inference)、后处理时间(postprocess)记录下来,上面代码表示写入到当前目录的result.csv文件中。