文章目录
- 老铁们✌,重要通知🙌!福利来了!!!😉
- 1.传统目标检测方法
- 2.One stage
- 3.Two stage
-
- SPPNet
- Faster R-CNN
-
- (1)请介绍一下faster R-CNN网络的原理(注: 需要能够详细画出网络结构图)
- (2)请简要叙述一下"R-CNN——faster R-CNN"
- (3)faster R-CNN是经典的two-stage 检测器,请简单说明其中two-stage的含义。
- (4)请简要说明一下RPN(Region Proposal Network)网络的作用、实现细节
- (5)说一下RoI Pooling是怎么做的?有什么缺陷?有什么作用?
- (6) ROI Pooling与ROI Align(Mask R-CNN)的区别
- (7)Faster R-CNN是如何解决正负样本不平衡的问题?
- (8)Faster RCNN怎么筛选正负anchor
- (9)Faster R-CNN中bbox回归用的是什么公式,说一下该网络是怎么回归bbox的?
- (10)简述faster rcnn的前向计算过程并简述faster rcnn训练步骤
- (11)介绍faster rcnn这个流程,faster rcnn有哪些缺点?如何改进?
- (12)阐述一下Mask RCNN网络,这个网络相比于Faster RCNN网络有哪些改进的地方
- (13) 比较FasterRCNN在RCNN系列中的改进点
- (14) Faster RCNN的loss有哪些?分别讲下。
- Mask RCNN
- ROI
- ROI Pooling
- 目标检测回归损失函数有哪些?
- One Stage Vs Two Stage
- 4.RetinaNet
- 5.FPN
- 6.综合问题
-
- 6.1 阐述一下ssd和retinanet的区别
- 6.2 faster rcnn和yolo,ssd之间的区别和联系
- 6.3 分析一下SSD,YOLO,Faster rcnn等常用检测网络对小目标检测效果不好的原因
- 6.4 目标检测技巧有哪些?
- 6.5 讲一下目标检测优化的方向
- 6.6 如果只能修改RPN网络的话,怎么修改可以提升网络小目标检出率
- 6.7 阐述一下如何检测小物体
- 6.8 阐述一下目标检测任务中的多尺度
- 6.9 如果有很长,很小,或者很宽的目标,应该如何处理目标检测中如何解决目标尺度大小不一的情况 小目标不好检测,有试过其他的方法吗?比如裁剪图像进行重叠
- 6.10 检测的框角度偏移了45度,这种情况怎么处理?<海康-一面(2020)>
- 6.11 做目标检测的话,数据集用什么?
- 6.12 用自己的数据集做目标检测,在采集数据时应注意哪些问题?
- 7.算法的比较
- 8.人脸检测相关问题
- 9.分类、目标检测、语义分割、实例分割的区别
老铁们✌,重要通知🙌!福利来了!!!😉
【计算机视觉 复习流程剖析及面试题详解 】
【深度学习算法 最全面面试题(30 页)】
【机器学习算法 最全面面试题(61页)】
1.传统目标检测方法
1.1 Viola-Jones (人脸检测)
1.2 HOG+SVM (行人检测、Opencv实现)
1.3 DPM (物体检测)
NMS (非极大值抑制算法)
1 利用得分,给检测框排序:
选出最大得分的检测框A1,
将与A1的IOU重叠率高的检测框,进行删除,
2 其余的剩下的检测再次排序,选择最大的A2,
将与A2的IOU重叠率高的检测框,再次进行删除,
3 …迭代多次
2.One stage
One-stage核心组件: CNN网络 + 回归网络
主要包含SSD,YOLOv1、YOLOv2、YOLOv3系列。
主干网络:CNN; 核心组件:回归网络
2.1 Yolo体系变化
输入侧 | Backbone | Neck | Head | Tricks | |
---|---|---|---|---|---|
YOLO | |||||
YOLOv1 | 调整输入图像的尺寸以支持对图像细粒度特征的挖掘与检测 | 使用了Leaky ReLu,没有引入BN层 | |||
YOLOv2 | 引入了多尺度训练,并且优化了预训练模型。 | 设计了Darknet-19网络,并引入了BN层优化模型整体性能 | 进行了网络结构和损失函数的改进,引入 anchor box | ||
YOLOv3 | 设计了Darknet-53结构(类似 ResNet 引入残差结构) | 引入了FPN,以支持后面的Head侧采用多尺度来对不同size的目标进行检测,越精细的grid cell就可以检测出越精细的目标物体。 | 引入了多尺度检测逻辑和多标签分类思想,优化了损失函数 | ||
YOLOv4 | 使用了Mosaic和CutMix高阶数据增强来提升模型的整体性能 | 受CSPNet网络结构启发,将多个CSP子模块进行组合设计成为CSPDarknet53,并且使用了Mish激活函数。 | SPP和PAN | 沿用了YOLOv3的整体架构,并引入了CIOU Loss和DIOU NMS来提升Head结构的整体性能。 | 使用了SAT,CmBN和Label Smoothing等 |
YOLOv5 | 使用了Mosaic高阶数据增强策略,并增加自适应图像调整策略。 | 使用了SPP和PAN,但是在PAN模块进行融合后,将 v4中使用的常规CBL替换成借鉴CSPnet设计的CSP_v5结构,加强网络特征融合的能力。 | 引入了自适应anchor box和邻域正负样本分配策略 | 和 v4 一致,引入了更多的工程优化逻辑,还尝试了混合精度训练和模型EMA策略 | |
YOLOX | 在YOLOv5的基础上摒弃了预训练逻辑,并使用Mosaic和MixUp高阶数据增强算法。 | 沿用了YOLOv3的Backbone结构 | 使用了YOLOv3的结构,并且使用了SPP | 引入了 解耦头,并使用Anchor-free思想和SimOTA正负样本分配策略进行损失函数的计算与优化。 | 使用了v5中提到的模型EMA策略,并且使用余弦退火学习率优化训练过程。 |
YOLOv6 | 沿用了v5的整体逻辑 | 设计了EfficientRep Backbone结构; 将SPPF优化设计为更加高效的SimSPPF,增加特征重用的效率。 | 设计了可重参数化、更高效的Rep-PAN | 使用Anchor-free和SimOTA标签分配策略,并在其基础上改进了解耦检测头结构,在损失函数中引入了SIoU边界框回归损失。 | 进行了很多蒸馏方向上的尝试 |
YOLOv7 | 沿用了v5的整体逻辑 | 在v5的基础上,设计了E-ELAN和MPConv结构 | SPPSCP模块和优化的PAN模块 | 引入RepVGG style改造了Head网络结构,并使用了辅助头训练以及相应的正负样本匹配策略。 | EMA策略 |
YOLOv8 | 将C3模块以及RepBlock++替换++为了C2f,实现了进一步的轻量化;SPPF替换SPP | PAN-FPN(上采样1x1 卷积 删除) | 解耦头结构,将分类和检测头分离;使用 Anchor-Free;VFL Loss作为分类损失,DFL Loss+CIOU Loss作为分类损失 |
YOLO系列算法是一类典型的one-stage目标检测算法,其利用anchor box将分类与目标定位的回归问题结合起来,从而做到了高效、灵活和泛化性能好。
-
YOLOv1: YOLOv1的核心思想就是利用整张图作为网络的输入,直接在输出层回归 bounding box(边界框) 的位置及其所属的类别。
YOLOv1的基本思想: 把一副图片,首先reshape成448×448大小(由于网络中使用了全连接层,所以图片的尺寸需固定大小输入到CNN中),然后将划分成SxS个单元格(原文中S=7),以每个格子所在位置和对应内容为基础,来预测检测框和每个框的Confidence以及每个格子预测一共C个类别的概率分数。
创新点:
- 将整张图作为网络的输入,直接在输出层回归bounding box的位置和所属的类别
- 速度快,one stage detection的开山之作
损失函数设计细节:YOLOv1对位置坐标误差,IoU误差,分类误差均使用了均方差作为损失函数。激活函数(最后一层全连接层用线性激活函数,其余层采用leak RELU)
缺点:
- 首先,每个单元格只预测2个bbox,然后每个单元格最后只取与gt_bbox的IOU高的那个最为最后的检测框,也只是说每个单元格最多只预测一个目标。
- 损失函数中,大物体 IOU 误差和小物体 IOU 误差对网络训练中 loss 贡献值接近(虽然采用求平方根方式,但没有根本解决问题)。因此,对于小物体,小的 IOU 误差也会对网络优化过程造成很大的影响,从而降低了物体检测的定位准确性。
- 由于输出层为全连接层,因此在检测时,YOLO 训练模型只支持与训练图像相同的输入分辨率的图片。
- 和two-stage方法相比,没有region proposal阶段,召回率较低
-
YOLOv2: YOLOv2又叫YOLO9000,其能检测超过9000种类别的物体。相比v1提高了训练图像的分辨率;引入了faster rcnn中anchor box的思想,对网络结构的设计进行了改进,输出层使用卷积层替代YOLO的全连接层,联合使用coco物体检测标注数据和imagenet物体分类标注数据训练物体检测模型。相比YOLO,YOLO9000在识别种类、精度、速度、和定位准确性等方面都有大大提升。
**相比于v1的改进: **
- Anchor: 引入了Faster R-CNN中使用的Anchor,作者通过在所有训练图像的所有边界框上运行k-means聚类来选择锚的个数和形状(k = 5,因此它找到五个最常见的目标形状)
- 修改了网络结构,去掉了全连接层,改成了全卷积结构。
- 使用Batch Normalization可以从model中去掉Dropout,而不会产生过拟合。
- 训练时引入了世界树(WordTree)结构,将检测和分类问题做成了一个统一的框架,并且提出了一种层次性联合训练方法,将ImageNet分类数据集和COCO检测数据集同时对模型训练。
-
YOLOv3: YOLOv3总结了自己在YOLOv2的基础上做的一些尝试性改进,有的尝试取得了成功,而有的尝试并没有提升模型性能。其中有两个值得一提的亮点,一个是使用残差模型,进一步加深了网络结构;另一个是使用FPN架构实现多尺度检测。
改进点:
- 多尺度预测 (类FPN):每种尺度预测3个box, anchor的设计方式仍然使用聚类,得到9个聚类中心。
- 更好的基础分类网络(类ResNet)和分类器 darknet-53。
- 用逻辑回归替代softmax作为分类器。
解释:cmBN: Conv + Mish + BatchNorm
2.2 YOLO网络结构是怎样的?
强烈推荐阅读:Yolo发展史
YOLO网络借鉴了GoogLeNet分类网络结构,不同的是YOLO使用1x1卷积层和3x3卷积层替代inception module。如下图所示,整个检测网络包括24个卷积层和2个全连接层。其中,卷积层用来提取图像特征,全连接层用来预测图像位置和类别概率值。
2.3 YOLO的输入、输出、损失函数分别是什么?
YOLO将输入图像分成7x7的网格,最后输出是7x7xk的张量。YOLO网络最后接了两个全连接层,全连接层要求输入是固定大小的,所以YOLO要求输入图像有固定大小,论文中作者设计的输入尺寸是448x448。
2.4 YOLO怎样预测?
采用 NMS 算法从输出结果中提取最有可能的对象和其对应的边界框。
手写NMS:
https://zhuanlan.zhihu.com/p/75348108
NMS步骤如下:
1.设置一个Score的阈值,一个IOU的阈值;
2.对于每类对象,遍历属于该类的所有候选框,
①过滤掉Score低于Score阈值的候选框;
②找到剩下的候选框中最大Score对应的候选框,添加到输出列表;
③进一步计算剩下的候选框与②中输出列表中每个候选框的IOU,若该IOU大于设置的IOU阈值,将该候选框过滤掉,否则加入输出列表中;
④最后输出列表中的候选框即为图片中该类对象预测的所有边界框
3.返回步骤2继续处理下一类对象。
2.5 YOLOv3不使用Softmax对每个框进行分类的原因?
Softmax使得每个框分配一个类别(score最大的一个),而对于Open Images这种数据集,目标可能有重叠的类别标签,因此Softmax不适用于多标签分类。
Softmax可被独立的多个logistic分类器替代,且准确率不会下降。
2.6 YOLOv3的架构
YOLOv3在之前Darknet-19的基础上引入了残差块,并进一步加深了网络,改进后的网络有53个卷积层,取名为Darknet-53。
强烈推荐阅读: yolo3深度解析
图片引用:YOLOv3网络结构图
请叙述一下YOLOv3中k-means聚类获得anchor boxes过程详解
- 将所有的bounding box坐标提取出来
- 数据处理获得所有训练数据bounding boxes的宽高数据(长=右下角横坐标-左上角横坐标、宽=右下角纵坐标-左上角纵坐标)
- 初始化k个anchor box(通过在所有的bounding boxes中随机选取k个值作为k个anchor boxes的初始值)。
- 计算每个bounding box与每个anchor box的iou值,计算得到距离(传统的聚类方法是使用欧氏距离来衡量差异,YOLOv3作者发现传统方法在box尺寸比较大的时候,其误差也更大,改用iou距离):
d = 1 − i o u d = 1 - iou d=1−iou - 分类操作。上一步得到每一个bounding box对于每个anchor box的误差 D n ∗ k D_{n*k} Dn∗k,对于第i个bounding box对于每个anchor box的误差为{d(i,1),d(i,2),…,d(i,k)},选取误差最小的anchor box,分配给它。
- anchor box更新。(经过上一步,我们就知道每一个anchor box都有哪些bounding box属于它,然后对于每个anchor box中的那些bounding box,我们再求这些bounding box的宽高的中值大小(这里参照github上作者qqwweee那个yolov3项目,也许也有使用平均值进行更新),将其作为该anchor box新的尺寸)
- 重复操作第四步到第六步,直到在第五步中发现对于全部bounding box其所属的anchor box类与之前所属的anchor box类完全一样。(这里表示所有bounding box的分类已经不再更新)
- 计算anchor boxes精确度。至第七步,其实已经通过k-means算法计算出anchor box。
YOLOv3框是怎么得到的? YOLOv3有什么致命问题?
yolov3通过聚类的方式自定义anchor box的大小,在一定程度上,这可以提高定位的准确率。
缺点: 识别物体位置精准性差,召回率低(在每个网格中预测两个bbox这种约束方式减少了对同一目标的多次检测)
2.7 YOLOv4的架构
架构解析
思维导图:
CSPDarknet53
解决了其他大型CNN框架Backbone中网络优化的梯度信息重复问题,将梯度的变化从头到尾地集成到特征图中,因此减少了模型的参数量和FLOPS(每秒浮点运算次数)数值,既保证了推理速度和准确率,又减小了模型尺寸。
BackBone训练策略
1.采用了Mosaic数据增强
(参考了CutMix数据增强; 区别:Mosaic是一种将4张训练图像合并成一张进行训练的数据增强方法(而不是CutMix中的2张)。
每个小批包含一个大的变化图像(4倍),减少了估计均值和方差的时需要大mini-batch的要求,降低了训练成本。
2.DropBlock正则化(引入:克服Dropout随机丢弃特征):块的相邻相关区域中丢弃特征。
3.类标签平滑
检测头训练策略
1.CIoU-loss: CIOU只是在DIOU基础上增加了一项 av
2.CmBN策略: 只在每个Batch内部使用CBN的方法
3.SAT:
第一阶段:NN改变原始图像而不是网络权值。通过这种方式,NN对其自身进行一种对抗式的攻击,改变原始图像,制造图像上没有目标的假象;
第二阶段:训练NN对修改后的图像进行正常的目标检测。
消除网格敏感度
4.单目标使用多Anchor
5.余弦模拟退火
检测头推理策略
Mish, SPP, PAN,SAM和DIoU-NMS
为了增大感受野,使用了SPP-block(对任意尺寸的特征图直接进行固定尺寸的池化-> 固定数量的特征),使用PAN代替FPN进行参数聚合以适用于不同level的目标检测。
SPP:用来解决不同尺寸的特征图如何进入全连接层的
PAN: 代替FPN进行参数聚合以适用于不同level的目标检测,YOLOv4算法将融合的方法由addition改为Concatenation
DarkNet-53相对DarkNet-19的有如下几点改进:
1.加深网络层,精度提升,但是速度有所下降,
2.进入了残差网络resNet模块,防止梯度下降;
3.用卷积strid==2代替了池化,防止信息丢失
4.使用SPP实现多尺寸输入,同尺寸输出。
名词解释:
CSPNet (跨阶段局部网络)
SPP-Net(空间金字塔池化网络):Spatial Pyramid Pooling Networks
PAN(路径聚合网络):Path Aggregation Network
SAT(自对抗训练): Self-Adversarial Training
2.8 YOLOv5的架构
2.8.1 yolo5原理
原理可以分为四部分:输入端、backbone、Neck、输出端;
输入端
:针对小目标的检测,沿用了v4的mosaic增强,当然这个也是v5作者在他复现的v3上的原创,对不同的图片进行随机缩放、裁剪、排布后进行拼接;二是自适应锚框计算,在v3、v4中,初始化锚框是通过对coco数据集的进行聚类得到,v5中将锚框的计算加入了训练的代码中,每次训练时,自适应的计算不同训练集中的最佳锚框值;
backbone
:沿用了V4的CSPDarkNet53结构,但是在图片输入前加入了Focus切片
操作,CSP结构实际上就是基于Densnet的思想,复制基础层的特征映射图,通过dense block发送到下一个阶段,从而将基础层的特征映射图分离出来。这样可以有效缓解梯度消失问题,支持特征传播,鼓励网络重用特征,从而减少网络参数数量。在V5中,提供了四种不同大小的网络结构:s、m、l、x,通过depth(深度)和width(宽度)两个参数控制。
Neck
:采用了SPP+PAN多尺度特征融合,PAN是一种自下而上的特征金字塔结构,是在FPN的基础上进行的改进,相对于FPN有着更好的特征融合效果。
输出端
:沿用了V3的head,使用GIOU损失进行边框回归,输出还是三个部分:置信度、边框信息、分类信息。
2.8.2 yolov5引入了CSP结构,介绍一下它的原理和作用?
CSP结构是一种思想,它和ResNet、DenseNet类似,可以看作是DenseNet的升级版,它将feature map拆成两个部分:
一部分进行卷积操作;另一部分和上一部分卷积操作的结果进行concate。主要解决了三个问题:
1.增强CNN的学习能力,能够在轻量化的同时保持着准确性;2. 降低计算成本;3. 降低内存开销。
CSPNet改进了密集块和过渡层的信息流,优化了梯度反向传播的路径,提升了网络的学习能力,同时在处理速度和内存方面提升了不少。
2.9 YOLOX的架构
2.9.1 简介
简单理解,YOLOX = YOLO v5-5.0 + FCOS + OTA.
YOLOX整体上依然沿用了YOLO v5(5.0版)的结构,但在检测头上进行了修改,使用了类似FCOS的检测头(一些细节上有差异). 此外,YOLOX是Anchor-free的,标签分配策略上其基于OTA进行了简化,提出SimOTA,SimOTA更高效地实现了训练阶段给每个GT自适应动态分配k个正样本的过程.
2.9.2 网络改进点
Decoupled Head 解耦头
作者研究发现,检测头耦合会影响模型性能。采用解耦头替换YOLO的检测头可以显著改善模型收敛速度。因此,作者将YOLO的检测头替换为轻量解耦头:它包含一个1 × 1 1\times 11×1卷积进行通道降维,后接两个并行分支(均为3 × 3 3 \times 33×3卷积)。注:轻量头可以带来1.1 m s 1.1ms1.1ms的推理耗时。
Mosaic + Mixup 数据增强
对图像进行混类增强的算法,它将不同类之间的图像进行混合,从而扩充训练数据集。
作者颠覆性发现:使用强大的数据增强后,ImageNet预训练模型无益,所有后续模型都是随机初始化权重。
Anchor-free
YOLO v4、YOLO v5均采用了YOLO v3原始的anchor设置。然而anchor机制存在诸多问题:
(1) 为获得最优检测性能,需要在训练之前进行聚类分析以确定最佳anchor集合,这些anchor集合存在数据相关性,泛化性能较差;
(2) anchor机制提升了检测头的复杂度。
Anchor-free检测器在过去两年得到了长足发展并取得了与anchor检测器相当的性能。
将YOLO转换为anchor-free形式非常简单,我们将每个位置的预测从3下降为1并直接预测四个值:即两个offset以及高宽。这种改进可以降低检测器的参数量于GFLOPs进而取得更快更优的性能:42.9%AP。
Multi positives
将中心3*3区域都认为是正样本,即从上述策略每个gt有1个正样本增长到9个正样本。且AP提升到45%,已经超越U版yolov3的44.3%AP。
SimOTA
OTA从全局角度分析了标签分配并将其转化为最优运输问题取得了SOTA性能。
然而,作者发现:最优运输问题优化会带来25 % 25%25%的额外训练耗时。因此,我们将其简化为动态top−k策略以得到一个近似解(SimOTA)。SimOTA不仅可以降低训练时间,同时可以避免额外的超参问题。SimOTA的引入可以将模型的性能从45.0 % 提升到47.3 %,大幅超越U版YOLOv的44.3 % 。
simOTA流程:
1、确定正样本候选区域。
2、计算anchor与gt的iou。
3、在候选区域内计算cost。
4、使用iou确定每个gt的dynamic_k。
5、为每个gt取cost排名最小的前dynamic_k个anchor作为正样本,其余为负样本。
6、使用正负样本计算loss。
End-to-End YOLO
YOLO X参考PSS添加了两个额外的卷积层,one-to-one标签分配以及stop gradient。这些改进使得目标检测器进化成了端到端形式,但会轻微降低性能与推理速度。
2.10 YOLO6的架构
2.10.1 简介
EfficientRep Backbone
参考了重参数结构化的实现,对多卷积分支的拓扑结构进行融合。其实yolov5中也有使用到,yolov5中对3x3卷积和bn层进行了融合。
yolov6的网络结构部分,可以看见这里提出了3个模块:RepConv,RepBlock,SimSPPF
SimSPPF
通过源码分析,yolov6的代码很大部分是套用yolov5的代码结构的。所谓的SimSPPF和SimConv本质上只是激活函数使用了ReLU而不是SiLU
RepConv
RepVGG将3x3,1x1,identity分支的残差结果利用数学计算方法等价为一个3x3的卷积结构,实现训练与推断过程的解耦;RepMLP将局部的CNN先验信息加进了全连接层,使得其与MLP相结合等等。这里需要注意,重结构化层MLP结构也不是说变成Linear层,而是简化为1x1的卷积。
原理如下图所示:
核心的思想是为了解耦训练过程与推理过程。训练过程中花的时间可以长点,缩短推理时间。在推理过程中,重结构化的网络拓扑结构会变得简单,加快推理速度。
RepBlock
多个RepConv的堆叠,不过其中 RepBlock 的第一个 RepConv 会做 channel 维度的变换和对齐。
Rep-PAN
结构示意图如下所示:
这里的1x1 Conv其实是1x1卷积的作为通道降维操作,Upsample是使用了反卷积nn.ConvTranspose2d(其实也是卷积的一种);3x3 Conv为了减半尺寸,RepBlock与backbone中的类似。
Decoupled Head
结构示意图:
意思就是将解耦进行到底。为每一层的预测特征层都配置不一样的回归卷积,分类卷积,置信度卷积。按照一般的FPN结构是3层,那么现在就是每一层都会有其独立的卷积输出。整个detect head不共享任何的结构,参数完全是独立的。
在head这一部分,除了是完全参数独立之外,其实没有过太多的改进。训练和推理形式都是类似的,返回和推理的shape也是和yolov5类似的。
总结:
综上所述,将参数重结构化(3x3,1x1,残差分支合并为一个3x3卷积)方法使用在yolov5的框架中,改进了backbone与neck部分,方便部署。然后在head上使用了参数全独立的解耦。不过,缺点是甚至没有借鉴yolov5的网格敏感度消除,网络可能会出现极端点无法预测的情况。
2.10.2 训练策略改进
Anchor-free 无锚范式
由于 Anchor-based检测器需要在训练之前进行聚类分析以确定最佳 Anchor 集合,这会一定程度提高检测器的复杂度,而anchor-free就不需要任何anchor的手工或者是聚类的设置,直接是类似语义分割的想法在每个特征点上直接预测4个回归目标域k个分类目标。
SimOTA 标签分配策略
YOLOv5 的标签分配策略是基于 Shape 匹配,并通过跨网格匹配策略增加正样本数量,从而使得网络快速收敛,但是该方法属于静态分配方法,并不会随着网络训练的过程而调整。
YOLOv6 引入了 SimOTA 算法动态分配正样本,进一步提高检测精度。
SIoU 边界框回归损失
对于SIoU损失,其结果形式搞得相当的复杂,其具体包含了四个部分:角度损失(Angle cost)、距离损失(Distance cost)、形状损失(Shape cost)、IoU损失(IoU cost)
2.10.3 总结
yolov6的实验对比更加偏向于轻量级模型的对比。
yolov6 的改进 主要体现: 一个是模型上的改进,另外一个是训练策略上的改进。
但是可以看见,无论是模型上的改进还是训练策略上的改进并没有提出一些比较新颖的见解。对于网络结构上的改进,主要是利用了重结构化的思想改进了yolov5的框架,同时将head进行参数的完全独立不共享(这样做可能会有效,但是感觉增加了很大的参数量)。而参数重结构化可以使得推理过程的速度加快,改变网络的拓扑结构。
对于训练策略上,没有消除网格的敏感度,可能会导致特殊值无法处理的情况。使用了一个比较新颖的Iou损失函数。但是,无论是模型还是训练策略,更感觉是一个大杂烩。特别是完全的参数独立的解耦头,只提升了0.2%,对于我们个人的配置有点怀疑其有效性,根绝投入和收获不成比例。
2.11 YOLO7的架构
2.11.1 改进点
主要引入了三种模块:E-ELAN
模块、SPPSPC
模块、REP
模块。
CSPVoVNet:在考虑参数量、运算量、计算密度、梯度路径等条件下改进了VoVNet,不同层的权重可以学习到更多样化的特征,推理速度更快、准确率更高
VoVNet的另一个表述:
E-ELAN
● ELAN:通过控制最短和最常的梯度路径来让很深的网络可以有效的学习和收敛,但是如果堆叠更多的模块可能会破坏这种稳定的状态;
YOLOv7提出了extended ELAN(E-ELAN),它通过expand cardinality、shuffle cardinality、merge cardinality等方法达到不破坏梯度路径的状态,并且能够持续增强网络学习的效果。
简化如下:
CBS 主要是 Conv + BN + SiLU
构成。
SPPSPC模块
只使用了一次SPPSPC模块,在backbone与head对接的地方。
总的输入会被分成两段进入不同的分支,最中间的分支其实就是金字塔池化操作,上侧则为一个point conv,最后将所有分支输出的信息流进行concat。
REP模块
REP在训练和部署的时候结构不同,在训练的时候由3x3的卷积添加1x1的卷积分支,同时如果输入和输出的channel以及h,w的size一致时,再添加一个BN的分支,三个分支相加输出,在部署时,为了方便部署,会将分支的参数重参数化到主分支上,取3*3的主分支卷积输出。
MP 结构
MP 层 主要是分为 Maxpool 和 CBS , 其中 MP1 和 MP2 主要是通道数的比变化。
使用了max pooling 和 stride=2的conv。 需要注意backbone中的MP前后通道数是不变的。
模型缩放
作用: 将模型进行缩放,可以适用于不同的运算设备。
作者提出 compound model scaling (复合模型缩放),即对 concatenation-based(基于串联) 的模型进行模型缩放时,只需要对 computational block 中的 depth 进行缩放,其余的 transition 进行相应的宽度缩放,就能够维持初始模型在设计时拥有的性质, 以维持最佳的结构。
结果:可以看到使用 model scaling 技术可以提高准确率,若单纯将 width 放大 1.25 倍可以提升 0.7% AP,若单纯将 depth 放大 2 倍则可以提升 1% AP,若进一步使用 compound model scaling:depth 放大 1.5 倍,width 放大 1.25 倍可以提升最多准确率,高达 1.2% AP。
2.12 YOLOv8 的架构
具体改进如下:
-
Backbone:第一层卷积由原本的 6×6 卷积改为 3×3 卷积;参考 YOLOv7 ELAN 设计思想将 C3 模块换成了 C2f 模块,并配合调整了模块的深度。
-
PAN-FPN:YOLOv8将YOLOv5中PAN-FPN上采样阶段中的 1x1 卷积结构删除了,同时也将C3模块替换为了C2f模块。
-
Head 部分: 改动较大,换成了解耦头结构,将分类任务和回归任务解耦,同时也从 Anchor-Based 换成了 Anchor-Free。
-
损失函数:YOLOv8使用 BCE Loss作为分类损失,使用DFL Loss+CIOU Loss作为回归损失; (Distribution Focal Loss)
-
样本匹配:采用了 Task-Aligned Assigner匹配方式。
backbone:
# [from, repeats, module, args]
- [-1, 1, Conv, [64, 3, 2]] # 0-P1/2 320*320*64
- [-1, 1, Conv, [128, 3, 2]] # 1-P2/4 160*160*128
- [-1, 3, C2f, [128, True]]
- [-1, 1, Conv, [256, 3, 2]] # 3-P3/8 80*80*256
- [-1, 6, C2f, [256, True]]
- [-1, 1, Conv, [512, 3, 2]] # 5-P4/16 40*40*512
- [-1, 6, C2f, [512, True]]
- [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32 20*20*1024
- [-1, 3, C2f, [1024, True]]
- [-1, 1, SPPF, [1024, 5]] # 9 20*20*1024
# YOLOv8.0n head
head:
- [-1, 1, nn.Upsample, [None, 2, 'nearest']] # 40*40*1024
- [[-1, 6], 1, Concat, [1]] # cat backbone P4 [1]: concat拼接的维度是1 40*40*(1024+512)
- [-1, 3, C2f, [512]] # 12 40*40*512
- [-1, 1, nn.Upsample, [None, 2, 'nearest']] # 80*80*512
- [[-1, 4], 1, Concat, [1]] # cat backbone P3 80*80*(512+256)
- [-1, 3, C2f, [256]] # 15 (P3/8-small) 80*80*256
- [-1, 1, Conv, [256, 3, 2]] # 40*40*256
- [[-1, 12], 1, Concat, [1]] # cat head P4 # 40*40*(512+256)
- [-1, 3, C2f, [512]] # 18 (P4/16-medium) 40*40*512
- [-1, 1, Conv, [512, 3, 2]] # 20*20*512
- [[-1, 9], 1, Concat, [1]] # cat head P5 20*20*(1024+512)
- [-1, 3, C2f, [1024]] # 21 (P5/32-large) 20*20*1024
- [[15, 18, 21], 1, Detect, [nc]] # Detect(P3, P4, P5) 将第15、18、21层的输出(分别是80*80*256、40*40*512、20*20*1024)
# 作为本层的输入。nc是数据集的类别数。
C2f模块
针对C3模块,其主要是借助CSPNet提取分流的思想,同时结合残差结构的思想,设计了所谓的C3 Block,这里的CSP主分支梯度模块为BottleNeck模块,也就是所谓的残差模块。同时堆叠的个数由参数n来进行控制,也就是说不同规模的模型,n的值是有变化的。
C3模块: 在模块中使用了3个卷积模块(Conv+BN+SiLU),以及n个BottleNeck。
C3模块结构图:
其实这里的梯度流主分支,可以是任何之前你学习过的模块,比如,美团提出的YOLOv6中就是用来重参模块RepVGGBlock来替换BottleNeck Block来作为主要的梯度流分支,而百度提出的PP-YOLOE则是使用了RepResNet-Block来替换BottleNeck Block来作为主要的梯度流分支。而 YOLOv7 则是使用了ELAN Block来替换BottleNeck Block来作为主要的梯度流分支。
Conv、BottleNeck、C3的代码如下:
class Conv(nn.Module):
# Standard convolution 通用卷积模块,包括1卷积1BN1激活,激活默认SiLU,可用变量指定,不激活时用nn.Identity()占位,直接返回输入
def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups
super(Conv, self).__init__()
self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False)
self.bn = nn.BatchNorm2d(c2)
self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
def forward(self, x):
return self.act(self.bn(self.conv(x)))
def fuseforward(self, x):
return self.act(self.conv(x))
class Bottleneck(nn.Module):
# Standard bottleneck 残差块
def __init__(self, c1, c2, shortcut=True, g=1, e=0.5): # ch_in, ch_out, shortcut, groups, expansion
super(Bottleneck, self).__init__()
c_ = int(c2 * e) # hidden channels
self.cv1 = Conv(c1, c_, 1, 1)
self.cv2 = Conv(c_, c2, 3, 1, g=g)
self.add = shortcut and c1 == c2
def forward(self, x): # 如果shortcut并且输入输出通道相同则跳层相加
return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))
class C3(nn.Module):
# CSP Bottleneck with 3 convolutions
def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion
super(C3, self).__init__()
c_ = int(c2 * e) # hidden channels
self.cv1 = Conv(c1, c_, 1, 1)
self.cv2 = Conv(c1, c_, 1, 1)
self.cv3 = Conv(2 * c_, c2, 1) # act=FReLU(c2)
self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)]) # n个残差组件(Bottleneck)
# self.m = nn.Sequential(*[CrossConv(c_, c_, 3, 1, g, 1.0, shortcut) for _ in range(n)])
def forward(self, x):
return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), dim=1))
通过C3代码可以看出,对于cv1卷积和cv2卷积的通道数是一致的,而cv3的输入通道数是前者的2倍,因为cv3的输入是由主梯度流分支(BottleNeck分支)依旧次梯度流分支(CBS,cv2分支)cat得到的,因此是2倍的通道数,而输出则是一样的。
不妨我们再看一下YOLOv7中的模块:
YOLOv7通过并行更多的梯度流分支,放ELAN模块可以获得更丰富的梯度信息,进而获者更高的精度和更合理的延迟。
C2f模块就是参考了C3模块以及ELAN的思想进行的设计,让YOLOv8可以在保证轻量化的同时获得更加丰富的梯度流信息。
C2f模块的结构图如下:
C2f模块对应的Pytorch实现如下:
class C2f(nn.Module):
# CSP Bottleneck with 2 convolutions
def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion
super().__init__()
self.c = int(c2 * e) # hidden channels
self.cv1 = Conv(c1, 2 * self.c, 1, 1)
self.cv2 = Conv((2 + n) * self.c, c2, 1) # optional act=FReLU(c2)
self.m = nn.ModuleList(Bottleneck(self.c, self.c, shortcut, g, k=((3, 3), (3, 3)), e=1.0) for _ in range(n))
def forward(self, x):
y = list(self.cv1(x).split((self.c, self.c), 1))
y.extend(m(y[-1]) for m in self.m)
return self.cv2(torch.cat(y, 1))
SPPF改进
SPP结构又被称为空间金字塔池化,能将任意大小的特征图转换成固定大小的特征向量。
处理步骤:
输入层:首先我们现在有一张任意大小的图片,其大小为w * h。
输出层:21个神经元 – 即我们待会希望提取到21个特征。
分析如下图所示:分别对1 * 1分块,2 * 2分块和4 * 4子图里分别取每一个框内的max值(即取蓝框框内的最大值),这一步就是作最大池化,这样最后提取出来的特征值(即取出来的最大值)一共有1 * 1 + 2 * 2 + 4 * 4 = 21个。得出的特征再concat在一起。
YOLOv5中SPP的结构图如下图所示:
SPPF替换SPP,二者效果一致,但前者较后者的执行时间减少至1/2。
SPPF对应代码:
class SPPF(nn.Module):
# Spatial Pyramid Pooling - Fast (SPPF) layer for YOLOv5 by Glenn Jocher
def __init__(self, c1, c2, k=5): # equivalent to SPP(k=(5, 9, 13))
super().__init__()
c_ = c1 // 2 # hidden channels
self.cv1 = Conv(c1, c_, 1, 1)
self.cv2 = Conv(c_ * 4, c2, 1, 1)
self.m = nn.MaxPool2d(kernel_size=k, stride=1, padding=k // 2)
def forward(self, x):
x = self.cv1(x)
with warnings.catch_warnings():
warnings.simplefilter('ignore') # suppress torch 1.9.0 max_pool2d() warning
y1 = self.m(x)
y2 = self.m(y1)
return self.cv2(torch.cat([x, y1, y2, self.m(y2)], 1))
PAN-FPN改进
先看一下YOLOv5以及YOLOv6的PAN-FPN部分的结构图:
YOLOv5的Neck部分的结构图如下: YOLOv6的Neck部分的结构图如下:
YOLOv8的neck结构图:
可以看到:相对于YOLOv5或者YOLOv6,YOLOv8将C3模块以及RepBlock++替换++为了C2f,同时细心可以发现,相对于YOLOv5和YOLOv6,YOLOv8选择将上采样之前的1×1卷积去除了,将Backbone不同阶段输出的特征直接送入了上采样操作。
Head部分
先看一下YOLOv5本身的Head(Coupled-Head):
而YOLOv8则是使用了Decoupled-Head,同时由于使用了DFL 的思想,因此回归头的通道数也变成了4*reg_max的形式:
YOLOv8的head结构图:
对比一下YOLOv5与YOLOv8的YAML
损失函数
对于YOLOv8,其分类损失为VFL Loss,其回归损失为 CIOU Loss+ DFL 的形式,这里Reg_max默认为16。
VFL主要改进是提出了非对称的加权操作,FL 和 QFL 都是对称的。而非对称加权的思想来源于论文PISA,该论文指出首先正负样本有不平衡问题,即使在正样本中也存在不等权问题,因为mAP的计算是主正样本。
q是label,正样本时候q为bbox和gt的IoU,负样本时候q=0,当为正样本时候其实没有采用FL,而是普通的BCE,只不过多了一个自适应IoU加权,用于突出主样本。而为负样本时候就是标准的FL了。可以明显发现VFL比QFL更加简单,主要特点是正负样本非对称加权、突出正样本为主样本。
针对这里的DFL(Distribution Focal Loss),其主要是将框的位置建模成一个 general distribution,让网络快速地聚焦于和目标位置距离近的位置的分布。
DFL 能够让网络更快地聚焦于目标 y 附近的值,增大它们的概率;
DFL的含义是以交叉熵的形式去优化与标签y最接近的一左一右2个位置的概率,从而让网络更快地聚焦到目标位置的邻近区域的分布;也就是说学出来的分布理论上是在真实浮点坐标的附近,并且以线性插值的模式得到距离左右整数坐标的权重。
样本的匹配
标签分配是目标检测非常重要的一环,在YOLOv5的早期版本中使用了MaxIOU作为标签分配方法。然而,在实践中发现直接使用边长比也可以达到一样的效果。而YOLOv8则是抛弃了Anchor-Base方法使用Anchor-Free方法,找到了一个替代边长比例的匹配方法,TaskAligned。
为与NMS搭配,训练样例的Anchor分配需要满足以下两个规则:
1) 正常对齐的Anchor应当可以预测高分类得分,同时具有精确定位;
2) 不对齐的Anchor应当具有低分类得分,并在NMS阶段被抑制。
基于上述两个目标,TaskAligned设计了一个新的 Anchor alignment metric 来在Anchor level 中衡量 Task-Alignment的水平。并且,Alignment metric 被集成在了 sample 分配和 loss function里来动态地优化每个 Anchor 的预测。
Anchor alignment metric:
分类得分和 IoU表示了这两个任务的预测效果,所以,TaskAligned使用分类得分和IoU的高阶组合来衡量Task-Alignment的程度。
使用下列的方式来对每个实例计算Anchor-level 的对齐程度:
s 和 u 分别为分类得分和 IoU 值,α 和 β 为权重超参。从上边的公式可以看出来,t 可以同时控制分类得分和IoU 的优化来实现 Task-Alignment,可以引导网络动态地关注高质量的Anchor。
Training sample Assignment:
为提升两个任务的对齐性,TOOD聚焦于Task-Alignment Anchor,采用一种简单的分配规则选择训练样本:对每个实例,选择m个具有最大t值的Anchor作为正样本,选择其余的Anchor作为负样本。然后,通过损失函数(针对分类与定位的对齐而设计的损失函数)进行训练。