YOLOv5转NCNN
新增yolov5模型剪枝和NCNN INT8量化。
项目持续更新,主要包括yolov5s模型的剪枝,int8量化,ncnn模型转换和Android移植测试。
基于YOLOv5最新v5.0 release,和NCNN官方给出example的差别主要有:
- 激活函数hardswish变为siLu;
- 流程和详细记录u版YOLOv5目标检测ncnn实现略微不同
动态库用的是官方编译好的ncnn-20210507-ubuntu-1604-shared
mkdir build
cd build
cmake ..
make -j8
./yolov5 ../bus.jpg
可以看到:
参考https://github.com/nihui/ncnn-android-yolov5 ,使用这里转的v5.0分支的ncnn模型。
见https://github.com/midasklr/yolov5prune 稀疏训练+Bn层剪枝,可以获得更加紧凑的模型,这里一次稀疏训练+60%Bn层剪枝,模型从28M降低到7M。
以下为yolov5s.pt转NCNN流程,自己训练的模型一样:
先测试下yolov5s效果:
python detect.py --weights yolov5s.pt --source data/images
效果不错:
导出 onnx,并用 onnx-simplifer 简化模型,这里稍微不同,如果按照详细记录u版YOLOv5目标检测ncnn实现,那么直接导出来的模型可以看到输出:
python models/export.py --weights yolov5s.pt --img 640 --batch 1
可以看到后处理怎么都出来了???
看看models/yolo.py代码发现:
inference里面不就对应上面onnx模型那部分输出处理后然后torch.cat起来么,这部分处理我们放在代码里面做,所以可以注释这部分:
这样导出来的模型就是三个输出了:
ok,输出和详细记录u版YOLOv5目标检测ncnn实现对应上了,同时可以看到激活函数silu:
经过onnx-sim简化一下:
python -m onnxsim yolov5s.onnx yolov5s-sim.onnx
后续和详细记录u版YOLOv5目标检测ncnn实现一样,ncnn转化后激活函数转为swish,可swish的实现:
Swish::Swish()
{
one_blob_only = true;
support_inplace = true;
}
int Swish::forward_inplace(Mat& bottom_top_blob, const Option& opt) const
{
int w = bottom_top_blob.w;
int h = bottom_top_blob.h;
int channels = bottom_top_blob.c;
int size = w * h;
#pragma omp parallel for num_threads(opt.num_threads)
for (int q = 0; q < channels; q++)
{
float* ptr = bottom_top_blob.channel(q);
for (int i = 0; i < size; i++)
{
float x = ptr[i];
ptr[i] = static_cast<float>(x / (1.f + expf(-x)));
}
}
return 0;
}
} // namespace ncnn
和silu一样,那么就可以正常进行推理了,可能需要注意的就是三个输出节点不要弄错就ok。
- 最新ncnn新增支持int8量化
git clone https://github.com/Tencent/ncnn.git
cd ncnn
git submodule update --init
-
增加Focus层
将layer/yolov5focus.cpp/.h 两个文件放在ncnn/src/layer下,然后ncnn/src/CMakeList.txt增加:
ncnn_add_layer(YoloV5Focus)
-
编译
不同系统的编译见https://github.com/Tencent/ncnn/wiki/how-to-build
编译完成后会有:
ncnn/build/tools/onnx/onnx2ncnn :用于onnx转ncnn的工具
/ncnn/build/tools/ncnnoptimize 优化工具
ncnn/build/tools/quantize/ncnn2int8 转int8
ncnn/build/tools/quantize/ncnn2table 生成校准表
参考https://github.com/Tencent/ncnn/wiki/quantized-int8-inference 操作,在ncnn2table工具下,准备我们的检验图片放在images文件夹下,最好是我们训练模型的验证或者测试集,这里使用coco val数据集5k张图片。
然后
find images/ -type f > imagelist.txt
./ncnn2table yolov5s-opt.param yolov5s-opt.bin imagelist.txt yolov5s.table mean=[0,0,0] norm=[0.0039215,0.0039215,0.0039215] shape=[416,416,3] pixel=BGR thread=8 method=kl
然后转化模型:
./ncnn2int8 yolov5s-opt.param yolov5s-opt.bn yolov5s-int8.param yolov5s-int8.bin yolov5s.table
转化后的int8模型见yolov5s-int8.param和yolov5s-int8.bin。
使用最新的动态库:https://github.com/Tencent/ncnn/releases/tag/20210525
根据你的系统选择。
相关设置:
yolov5.opt.num_threads = 8;
yolov5.opt.use_int8_inference = true;
FP16模型和INT8模型对比:
kl和aciq两种量化方式,前者损失会更大。
input | inference time | model size | |
---|---|---|---|
yolov5s | 416 | 22 ms | 14.6 M(fp16) |
yolov5s-prune | 416 | 18 ms | 1.7 M(fp16) |
yolov5s-int8 | 416 | 52ms | 887.5k(int8) |
目前int8量化后效果还不错,但是推理时间慢了很多,可能原因是int8不针对x86,参考Tencent/ncnn#2974 后续测试arm上加速效果