本站内容均来自兴趣收集,如不慎侵害的您的相关权益,请留言告知,我们将尽快删除.谢谢.
近年来YOLO系列算法遍地开花,前阵子YOLOv6和YOLOv7相继被提出,一开源就有了极高的流量热度。还在用v5呢结果v6出来了,赶紧换v6训一波,结果v7又出来了不得不再换v7训和测,相信不少人和我一样,虽然结构差不多,但切换起来还是繁琐,而且自己的数据集精度用哪个训高还真不一定。当时就想把这3个代码整合在一个库的想法,但是这三个代码基本就是yolov5代码,当年debug yolov5代码时候吃了不少苦头,二次开发也非常的不友好。
前两天看到了百度飞桨公众号上的宣传,推出了一个重构后的YOLO库 YOLOSeries,说同时支持PP-YOLOE、YOLOX、YOLOv5、YOLOv6、YOLOv7,于是立马去使用了下。代码链接是这个:
这个库全称是 PaddleDetection_YOLOSeries,顾名思义就是基于百度飞桨的PaddleDetection 复现的,这个库看起来也是百度飞桨派人维护更新的。yoloseries代码其实就是PaddleDetection上加了v5 v6 v7模型,但是受限于v5 v6 v7的GPL协议而单独另开一个维护,yoloseries作者也说会同步和PaddleDetectio保持更新。原版的PaddleDetection里就有YOLOv3、YOLOv4、PP-YOLOE和YOLOX,代码链接是这个:
言归正传,yoloseries库对yolov5、yolov6、yolov7的重构是我比较关心的,更不用说还有百度他们的yoloe (torch的yoloe好像没看到有复现的) 和 convnext更高精度版本,看着首页的一排排表格和下载链接就兴奋。
1.各个YOLO基本结构
首先还是简单回顾下几个热门YOLO的大致结构:从2020年开始
1.YOLOv5(2020):
YOLOv5主要提出更灵活和更轻量级的网络设计,还有Mosaic数据增强。模型精度最初不如YOLOv4,但是速度快的多,后来精度速度一直在迭代优化,逐渐成为业界顶流。关于取名之类的就不纠结了,作者几乎全年甚至全天无休地维护也使得YOLOv5始终保持着极高的热度,但是代码可读性比较差。
2.YOLOX(2021.08):
YOLOX主要特点是提出了Anchor Free的YOLO检测思路,使用SimOTA改进label assign,和Decoupled Head解耦头。还有一些trick包括采用MixUp和Mosaic,最后15个epoch关闭Mosaic使用L1 loss等。
值得一提的是,PaddleDetection中的YOLOX我看了下,是我见过复现精度最高的版本,比旷视的原版也更高,其中大模型更高的多,YOLOX-x直接51.8。
3.YOLOE(2022.03):
YOLOE是PaddleDetection团队自己研发出来的,backbone采用了新设计的CSP-RepVGG形式的变种ResNet,这个确实挺惊艳的,head也是解耦头还有加了个ESE Attention,也是Anchor Free的,标签分配采用了早期ATSS后期TAL(Task Alignment Learning)联合的做法。PP-YOLOv2和PP-YOLO是百度之前的作品,一段时间出一个PP特色模型看得出来是很用心做的了。
4.YOLOv6(2022.06):
YOLOv6是美团做的,具体参照 美团技术团队:YOLOv6:又快又准的目标检测框架开源啦 。怎幺说呢,大致就是YOLOX+整体RepVGG,还有一些trick包括采用SIoU loss、relu加速、训400epoch等。但我个人觉得创新点还不足以取名v6,也一直没有放出m l x版本完整的一套模型,感觉就是3个小模型在坐吃山空。
5.YOLOv7(2022.07):
YOLOv7是YOLOv4团队做的,AlexeyAB和YOLOR/CSP的作者Wang Chien-Yao王建尧均为YOLO界的知名大佬,算是YOLO正统,并且已经列在了YOLO之父Joseph Redmon的darknet上 https:// github.com/pjreddie/dar knet 。
YOLOv7主要是结构上提出E-ELAN模块,大规模E-ELAN模块堆叠都保持链路计算量参数量稳定,还有YOLOR的隐式参数学习和aux辅助头的设计。还放出了加P6的1280尺度的超大模型,可以看出目标不只是为了比较现有的YOLO,还为了和ConvNeXt Swin Transformer等模型硬碰硬,总之业内知名大佬出品,那肯定是用心设计过的。
2.各个YOLO代码实现和异同
yoloseries代码其实就是PaddleDetection上加了v5 v6 v7模型,PaddleDetection这个套件的检测模型大体框架还是很清楚简单的,yoloseries的实现也是直接继承使用。配置文件采用的yml形式是我比较喜欢的,因为以前用dict形式多层括号的时候就觉得繁琐也出过错,还有命名大小写驼峰形式不会看的很累,嵌套形式的config我觉得比把所有的都塞一个yml里方便清楚的多。
而模型结构的代码基本都在PaddleDetection/ppdet/modeling,新加yolo模型的话就是添加到这几个文件下:architectures、backbones 、neck、head、loss。YOLOE YOLOX的代码本来就都是Apache协议,百度和旷视写的也比较清楚易懂了。而原版v5 v6 v7的代码都是GPL协议,其实都总归是v5的代码,debug起来简直就是噩梦了,尽管yolov5作者几乎全天无休的维护,但是基本小修小改,代码几乎不大改,可读性依然很差,记得有个大佬说的很形象,yolov5代码就像是一辆摇摇晃晃东修西补的老爷车在高速公路上以120码的速度狂飙。而YOLOSeries代码重构,我看了下还是做的很好的。
一起来看下YOLOSeries里的v5 v6 v7相关的代码以及和yoloe yolox的异同点:
代码目录主要在 https:// github.com/nemonameless /PaddleDetection_YOLOSeries/tree/develop/ppdet/modeling
1.architectures:
主要是整体结构排布,architectures下的py都是这个作用,每个网络都继承自meta_arch.py里的BaseArch,重写get_loss()和get_pred()函数,分别对应训练和预测阶段。具体可以看 https:// github.com/nemonameless /PaddleDetection_YOLOSeries/tree/develop/ppdet/modeling/architectures
v5 v6 v7都采用的是ppdet/modeling/architectures/yolov5.py,应该是为了区分开yolov3和ppyolo的ppdet/modeling/architectures/yolo.py吧,内容基本一样yolov5.py更简洁点,yolo.py还有些跟踪相关的。而yolox.py也另开一个,因为只有它是在网络中用 F.interpolate 来制造多尺度训练的,所以yolox.py里是新加了_preprocess和_get_size函数。
2.backbones:
是各个backbone的代码,其中yoloe和v5都是ppdet/modeling/backbones/csp_darknet.py,v6则是 efficientrep.py ,还有v7也写在了csp_darknet.py里,而yoloe则是 cspresnet.py 。主要看 ppdet/modeling/ backbones里的代码,具体可以看
https:// github.com/nemonameless /PaddleDetection_YOLOSeries/tree/develop/ppdet/modeling/backbones
backbone的组成还是很有规律的,都是1个stem + 4个stage,下采样率分别是1/2,1/4, 1/8, 1/16, 1/32,最后一个stage还要加SPP模块才输出。yolox和yolov5的backbone几乎只有个别参数的差别,可能yolox本来就是用的yolov5 v6.0版本之前的backbone,所以用arch_settings给一个arch参数指定选用哪个。常规模型一般是640尺度的,return_idx都是后3位索引,表示输出是后3个stage的特征,步长分别是8, 16, 32,对应特征图就是80×80,40×40,20×20。而P6模型是1280尺度,就是1个stem+5个stage,输出后4个stage的特征图,所以FPN和head也相应就是4层,步长分别是8,16,32,64, 对应特征图就是160×160,80×80,40×40,20×20。P6模型现在只有v5 v7提供了,训一个P6模型是非常耗资源的。
2.1 stem的区别 ,stem主要是要进行1/2下采样和通道数从3通道增大
(1) yolov5(包括它的P6)的做法是一个大卷积核为6的ConvBN
(2) yolox则是Focus结构=切片重组+一个3×3的ConvBN,切片重组是下采样
(3) yoloe是3层3×3的ConvBN,代码里称为use_large_stem
(4) yolov6是一个RepConv(RepVGGBlock训练时是两个并联的Conv BN部署时会融合成一个卷积)
(5) yolov7就复杂了,L和X版本是像yoloe那样的3层3×3的ConvBN,tiny版本则是两层3×3的ConvBN,P6的W6 E6 D6 E6E则是ReOrg+ConvBN,其实就是Focus结构=切片重组+一个3×3的ConvBN
2.2 stage的区别 ,每个stage基本都遵循着 下采样+ConvBN堆叠的模块 的设计,最后一个stage一般再加一个SPP,这些stage输出的特征通常称为c2 c3 c4 c5,而c1也就是stem,也可以记为2的几次方就下采样几,c5就是下采样1/32,特征图大小就是640/32=20
(1) yolov5(包括它的P6):一个3×3的ConvBN下采样 + CSPLayer(C3),使用的是SPPF是一个单尺寸5核的SPP
(2) yolox:和yolov5一样,也是一个3×3的ConvBN下采样 + CSPLayer(C3),使用的SPP是(5, 9, 13)的核设置
(3) yoloe:是RepVGG+ResNet改造的,一个3×3的ConvBN下采样 + 一堆BasicBlock + 可选的EffectiveSE注意力机制 + 一个3×3的ConvBN,SPP模块是写在了neck里
(4) yolov6:是和yolov5 yolox一样,模块换成一个RepConv下采样 + RepLayer,使用的SPP是SimSPPF是和yolov5一样单尺寸5的核,但是使用relu激活而不是silu
(5) yolov7还是最复杂也没有章法:下采样方法第一个stage和后3个略有不同,总共包括了Conv, DownC, MPConvLayer, MP, None等各种方法,按模型arch种类来选择,在YOLOSeries代码里写的还是非常清楚的。 https:// github.com/nemonameless /PaddleDetection_YOLOSeries/blob/develop/ppdet/modeling/backbones/csp_darknet.py#L871 。至于堆叠的模块,是ELANLayer,在E6E里是ELAN2Layer其实是两个并联ELANLayer。最后SPP是SPPCSPC(5, 9, 13)卷积核设置,tiny则是SPPELAN(5, 9, 13)卷积核设置。具体还是看代码吧。
3. neck:
neck的组成都是采用了PAN形式,自底向上+自顶上下两个链路使得特征融合更充分。主要看 ppdet/modeling/ necks里的代码
(1) yolov5:是YOLOCSPPAN,两个lateral_convs+fpn_blocks组合 + 两个downsample_convs+pan_blocks组合,细看其实就是4个ConvBN+CSPLayer组合,P6模型则是3对+3对=6个组合
(2) yolox:是YOLOCSPPAN,和yolov5的neck一模一样,旷视原版的写法虽然清楚但不易复用为P6,只能另写一个PAFPNP6之类的
(3) yoloe:是CustomCSPPAN,写在了ppdet/modeling/necks/custom_pan.py里,结构和以前ppyolo前两版的PPYOLOPAN类似包括spp drop_block等trick,只是基础结构使用了cspresnet的模块,ConvBNLayer+CSPStage的组合
(4) yolov6:是RepPAN,和yolox和v5一样,只是基础模块是RepLayer而不是CSPLayer,上采样换成了Conv2DTranspose而不是nn.Upsample,是SimConv+RepLayer的组合
(5) yolov7:是ELANFPN,也是2对+2对=4个组合,BaseConv+ELANLayer,但是再各加一层卷积缩小下通道数,也还是复杂会分情况,L和X版本PAN的下采样是MPConvLayer,在tiny里则是普通ConvBN,最后一层卷积只有在L版本里是RepConv别的都是BaseConv。P6版本另写了一个ELANFPNP6,3对+3对=6个组合,E6、D6和E6E版本PAN的下采样是DownC,在W6里则是普通ConvBN,另外E6E版本的是使用的ELAN2Layer,相当于并联的两个ELANLayer,最后一层卷积都是BaseConv。另外如果是P6版本还需要使用aux_head训,就需要再返回更浅4层的fpn特征,也就是ELANFPNP6 返回的是8层特征图,当然也可以不使用aux_head就返回常规的4层。具体可以看代码: https:// github.com/nemonameless /PaddleDetection_YOLOSeries/blob/develop/ppdet/modeling/necks/yolo_fpn.py#L1266
4. head:
head的组成也有很多区别。主要看 ppdet/modeling/ heads里的代码:
(1) yolov5:和yolov3一样简洁直接就是一个anchor一层卷积直接输出分类回归结果,通道数是3x(num_classes + 5),后续会拆出4维度的reg(xywh)+num_classes维度的分类信息cls+1维度的obj,score是cls和obj想乘。
(2) yolox:是解耦的头,1层公用卷积的stem_conv+2层卷积的conv_cls/conv_reg(含pred卷积),其中pred的卷积都是nn.Conv2D而不是BaseConv,而且conv_reg的pred是包含了obj一起的是5维度的,其实一起做或拆开做是一样的,在旷视原版obj和reg的pred是拆分的。score是cls和obj想乘再开了个根号。
(3) yoloe:也是解耦的头,但是分类回归没有公用部分,一开始就分离cls和reg成为stem_cls和stem_reg,还加上了ESEAttn注意力,然后是nn.Conv2D的pred层卷积,还加上了一个projection conv。在预测的时候,neck的特征先过一层adaptive_avg_pool2d,再短路连接之类的总之就像TOOD那样。
(4) yolov6:和yolox一样。。。
(5) yolov7:P5的几个基础版本像L X,还是和yolov3一样简洁直接就是一层卷积直接输出分类回归结果,但是使用了YOLOR的ImplicitA和ImplicitM隐式参数机制,训练的时候正常过,预测的时候会做一个参数融合。另外P6模型的时候就加了aux就得再加4个卷积层。通道数依然是3x(num_classes + 5),score依然是cls和obj想乘。
5. loss(label assign):
到了最激动人心的loss部分了,应该是各家YOLO闪光点的地方了。loss调用一般写在 ppdet/modeling/ heads里,主要看各个head里的get_loss()函数,而定义一般写在 ppdet/modeling/ losses里:
(1) yolov5:anchor based,标签分配是传统的手动设计的anchor网格和gt匹配,cls obj都是BCEWithLogitsLoss,而reg采用了ciou loss。loss_weight是cls 0.5 obj 1.0 reg 0.05,obj还有一个balance[4.0, 1.0, 0.4]进一步乘上去加权obj。此外为了适配不同batch size的范围,总loss是乘以了总batch size的,这样就无论训什幺batch size都保持lr设置不变,但还是总batch size大点训精度会更高。
(2) yolox:anchor free,标签分配采用simota能自动决定每个gt拥有多少正样本和来自哪一层,cls和obj采用F.binary_cross_entropy,reg采用IouLoss,loss_weight除了IouLoss权重为5其余均为1,而L1 loss是最后15epoch才加配合着关闭mosaic。
(3) yoloe:anchor free,标签分配是联合策略,前1/3时期ATSS+后1/3时期TAL,cls使用了VFL(varifocal_loss), reg使用了GIoULoss,还有df_loss,值得一提的是L1 loss虽然也计算了但是没有加到总loss上,估计只是为了看收敛情况吧,因为确实L1 loss最能体现收敛。loss_weight是cls 1.0 iou 2.5 dfl 0.5,看样子也是实验过很多次调过了。
(4) yolov6:和yolox一样。。。iou loss换成了ciou和siou。
(5) yolov7: 标签分配方面,结合yolov5和Yolox,将simOTA中的第一步“使用中心先验”替换成“yolov5中的策略”,至于Aux Head是为对应的主head加强每层特征精修的。这个文章讲的还不错,yolov7正负样本分配详解 – 骚骚骚的文章 – 知乎 https:// zhuanlan.zhihu.com/p/54 3160484 。剩下的loss方面和yolov5一样,loss_weight是cls 0.3 obj 0.7 reg 0.05,此外tiny版本和P6的几个大模型loss_weight也不同。
6. data aug
这方面其实各个模型都可以借用过去试试。PaddleDetection的数据增强基本都用写在 ppdet/data/transform/operators.py 和 ppdet/data/transform/batch_operators.py,直接搜OP名字就能看到定义。配置文件都在各自的xxx_reader.yml里
(1) yolov5 v6 v7:reader基本一致,Mosaic里是用的random_perspective仿射变换,还有非常耗时的图片hsv变换。v7的Mosaic里还加了mixup和paste_in。
(2) yolox:mosaic是用的random_affine仿射变换,使用mixup当copy paste用,还有一个另类的hsv变换。最后15个epoch会关闭Mosaic使用L1 loss,因为做了mosaic会把原图的gt相对变小改变了分布,最后关闭mosaic相当于加大了变相加大了训练的gt分布,这个肯定是有增益的。
(3) yoloe:只有最基本的,除了RandomDistort、RandomExpand、RandomCrop,ppyolo前两版还有mixup。
需要说明的是,数据增强的操作会影响训练速度。其中hsv和mosaic(包括mixup copy paste等)都是非常耗时的OP,yolov5 采用了cache机制加速,后面v6 v7直接照用。mosaic是公认涨点很多的操作,从这点看yoloe很吃亏,后续看yoloe作者他们会不会加上吧。
7.其他
模型库完整性上看,v5 v7 yoloe yolox都是s m l x齐全的,v6只有3个小模型没有m l x版本,P6-1280尺度模型只是v5 v7有提供。
还有其他一些trick方面的:
(1)训练epoch数:v6是训了400epoch,v5 v6 v7 yoloe都是300epoch,yoloe后来也训了个400epoch是比v6更高的,听说yoloe yolox还会开放obj365预训练权重,这可是个超级外挂。
(2)多尺度训练方面:yoloe yolox是多尺度,v5 v6 v7是单尺度640,但是这个多尺度好像只能涨一点点精度不像两阶段那种涨很多,P6的1280尺度只有v5 v7有,训一个可费时间了。
(3)优化器方面,全都是SGD(Momentum),但yolox v5 v6 v7还带了nesterov,v5 v6 v7还有momentum=0.937和分组优化器不同lr等trick,估计也能涨0.几吧,只能说v5种树,v6 v7乘凉。
(4)NMS:关于NMS方面看了这两个回答后才恍然大悟,【 如何评价美团提出的yolo v6? – 知乎 https://www. zhihu.com/question/5392 05443/answer/2557990982 】【如何评价Alexey Bochkovskiy团队提出的YoloV7? – 知乎 https://www. zhihu.com/question/5419 85721/answer/2562541931 】。原来v5 v6 v7的同一套nms还有这幺多猫腻,这样部署的时候和eval严格意义上就不一样了,权重还是同一个权重,但是部署onnx后再去eval就无法得到直接eval那幺高精度,所以v5 v6 v7的确是eval出了一个虚高的精度。而yolox和yoloe这方面就做的一致,这样子看eval的精度表格上yolox和yoloe其实是吃亏的,但是部署onnx方面不至于有掉精度的落差。
(5)激活函数和速度的trick:v5 v7 yoloe yolox都是整体全部使用了silu激活,因为在trt部署的时候无论是torch还是paddle都会用x*sigmoid(x)融合无去代替silu,这样会快很多很多。而v6在大部分卷积里使用的是relu,也有silu,看了这篇 【如何评价美团提出的yolo v6? – 张志的回答 – 知乎 https://www. zhihu.com/question/5392 05443/answer/2543602804 】后才明白,理论上来讲只需要把任何yolo中的所有激活函数换成relu,速度就可以提高20%~30%的性能。
(6)量化:v6 v7 yoloe都是使用了RepVGG,但是量化性能不佳是RepVGG的固有问题,看到PaddleSlim团队对v6 v7 yoloe的处理也有些措施和成效,有空去试用下看看。 https:// github.com/PaddlePaddle /PaddleSlim/tree/develop/example/auto_compression
3.总结
现在YOLO各个模型库各自为营,维护力度不一,用户只能使用某一套,如想使用多个模型在使用时需要切换代码库和调整用法。而yoloseries这个库设计的初衷和实施方面都还是很好的,yoloseries作者也说会和PaddleDetection定期同步更新,加了个飞桨的用户群也一直有百度的工程师回复初学者问题。
我试验了一下YOLOSeries的每个YOLO模型都能训练测试ok,用起来还行吧,代码风格也是比较易懂了,至少是比v5系列的代码可读性高很多了,二次开发也挺方便。YOLO还在继续发展中,不知道后面还有什幺yolo模型,总之还是继续保持学习态度吧,结合代码和论文一起看模型,一起来say yolo again吧!
Be First to Comment