【YOLO系列】YOLOv8理论
参考笔记:YOLOv8详解 【网络结构+代码+实操】-CSDN博客
文中的大多内容和图片引自该博客
学习视频:Enzo_Mi的个人空间-Enzo_Mi个人主页-哔哩哔哩视频的YOLOv8系列
2.1、yolov8原理引言_哔哩哔哩_bilibili
目录
1.YOLOv8概述
2.模型结构设计
2.1 Backbone和Neck的具体变化
2.2 Head的具体变化
3.Loss计算
4.训练数据增强
5.训练策略
6.模型推理过程
1.YOLOv8概述
YOLOv8 算法的核心特性和改动可以归结为如下:
-
Backbone:
backbone、neck 可能参考了 YOLOv7 ELAN 设计思想,将 YOLOv5 的 C3 结构换成了梯度流更丰富的 C2f 结构,并对不同大小模型(n、s、m、l、x)调整了不同的通道数
-
Head: Head 部分与 YOLOv5 相比有两大改进:
-
换成了主流的解耦头结构(Decouped-Head)将分类头和检测头分离
-
Anchor-Based --> Anchor-Free
-
-
Loss :正负样本匹配方式改变、移除置信度损失、增加Distribution Focal Loss(DFL)
-
Train:借鉴 YOLOX ,在训练阶段的最后 10 个 epoch 关闭 Mosiac 数据增强,可以有效地提升精度
2.模型结构设计
下图中左侧为 YOLOv5-n,右侧为 YOLOv8-n
在暂时不考虑 Head 情况下,对比 YOLOv5 和 YOLOv8 的 yaml 配置文件可以发现改动较小
YOLOv5-n、YOLOv8-n
YOLOv8 的网络结构如下图所示:
YOLOv8模型结构图1
YOLOv8模型结构图2
2.1 Backbone和Neck的具体变化
(1)第一个卷积层的 kernel_size 从 6x6 变成了 3x3
(2)所有的 C3 模块换成 C2f ,多了额外的 Split 操作和更多的跳层连接
C3、C2f对比
C2f 的代码实现在 ultralytics/nn/modules/block.py 的 Class C2f :
#C2f模块
class C2f(nn.Module):
def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5):
super().__init__()
'''
:params c1: in_channle
:params c2: out_channel
:params n: Bottleneck模块重复次数
:params shortcut: 是否开启残差连接,如果启用,则原始输入x会与经过两个卷积层之后的输出相加
:params g: 卷积的groups数 =1就是普通的卷积 >1分组卷积
:params e: 通道扩展比例,决定第一个卷积的输出通道数。扩展比例为'e',则第一个卷积的输出通道数为 `e * c2`
'''
#根据扩展比例e计算第一个CBS模块的输出通道数
self.c = int(c2 * e)
#Split之前的CBS模块:1x1卷积,将输入通道数 `c1` 缩减到 `c_`,用于降维
self.cv1 = Conv(c1, 2 * self.c, 1, 1)
#Concat之后的CBS模块:1x1卷积,将通道数缩减到 `c2`,用于特征提取
self.cv2 = Conv((2 + n) * self.c, c2, 1)
#n个Bottleneck模块,Bottleneck模块中的两个CBS模块的卷积核大小均为3x3
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):
'x:shape=[batch_size,channel,h,w]'
#1x1卷积
y=self.cv1(x)
'''Split,在y的第一个维度channel上分块,每个块的通道数为channel/2,所以
y[0] shape=[batch_size,channel/2,h,w]
y[1] shape=[batch_size,channel/2,h,w]'''
y=list(y.chunk(2, 1))
#接多个Bottleneck模块(每一次Bottleneck之后的输出要保存到y中)
y.extend(m(y[-1]) for m in self.m)
#将所有输出在通道维度上作concat,然后作最后一次卷积操作
return self.cv2(torch.cat(y, 1))
(3)去掉了 neck 中的2个卷积连接层
(4)backbone 的 c2f 模块中的 Bottleneck 重复次数从 3-6-9-3 -> 3-6-6-3
2.2 Head的具体变化
从原先的耦合头变成了解耦头,并且从 YOLOv5 的 Anchor-Based 变成了 Anchor-Free
关于 Anchor-Baesd 和 Anchor-Free 的对比和区别可以看我写的另一篇博客:
Anchor-Based 与 Anchor-Free-CSDN博客
下图是耦合头和解耦头的对比:
耦合头和解耦头对比
在 YOLOv8 中,网络输出不再有之前的 objectness(置信度) 分支,只有解耦的分类和回归分支,并且其回归分支使用了 Distribution Focal Loss 中提出的积分形式表示法。 下面是 YOLOv5 和 YOLOv8 的网络输出结果对比:
YOLOv5、YOLOv8网络输出结果对比
3.Loss计算
Loss 计算过程包括两个部分:正负样本匹配和损失计算
由于篇幅比较长,我写在了另外两篇博客中
正负样本匹配:【YOLOv8】正样本匹配-CSDN博客
损失计算:【YOLOv8】损失函数-CSDN博客
4.训练数据增强
数据增强方面和 YOLOv5 差距不大,只不过 YOLOv8 借鉴了 YOLOX,在最后 10 个 epoch 关闭 Mosaic 数据增强。假设训练 epoch 是 500 ,其示意图如下所示:
训练、测试、验证时的数据增强策略
5.训练策略
YOLOv8 的训练策略和 YOLOv5 没有太多区别,最大区别是模型的训练 epoch 数从 300 提升到了 500,这也导致训练时间急剧增加。以 YOLOv8 - S 为例,其训练策略汇总如下:
YOLOv8 - S 默认训练参数
6.模型推理过程
YOLOv8 的推理过程如下,特别注意需要对根据 DFL 损失函数设计的 4 个值各自对应的长度为 16 的概率分布进行解码,变成常规的坐标值形式 xyxy 或 xywh
YOLOv8推理过程
其推理和后处理过程为:
(1) 坐标值解码: 4 x 16 -> ->
(2) 维度变换
YOLOv8 输出特征图尺度为 80 x 80 、40 x 40 、20 x 20 的三个特征图。Head 部分输出分类和回归共 6 个尺度的特征图。 将 3 个不同尺度的类别预测分支、 bbox 预测分支进行拼接,并进行维度变换。类别预测分支和 bbox 预测分支 shape 分别为 (b,8400,80),(b,8400,4)。对于每一张图片, 8400 = anchor point = 预测框数量
(3) 解码还原到原图尺度
分类预测分支进行 Sigmoid 计算,而 bbox 预测分支将(1)得到 的坐标值还原到原始图像尺寸
(4) 阈值过滤
遍历 batch 中的每张图,对每张图的所有预测框使用置信度(使用预测框的最大类别概率作为预测框的置信度)阈值进行过滤。源码中过滤之后还需要考虑 nms_pre ,确保过滤后的预测框数不多于 nms_pre 。这样考虑的原因可能是防止 nms 处理时间太长
(5) nms
将(4)过滤后的预测框进行 nms 即可。最终每张图片输出的预测框不能多于 max_per_img