赞
踩
目录
YOLOv8 目前支持图像分类、物体检测和实例分割任务
1.提供了一个全新的 SOTA 模型,包括 P5 640 和 P6 1280 分辨率 的目标检测网络和基于 YOLACT 的实例分割模型。和 YOLOv5 一样,基于缩放系数也提供了 N/S/M/L/X 尺度的不同大小模型,用于满足不同场景需求
2.骨干网络和 Neck 部分可能参考了 YOLOv7 ELAN 设计思想,将 YOLOv5 的 C3 结构换成了梯度流更丰富的 C2f 结构,并对不同尺度模型调整了不同的通道数,属于对模型结构精心微调,不再是无脑一套参数应用所有模型,大幅提升了模型性能
3.Head 部分换成了目前主流的解耦头结构,将分类和检测头分离,同时也从 Anchor-Based 换成了 Anchor-Free
4.Loss 计算方面采用了 TaskAlignedAssigner 正样本分配策略,并引入了 Distribution Focal Loss
5.训练的数据增强部分引入了 YOLOX 中的最后 10 epoch 关闭 Mosiac 增强的操作,可以有效地提升精度
YOLOv8 主要参考了最近提出的诸如 YOLOX、YOLOv6、YOLOv7 和 PPYOLOE 等算法的相关设计,本身的创新点不多,偏向工程实践,主推的还是 ultralytics 这个框架本身
推理的预处理就是熟悉的letterbox(根据参数配置可以为不同的缩放填充模式,主要用于resize到640)+ 转换rgb、chw、int8(0-255)->float(0-1),注意没有归一化操作。
训练的预处理可选项较多,官网下载的代码中参考配置文件:ultralytics/cfg/default.yaml
(1)连续使用两个3*3卷积直接降低了4倍分辨率。这个是真猛,敢在如此小的感受野下连续两次仅仅用一层卷积就下采样。当然作为代价它的特征图还是比较厚的分别为64、128。
(2)c2f 模块
这个其实也就是仿照YOLOv7 的ELAN 结构,通过更多的分支夸层链接,丰富了模型的梯度流。
(3)sppf 模块
对比spp,将简单的并行max pooling 改为串行+并行的方式。
- 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))
总体来说neck起到一个特征融合的作用,head用于检测
3个特点:
(1)decoupled head
这个解耦操作,看YOLO x 的论文,约有1% 的提升。
(2)obj 分支消失
(3)anchor free
概念:在目标定位任务里,由于目标的大小、尺寸都是不固定的,因此需要另一个框来预测目标的大概形状。
深度学习中常见的方法是:将整个图分为多个区域,依次判断各个区域有无目标、目标大小、目标尺寸等。
这些多个小区域被称为Anchor——即锚框,用以锚定实际的预测框。
Anchor-Free在训练中直接学习各种框形状。推理时不依靠聚类,而是根据学习到的边框距离/关键点位置,拟合物体尺寸。
A. Center-based methods——基于中心点的方法。
先找中心/中心区域,再预测中心到四条边的距离。
Anchor-Free通过不依赖数据集中的先验知识,使网络对“物体形状”有更好的表达能力,更具泛化潜能。在运动物体、尺寸不一物体检测上有所提升,同时检测被遮挡物体时也能更加灵活。
B.正负样本分配策略
在目标定位任务里,目标就是我们的正样本,无目标区域就是负样本,正样本总很少,负样本(即无目标区域)总太多。会导致模型学习不均衡。因此,我们需要正负样本分配策略来解决这一问题
现代目标检测器大部分都会在正负样本分配策略上面做文章,典型的如 YOLOX 的 simOTA、TOOD 的 TaskAlignedAssigner 和 RTMDet 的 DynamicSoftLabelAssigner,YOLOv8 算法中则直接引用了 TOOD 的 TaskAlignedAssigner,这类 Assigner 大都是动态分配策略。
动态分配策略特点:
动态分配策略可根据训练损失或其他指标来调整,可更好地适应不同的数据集和模型。
Task alignment learning(TAL)翻译过来叫做任务对齐学习
TAL核心公式——“anchor质量计算式”:用分类质量与回归质量加权乘积描述anchor的预测质量。通过训练,t可以引导网络动态的关注于高质量的正样本
TAL实现动态分配的逻辑——使用分类得分和IoU的组合来衡量任务对齐的程度。
使用上面公式对每个GT框实例计算Anchor-level 的对齐程度:s 和 u 分别为分类得分和 IoU 值,α 和 β 为权重超参(v8默认值α=0.5、β=6.0)。
t 可同时通过分类得分和IoU 的优化来实现任务对齐,可引导网络动态的关注于高质量的Anchor预测框。
采用一种简单的分配规则选择训练样本:对每个GT框实例,选择m个具有最大t 值的Anchor作为正样本,选择其余的Anchor作为负样本。
然后,通过损失函数(针对分类与定位的对齐而设计的损失函数)进行训练。
TAL分配样本的具体流程:
①预处理:
对类别、边框、特征图各anchor中心点进行处理
类别:进行sigmoid处理
边框:还原到网络输入尺度
中心:还原到网络输入尺度
对标签数据也做同样处理,开始做正样筛选
②初筛:对所有中心点在GT框中的anchor作为初步正样本。
③精筛:计算上一步中anchor的对齐分数alignment_metrics(即t),选择分数最高的m个候选框(默认为10)作为正样本。
④去除重复分配:通过mask矩阵与anchor求和,找出分给多个GT框的anchor,只留下CIOU值大的anchor。
⑤获得每样本对应的训练标签:通过上述步骤得到的掩码——anchor的正负样本、GT对应的正负样本、GT对应的正样本anchor索引,得到筛选出的正样本训练信息。
Loss 计算包括 2 个分支:分类分支依然采用 BCE Loss。回归分支使用了 Distribution Focal Loss(DFL Reg_max默认为16)+ CIoU Loss。
3 个 Loss 采用一定权重比例加权。
DFL损失。
目前被广泛使用的bbox表示可以看作是对bbox方框坐标建模了单一的狄拉克分布。但是在复杂场景中,一些检测对象的边界并非十分明确。如下图左面所示,对于滑板左侧被水花模糊,引起对左边界的预测分布是任意而扁平的,对右边界的预测分布是明确而尖锐的。对于这个问题,有学者提出直接回归一个任意分布来建模边界框,使用softmax实现离散的回归,将狄拉克分布的积分形式推导到一般形式的积分形式来表示边界框。
狄拉克分布可以认为在一个点概率密度为无穷大,其他点概率密度为0,这是一种极端地认为离散的标签时绝对正确的。
因为标签是一个离散的点,如果把标签认为是绝对正确的目标,那么学习出的就是狄拉克分布,概率密度是一条尖锐的竖线。然而真实场景,物体边界并非是十分明确的,因此学习一个宽范围的分布更为合理。我们需要获得的分布虽然不再像狄拉克分布那么极端(只存在标签值),但也应该在标签值附近。因此学者提出Distribution Focal Loss损失函数,目的让网络快速聚焦到标签附近的数值,是标签处的概率密度尽量大。思想是使用交叉熵函数,来优化标签y附近左右两个位置的概率,是网络分布聚焦到标签值附近。如下公式。Si 是网络的sigmod 输出,yi 和 yi+1 是上图的区间顺序,y是label 值。
- class DFL(nn.Module):
- # Integral module of Distribution Focal Loss (DFL) proposed in Generalized Focal Loss https://ieeexplore.ieee.org/document/9792391
- def __init__(self, c1=16):
- super().__init__()
- self.conv = nn.Conv2d(c1, 1, 1, bias=False).requires_grad_(False)
- x = torch.arange(c1, dtype=torch.float)
- self.conv.weight.data[:] = nn.Parameter(x.view(1, c1, 1, 1))
- self.c1 = c1
-
- def forward(self, x):
- b, c, a = x.shape # batch, channels, anchors
- return self.conv(x.view(b, 4, self.c1, a).transpose(2, 1).softmax(1)).view(b, 4, a)
- # return self.conv(x.view(b, self.c1, 4, a).softmax(1)).view(b, 4, a)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。