PPLcnet 和 YOLO 的碰撞,真的能在 cpu 上快到起飞?
【GiantPandaCV 导语】这是一篇论证博客,前几天,baidu 发布了 PPLcnet,这是一款专门为 cpu 设计的网络,在看完论文后,果断进行了 PPLcnet-yolo 的复现,一来是想验证下这个网络在 cpu 上的性能,二来如果验证效果 work,这套实验可以合并到自己的仓库。
一、PPLcnet 性能:¶
在看论文时,对我诱惑最大的是下面这张 benckmark 的比较。
其实在之前,有尝试过使用 mobilenetv2、mobilenetv3 进行实验,但是效果并没有让我感到理想,原因也简单,在 arm 架构上,mb 系列被 shuffle 系列一骑绝尘,这种优势并不是体现在精度上,事实上,它们的精度两两比较不会超过 3 个百分点。但是我认为侧端落地,速度和内存占用才是最关键的两个因素(前提是精度在可接受范围内),因此毫不犹豫使用 shufflenetv2 来做主干。
当然,并不能拿来主义,需要分析一些利弊后做取舍,比如对于 yolov5s 的 head,如果直接嫁接,会造成部分通道冗余,这不仅仅体现在模型参数上,在多个方面都可以看到。
使用模型剪枝的方式去逼近 channel 最大的承载量,开展实验去验证效果,这种也算是一种半暴力解法,可以节省很多无效时间。
另一方面,shufflenetv2-yolov5 模型的两个 branch 分支使用了大量了 bn 层,在部署时进行 fuse,速度可以再提升 15%(这个代码会在学位论文答辩后合并上去)。
在 GPU 架构上,Repvgg-yolov5 也是如此,头变得更厚更窄,主要还是为了缩小参数和产生的计算量(C3 结构的功劳),主干换成了 repvgg,在训练时采取多分支特征提取,部署时又重参化成直筒网络,可以加速 20%。参数和计算量分别减少了 35% 和 10%,在精度上,Repvgg-yolov5 的 map@0.5 提升了 1.1,map@.5:0.95 提升了 2.2,但代价是向前推理比原先的 yolov5s 要多耗费 1ms(测试显卡为 2080Ti)。
综上,大家请叫我为调参调包外加 debug 狂魔,毫无创新点,但都是对于工业部署很实用的模型。
在 cpu 架构上,之前以及做过 mbv2、mbv3 的实验,精度其实和 shufflev2 相差不大,但结果相对于 yolov5s,input size=352*352,yolov5s 的精度还略高于魔改后的模型,在速度上也并没有很大的优势。
再后来 PPLcnet 出现,有着很强烈的欲望想试一下这个网络是否能帮助 yolo 在 cpu 上加速。
模型的结构大致如下:
最主要的组成部分时深度可分离卷积,从第一层的 CBH 开始(conv+bn+hardswish),中间包含了 13 层 dw,而后面的 GAP 是指 7*7 的 Global Average Pooling,GAP 后面再加 point conv+FC+hardswish 组件,最后是输出为 1000 的 FC 层,想要了解更详细的可以查看论文:
https://arxiv.org/pdf/2109.15099.pdf
整篇论文可以归纳关于 PPLcnet 的四个重要结论:
- H-Swish 与大卷积核可以提升模型性能且不会造成较大的推理损耗(下看 Table9);
- 在网络的下层添加少量的 SE 模块可以更进一步提升模型性能且不会产生过多的损耗(实际上 Lcnet 仅仅这是在最后两层添加注意力,但是提升效果明显);
- GAP 后采用更大 FC 层可以极大提升模型性能(但也会让模型参数和计算量暴涨);
- dropout 技术可以进一步提升了模型的精度
二、PPLcnet-yolo:¶
下图是融合了 PPLcnet 的 YOLOv5,与原先的 Lcnet 不同的是,此处的层数有所改变,不仅如此,YOLOv5s head 中的 3*3 卷积也替换成了 Lc_Block,并且使用了 SE module,我们进行逐层分析:
1. 层数改变
如上图,CBH 通道数翻倍,抽掉了两个 channel 为 256 的 DSC 33 卷积层,替换成两个 DSC 55 层(无 SE Module),并且最后的四个 DSC 层都含有 SE 模块,总的层数仅增加了 3 层,SE Module 由原来的 2 层变成了 4 层(后 4 层),但是精度提升巨大,这点借鉴 shufflev2 的【2,4,8,4】偶数倍层数,有兴趣可以看下这篇论文,很有工程意义。
2. Dense Layer
Dense Layer 本质还是 GAP+FC,实验发现,添加 FC 层精度能提升 4 个点左右,但会导致模型参数的暴涨,影响推理速度,故剔除掉了所有的 FC 层,仅留下 point conv 和 dropout:
class Dense(nn.Module):
def __init__(self, c1, c2, filter_size, dropout_prob=0.2):
super().__init__()
self.avg_pool = nn.AdaptiveAvgPool2d(1)
self.dense_conv = nn.Conv2d(
in_channels=c1,
out_channels=c2,
kernel_size=filter_size,
stride=1,
padding=0,
bias=False)
self.hardswish = nn.Hardswish()
self.dropout = nn.Dropout(p=dropout_prob)
self.flatten = nn.Flatten(start_dim=1, end_dim=-1)
self.fc = nn.Linear(num_filters, num_filters)
def forward(self, x):
x = self.avg_pool(x)
b, _, w, h = x.shape
x = self.dense_conv(x)
x = self.hardswish(x)
x = self.dropout(x)
x = self.flatten(x)
x = self.fc(x)
x = x.reshape(b, self.c2, w, h)
return x
3. head PPLcnet 已经验证了在末端替换少量 55 卷积可以起到涨点的作用,因此也将原 yolov5s head 的 33 卷积换成 Lc Block,但因为 Lc Block 本质还是深度可分离卷积,即使使用了 55 的卷积核,融合了 SE module,参数量依旧比原先的 33 卷积少一半,实验发现可以涨点,产生的参数量也很少,个人觉得性价比特别高
# YOLOv5s head:
Model Summary: 297 layers, 4982390 parameters, 4982390 gradients, 9.4 GFLOPS
# YOLOv5s head with Lc_Block:
Model Summary: 307 layers, 4376531 parameters, 4376531 gradients, 8.6 GFLOPS
# YOLOv5s head with Lc_Block and SE Module:
Model Summary: 319 layers, 4378838 parameters, 4378838 gradients, 8.6 GFLOPS
还要一些小组件的改动,比如 SE module 的 Hard sigmoid 替换成 Silu,能涨点还能提速(这点跟着 v5 大神走),另外一个是避免 onnx 没有 h-sigmoid 这个算子,需要重构算子(这个重构会造成精度些许下降,所以替换激活函数是最省心的工作)。
4. 性能
模型复现后性能如下:
在 map@0.5 和 map@0.5:0.95 上都比原 yolov5s 少三个点左右,参数量和计算量少了一倍左右。
然而,上面的都不是重点,我认为最重要的还是性能,于是使用 PPLcnet 和 yolov5s 在 openvino 进行评测,测试硬件为 Inter Core @i5-10210。
首先提取 onnx 模型:
$ python models/export.py --weights PPLcnet.pt --img 640 --batch 1
$ python -m onnxsim PPLcnet.onnx PPLcnet-sim.onnx
接着将 PPLcnet-sim.onnx 转化为 IR 模型:
$ python mo.py --input_model PPLcnet-yolo.onnx -s 255 --data_type FP16 --reverse_input_channels --output Conv_462,Conv_478,Conv_494
同理,yolov5s 也是一样
$ python models/export.py --weights yolov5s.pt --img 640 --batch 1
$ python -m onnxsim yolov5s.onnx yolov5s-sim.onnx
$ python mo.py --input_model PPLcnet-yolo.onnx -s 255 --data_type FP16 --reverse_input_channels --output Conv_245,Conv_261,Conv_277
此时,我们可以得到四个模型:
模型对比:
而后进行测试,总 50 张图片,For 循环进行 1000 次向前推理,计算每张图片平均耗时:
通过测试可以看到,input size=640*640,PPLcnet 的一次先前推理比原 yolov5s 快 3 倍左右, 部分样例视图.
PPLcnet-yolo Forward Example:
YOLOv5s Forward Example:
留言:
后续会将复现的实验及代码合并到主分支:
https://github.com/ppogg/YOLOv5-Lite
欢迎大家白嫖,有问题可以提 issue,会尽快解决。
另外,这个是为 cpu 设计的模型,请使用 openvino 或者其他 cpu 向前推理框架进行部署和评测!!!
YOLOv5 6.0 版本来了
重头戏来了,昨天看到 YOLOv5 发布了第六版:
模型性能有所改观:
依旧是没有创新点,但是工程价值有突破,体现在计算资源和推理耗时方面。
另外,我觉得最主要的亮点有三个,YOLOv5-Nano 对移动设备的适配,Focus 层的变化,SPP 的改动:
1. YOLOv5-Nano 的性能:
之前在侧端设备上测试了带有 focus 层的量化版 yolov5s 模型,发现这玩意很容易崩,对于小模型的话,v5 大神是直接替换掉,可能是出于稳定性的考虑,毕竟 conv3*3 卷积在不同框架上的优化已经非常成熟了,对于大部分小模型,本身模型的参数和运行时产生的计算量并不多,使用 focus 也很难起到降参降计算量的作用,量化时还能更稳定一些。
2. Focus 层的改变:
3. SPP→SPPF:
By the way!!!v4 大牛,v5 大神,还要 Scale Yolov4 的作者,三人在社区上被称为 commit 狂魔,有段时间天天看到他们几个在 update,这种匠人精神着实令人佩服。。
本文总阅读量283次