YOLOv8网络详解

本文详细介绍了YOLOv8算法的改进,包括CSP思想的C2F模块,弃用Anchor-Based采用Anchor-Free,使用Task-Aligned Assigner和Distribution Focal Loss。还解析了模型中的关键组件如Conv、ConvTranspose、DWConv和CBAM模块,以及训练策略上的优化。
摘要由CSDN通过智能技术生成

介绍

     YOLOv8 YOLOv5 的一个重大更新版本,目前支持图像分类、物体检测和实例分割任务,YOLOv8整体架构如下图所示

YOLOv8 算法的核心特性可以归结为如下:

  • Backbone:使用的依旧是CSP的思想,将YOLOv5中的C3模块被替换成了C2f模块,实现了进一步的轻量化,同时YOLOv8依旧使用了YOLOv5等架构中使用的SPPF模块;
  • Head: Head部分较YOLOv5而言有两大改进:1)换成了目前主流的解耦头结构(Decoupled-Head),将分类和检测头分离 2)同时也从 Anchor-Based 换成了 Anchor-Free;
  • Loss :YOLOv8抛弃了以往的IOU匹配或者单边比例的分配方式,而是使用了Task-Aligned Assigner正负样本匹配方式。并引入了 Distribution Focal Loss(DFL),整体使用DFL Loss+CIOU Loss作为分类损失
  • Anchor-Free YOLOv8 抛弃了以往的 Anchor-Base ,使用了 Anchor-Free 的思想;
  • Train: 训练的数据增强部分引入了 YOLOX 中的最后 10 epoch 关闭 Mosiac 增强的操作,可以有效地提升精度;
作为YOLOv8模型家族的yolov8n yolov8s yolov8m yolov8l yolov8x 等模型参数、 运行速度、参数量如下图所示:

模型部分解析

C3与C2F

YOLOv8 YOLOv5 中的 C3 模块换成了 C2F 模型,我们先了解一下 C3模块。C3模块由一个卷积块和一个跳跃连接组成,用于提取特征并保持分辨率。具体来说,C3模块包含三个连续的卷积层,每个卷积层后面跟着一个BatchNormalization层和一个Mish激活函数(或ReLU激活函数)。C3模块的设计旨在增加网络的感受野和表征能力,同时减少参数数量和计算量。 C3具体代码如下所示:
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().__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) # optional act=FReLU(c2)
        self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, k=((1, 1), (3,
3)), e=1.0) for _ in range(n)))
    def forward(self, x):
        return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), 1))

接着我们讲解C2F模块,C2F模块旨在从不同层次的特征图中提取信息,并将这些信息进行融合,以便提高目标检测的性能。具体来说,C2F模块包含一个跨阶段的特征融合操作,通过将来自不同层的特征图相加,实现特征的融合。这有助于网络更好地理解不同尺度的特征,并提高目标检测的准确性和鲁棒性,其结构图如下图所示,这个过程的描述如下:首先,输入特征经过一个卷积层,然后使用chunk函数将输出特征平均拆分成两个向量,并将它们保存到一个列表中。接着,列表中的后半部分向量被输入到一个Bottleneck Block中,该Block包含n个Bottleneck模块。每个Bottleneck模块的输出都被追加到列表中,最终列表中包含n个Bottleneck模块的输出。这些输出被拼接起来,形成一个输出向量。最后,输出向量经过一个卷积层,输出大小为h×w×c_out。

autopad

autopad函数用于计算填充的大小,以使得经过填充后的输出张量大小保持不变。该函数接受三个参数:

  • k:卷积核的大小,可以是一个整数或一个序列。
  • p:填充的大小,默认为None。
  • d:扩张率的大小,默认为1。普通卷积的扩张率为1,而空洞卷积的扩张率大于1。

autopad函数代码如下所示:

def autopad(k, p=None, d=1):  # kernel(卷积核), padding(填充), dilation(扩张)
    # 返回pad的大小,使得padding后输出张量的shape不变
    if d > 1: # 如果采用扩张卷积,则计算扩张后实际的kernel大小
        k = d * (k - 1) + 1 if isinstance(k, int) else [d * (x - 1) + 1 for x in k]  # 
    if p is None:
        p = k // 2 if isinstance(k, int) else [x // 2 for x in k]  # 自动pad
    return p

Conv

在YOLOv8中,Conv作为卷积层(Convolutional Layer)是通过卷积操作对输入特征图进行处理的一种神经网络层。卷积操作可以看作是一个滤波器,在特征图上滑动并对每个位置的像素值进行加权求和,从而生成输出特征图。卷积操作可以有效地提取图像中的局部特征,例如边缘、纹理等,从而帮助网络理解图像内容,具体代码如下所示:

class Conv(nn.Module):
    # 标准的卷积 参数(输入通道数, 输出通道数, 卷积核大小, 步长, 填充, 组, 扩张, 激活函数)
    default_act = nn.SiLU()  # 默认的激活函数

    def __init__(self, c1, c2, k=1, s=1, p=None, g=1, d=1, act=True):
        super().__init__()
        self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p, d), groups=g, dilation=d, bias=False) # 2维卷积,其中采用了自动填充函数。
        self.bn = nn.BatchNorm2d(c2) # 使得每一个batch的特征图均满足均值为0,方差为1的分布规律
        # 如果act=True 则采用默认的激活函数SiLU;如果act的类型是nn.Module,则采用传入的act; 否则不采取任何动作 (nn.Identity函数相当于f(x)=x,只用做占位,返回原始的输入)。
        self.act = self.default_act 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))) # 采用BatchNorm
    def forward_fuse(self, x): #  用于Model类的fuse函数融合 Conv + BN 加速推理,一般用于测试/验证阶段
        return self.act(self.conv(x)) # 不采用BatchNorm

ConvTranspose

ConvTranspose(转置卷积)层是卷积神经网络中的一种操作,也称为反卷积。它与普通的卷积操作相反,用于将输入特征图放大(上采样)并生成更大尺寸的输出特征图。转置卷积通过学习可训练的参数(称为卷积核或滤波器)来实现这一目的。转置卷积的工作原理是在输入特征图中的每个像素周围填充零,并对其进行卷积操作。这使得输出特征图的尺寸比输入特征图大,其代码如下所示:

class ConvTranspose(nn.Module):
    # Convolution transpose 2d layer
    default_act = nn.SiLU()  # default activation

    def __init__(self, c1, c2, k=2, s=2, p=0, bn=True, act=True):
        super().__init__()
        self.conv_transpose = nn.ConvTranspose2d(c1, c2, k, s, p, bias=not bn)
        self.bn = nn.BatchNorm2d(c2) if bn else nn.Identity()
        self.act = self.default_act 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_transpose(x)))

DWConv

DWConv指深度可分离卷积(Depthwise Separable Convolution),是一种轻量级的卷积操作,常用于深度学习模型中以减少参数数量和计算量。深度可分离卷积由两部分组成:深度卷积(Depthwise Convolution)和逐点卷积(Pointwise Convolution)。

  • 深度卷积:对输入的每个通道分别应用一个独立的卷积核。如果输入有C个通道,那么将会有C个卷积核,每个卷积核只与对应通道的数据进行卷积操作。

  • 逐点卷积:对深度卷积的输出应用1x1的卷积核,目的是将通道数减少到所需的数量,以减少计算量。

详细代码如下所示:

class DWConv(Conv):
    # 深度可分离卷积
    def __init__(self, c1, c2, k=1, s=1, d=1, act=True):  # ch_in, ch_out, kernel, stride, dilation, activation
        super().__init__(c1, c2, k, s, g=math.gcd(c1, c2), d=d, act=act)

DWConvTranspose2d

DWConvTranspose2d指深度可分离转置卷积(Depthwise Separable Transpose Convolution),用于上采样(放大)特征图。深度可分离转置卷积结合了深度可分离卷积和转置卷积的特性,以减少参数数量和计算量。

深度可分离转置卷积包括两个步骤:

  • 深度可分离卷积(Depthwise Separable Convolution):首先,对输入的每个通道分别应用一个独立的卷积核(深度卷积)。这一步与常规的深度可分离卷积操作相同。

  • 转置卷积(Transpose Convolution):然后,对深度可分离卷积的输出进行逐点卷积(逐点卷积)。逐点卷积使用1x1的卷积核对每个通道的输出进行线性变换和融合,以恢复输出特征图的尺寸。

详细代码如下所示:

class DWConvTranspose2d(nn.ConvTranspose2d):
    # Depth-wise transpose convolution
    def __init__(self, c1, c2, k=1, s=1, p1=0, p2=0):  # 输入通道, 输出通道, 卷积核大小, 步长, padding, padding_out
        super().__init__(c1, c2, k, s, p1, p2, groups=math.gcd(c1, c2))

Bottleneck

Bottleneck的设计旨在减少模型的参数数量和计算量,同时提高网络的表达能力,先使用 3x3 卷积降维,剔除冗余信息,再使用 3×3 卷积升维,其具体代码如下:

class Bottleneck(nn.Module):
    # Standard bottleneck
    def __init__(self, c1, c2, shortcut=True, g=1, k=(3, 3), e=0.5):  # ch_in, ch_out, shortcut, groups, kernels, expand
        super().__init__()
        c_ = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, c_, k[0], 1)  # 输入通道: c1, 输出通道:c_ , 卷积核:3x3, 步长1
        self.cv2 = Conv(c_, c2, k[1], 1, g=g) # 输入通道:c_ , 输出通道c2, 卷积核:3x3, 步长1
        self.add = shortcut and c1 == c2  # 当传入的shortcut参数为true,且c1和c2相等时,则使用残差连接。

    def forward(self, x):
        return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))

BottleneckCSP

BottleneckCSP结合了Bottleneck和Cross Stage Partial连接(CSP),使得其可以在保持模型轻量化的同时,提高网络的感知能力和特征表达能力,代码如下:

class BottleneckCSP(nn.Module):
    # CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks
    def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):  # ch_in, ch_out, number, shortcut, groups, expansion
        super().__init__()  
        c_ = int(c2 * e)  # hidden channels
        # 输出x的大小是(b,c1,w,h)
        self.cv1 = Conv(c1, c_, 1, 1) # cv1的大小为(b,c_,w,h)
        self.cv2 = nn.Conv2d(c1, c_, 1, 1, bias=False) # cv2的大小为(b,c_,w,h)
        self.cv3 = nn.Conv2d(c_, c_, 1, 1, bias=False) # m通过Conv2d,变成cv3,大小是(b,c_,w,h)
        self.cv4 = Conv(2 * c_, c2, 1, 1)
        self.bn = nn.BatchNorm2d(2 * c_)  # applied to cat(cv2, cv3)
        self.act = nn.SiLU()
        self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)))  
        # cv1通过n个串联的bottleneck,变成m,大小为(b,c_,w,h)

    def forward(self, x):
        y1 = self.cv3(self.m(self.cv1(x))) # (b,c_,w,h)
        y2 = self.cv2(x) # (b,c_,w,h)
        return self.cv4(self.act(self.bn(torch.cat((y1, y2), 1))))
        # cat后:(b,2*c_,w,h) 返回cv4: (b,c2,w,h)

CBAM

CBAM模块通常由两个子模块组成:通道注意力模块(Channel Attention Module)和空间注意力模块(Spatial Attention Module)。

  • 通道注意力模块:用于对每个通道的特征图进行加权,以提取重要的通道信息。通道注意力模块通过计算每个通道的全局平均池化和全局最大池化,然后经过一系列全连接层和激活函数,最终产生一个通道注意力权重向量,用于加权每个通道的特征图。

  • 空间注意力模块:用于对特征图的空间位置进行加权,以提取重要的空间信息。空间注意力模块通过对特征图进行一系列卷积操作和激活函数,最终产生一个空间注意力权重图,用于加权特征图的每个空间位置。

CBAM模块将通道注意力和空间注意力结合起来,通过对特征图的通道和空间位置进行加权,提高了网络对重要特征的关注程度,从而增强了网络的表达能力和泛化能力,代码如下:

class CBAM(nn.Module):
    # Convolutional Block Attention Module
    def __init__(self, c1, kernel_size=7):  # ch_in, kernels
        super().__init__()
        self.channel_attention = ChannelAttention(c1)
        self.spatial_attention = SpatialAttention(kernel_size)

    def forward(self, x):
        return self.spatial_attention(self.channel_attention(x))

结语

后期将会对这篇文章进行进一步完善,同时更新YOLOv8实现教程。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

&瞎学的小曹ζ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值