本系列文章记录本人硕士阶段YOLO系列目标检测算法自学及其代码实现的过程。其中算法具体实现借鉴于ultralytics YOLO源码Github,删减了源码中部分内容,满足个人科研需求。
本篇文章在YOLOv5
算法实现的基础上,进一步完成YOLOv8
算法的实现。YOLOv8
相比于YOLOv5
,最主要的不同之处如下:
模型结构
:将YOLOv5
中的CSP模块替换为C2f模块
,将Detect(耦合头 + Anchor-based)模块替换为Detect模块(解耦头 + Anchor-free + DFL)
正样本匹配
:采用TaskAlignedAssigner分配策略
损失计算
:
- 类别损失:二值交叉熵损失
- 位置损失:
Distribution Focal Loss(DFL) + CIOU Loss
- 置信度损失:
YOLOv8不预测模型的目标置信度,不再使用该损失
文章地址:
YOLOv8算法实现(一):模型搭建
YOLOv8算法实现(二):正样本匹配(TaskAlignedAssigner)与损失计算
本文目录
1 模型结构
YOLOv8
的模型结构如图1所示,其包含以下几个模块:
CBS
:卷积层、批标准化(BN)和SiLU激活函数C2f
:多梯度融合特征提取模块SPPF
:快速金字塔池化特征层Detect
:检测头(解耦头 + Anchor-free + Distribution)
2 模型模块实现(common.py)
2.1 C2f模块
class Bottleneck(nn.Module):
'''
残差连接瓶颈层, Residual block
'''
def __init__(self, c1, c2, shortcut=True, g=1, e=0.5, k=1):
'''
:param c1: 输入通道
:param c2: 输出通道
:param shortcut: 为True时采用残差连接
:param g: groups 在输出通道上分组, c2 // g 分组后不同组之间的卷积核参数不同
:param e: 中间层的通道数
'''
super(Bottleneck, self).__init__()
c_ = int(c2 * e) # 中间层的通道
self.cv1 = Conv(c1, c_, k, 1) # ch_in, ch_out, kereal_size, stride
self.cv2 = Conv(c_, c2, 3, 1, g=g)
self.add = shortcut and c1 == c2
def forward(self, x):
out = self.cv2(self.cv1(x))
return x + out if self.add else out
class C2f(nn.Module):
def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5):
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)
self.m = nn.ModuleList(Bottleneck(self.c, self.c, shortcut, e=1.0, k=3) 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))
2.2 Detect模块(解耦头 + Anchor-free + Distribution)
YOLOv8对结果的预测有如下特征:
- 基于不同分辨率的
特征图
实现对不同大小的目标预测; - 每张特征图以像素为单位为单位,对中心点落在该
像素单位
的目标进行预测,每个单位负责得到一个预测结果;
假设特征图数量为 n l nl nl,特征图中的分辨率为 ( g r i d _ x i , g r i d _ y i ) (grid\_xi,grid\_yi) (grid_xi,grid_y