【5】YOLOv10更换空间金字塔池化模块 【手把手教学】【先进模块随心选】

【YOLOv10改进实战】**【5】YOLOv10更换空间金字塔池化模块 【手把手教学】【先进模块随心选】🥤🥤



提示:喜欢本专栏的小伙伴,请多多点赞关注支持。本文仅供学习使用,创作不易,未经作者允许,不得搬运或转载!!!


前言🛩️🛩️

本文主要介绍的是如何改进替换YOLOv10的空间金字塔池化模块,提供了9种先进模块供大家参考,使用时可据自己的实际情况进行选择。


一、空间金字塔池化模块介绍🛩️🛩️

1、SPP(Spatial Pyramid Pooling)🌱🌱

Spatial Pyramid Pooling (SPP)模块由何恺明等人于2014年的论文《Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition》中提出,旨在解决传统卷积神经网络(CNN)在处理变尺寸输入图像时的限制。

  • SPP模块的核心思想:通过引入空间金字塔池化,能够生成固定长度的输出,无论输入图像的尺寸如何。

SPP模块主要解决的问题:

  1. 输入图像尺寸的固定问题
    传统卷积神经网络(CNN)要求输入图像具有固定的尺寸。例如,在训练和测试过程中,输入图像的大小通常需要调整为相同的尺寸。然而,在实际应用中,图像的尺寸是多变的。如果强行对图像进行裁剪或缩放,可能会导致图像信息的丢失或失真,从而影响模型的性能。
  2. 多尺度信息的捕捉
    自然图像中的物体可以具有不同的尺度和位置,传统的固定大小的池化层无法充分捕捉这些多尺度信息。SPP通过在不同尺度上进行池化,能够更好地捕捉到图像中的多尺度特征,增强了模型对图像内容的理解能力。
  3. 特征表示的固定长度问题
    为了适应不同尺寸的输入图像,传统CNN在特征提取后,往往需要将特征映射调整为固定长度的向量表示。然而,直接通过裁剪或缩放等方式来实现这一目标,可能会导致特征信息的损失或变形。SPP通过多尺度池化,自然地生成固定长度的特征向量表示,无需对输入图像进行复杂的预处理。
  4. 预处理复杂性问题
    传统方法需要对输入图像进行预处理以确保尺寸统一,这不仅增加了处理的复杂性,还可能引入额外的误差。SPP模块通过其灵活的池化机制,能够直接处理不同尺寸的输入图像,简化了预处理过程,减少了图像信息的丢失和变形。

SPP模块的优势:

  1. 适应任意尺寸输入:SPP模块能够处理不同尺寸的输入图像,并将其转换为固定长度的表示,解决了传统CNN输入尺寸固定的问题。
  2. 捕捉多尺度信息:通过在不同尺度上进行池化,SPP模块能够捕捉到图像中的多尺度信息,提高了模型对图像内容的理解能力。
  3. 增强特征表示:SPP模块通过金字塔结构,提供了更丰富的特征表示,增强了模型的表达能力和分类性能。
  4. 简化处理流程:无需对输入图像进行裁剪或缩放,简化了预处理流程,减少了图像信息的丢失或失真。
    在这里插入图片描述

在这里插入图片描述

class SPP(nn.Module):
    """Spatial Pyramid Pooling (SPP) layer https://arxiv.org/abs/1406.4729."""
    def __init__(self, c1, c2, k=(5, 9, 13)):
        """Initialize the SPP layer with input/output channels and pooling kernel sizes."""
        super().__init__()
        c_ = c1 // 2  # hidden channels
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c_ * (len(k) + 1), c2, 1, 1)
        self.m = nn.ModuleList([nn.MaxPool2d(kernel_size=x, stride=1, padding=x // 2) for x in k])
    def forward(self, x):
        """Forward pass of the SPP layer, performing spatial pyramid pooling."""
        x = self.cv1(x)
        return self.cv2(torch.cat([x] + [m(x) for m in self.m], 1))

2、SPPF(Spatial Pyramid Pooling-fast)🌱🌱

SPPF模块时YOLOv5的作者Glenn jocher等人基于SPP模块提出的, 通过进一步简化和优化池化过程,减少了计算开销,同时保持了对多尺度特征的捕捉能力。
主要特点

  1. 优化的池化过程:SPPF 对传统 SPP 模块中的池化步骤进行了优化,减少了冗余计算和内存使用,提高了处理速度。
  2. 固定长度输出:SPPF 能够处理任意尺寸的输入图像,并生成固定长度的特征表示,适用于各种卷积神经网络结构。
  3. 多尺度特征表示:通过多尺度池化,SPPF 继续保留了捕捉图像中多尺度特征的能力,提高了模型对图像内容的理解能力。
  4. 计算效率提升:相比传统 SPP 模块,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))


3、SimSPPF(Simplified SPPF)🌱🌱

简化空间金字塔池化(simSPPF)模块源于YOLOv6,simSPPF 主要是将SPPF模块中的SiLU激活函数替换为ReLU激活函数,减少了计算开销,并保持了对多尺度特征的捕捉能力。

主要特点

  1. 高效计算:通过简化池化过程,减少计算量和内存使用,适应实时处理需求。
  2. 多尺度特征捕捉:通过不同尺度的池化,捕捉图像中的多尺度信息,增强特征表示能力。
  3. 固定长度输出:无论输入图像尺寸如何,输出特征的长度固定,便于后续处理。
    在这里插入图片描述
class SimConv(nn.Module):
    '''Normal Conv with ReLU activation'''
    def __init__(self, in_channels, out_channels, kernel_size, stride, groups=1, bias=False):
        super().__init__()
        padding = kernel_size // 2
        self.conv = nn.Conv2d(
            in_channels,
            out_channels,
            kernel_size=kernel_size,
            stride=stride,
            padding=padding,
            groups=groups,
            bias=bias,
        )
        self.bn = nn.BatchNorm2d(out_channels)
        self.act = nn.ReLU()

    def forward(self, x):
        return self.act(self.bn(self.conv(x)))

    def forward_fuse(self, x):
        return self.act(self.conv(x))

class SimSPPF(nn.Module):
    '''Simplified SPPF with ReLU activation'''
    def __init__(self, in_channels, out_channels, kernel_size=5):
        super().__init__()
        c_ = in_channels // 2  # hidden channels
        self.cv1 = SimConv(in_channels, c_, 1, 1)
        self.cv2 = SimConv(c_ * 4, out_channels, 1, 1)
        self.m = nn.MaxPool2d(kernel_size=kernel_size, stride=1, padding=kernel_size // 2)

    def forward(self, x):
        x = self.cv1(x)
        with warnings.catch_warnings():
            warnings.simplefilter('ignore')
            y1 = self.m(x)
            y2 = self.m(y1)
            return self.cv2(torch.cat([x, y1, y2, self.m(y2)], 1))


4、ASPP(AtrousSpatial Pyramid Pooling)🌱🌱

Atrous Spatial Pyramid Pooling (ASPP) 是一种用于图像分割任务的深度学习模块,旨在捕捉不同尺度的特征,从而更好地处理目标在图像中的各种大小和形状。ASPP模块被广泛应用于各种深度学习的图像分割任务中,如DeepLab系列中的DeepLabV3和DeepLabV3+等,用于目标检测任务也有着不错的效果。

ASPP的优点:

  1. 多尺度感知:能够在不同的尺度上捕捉特征,增强模型对大小不同物体的识别能力。
  2. 有效的上下文信息建模:通过不同空洞率的卷积核,能够有效地捕捉到远距离的上下文信息,提高分割的准确度和鲁棒性。
  3. 计算效率:通过引入空洞卷积,ASPP可以在不显著增加计算复杂度的情况下,扩大感受野。
    在这里插入图片描述
# without BN version
class ASPP(nn.Module):
    def __init__(self, in_channel=512, out_channel=256):
        super(ASPP, self).__init__()
        self.mean = nn.AdaptiveAvgPool2d((1, 1))  # (1,1)means ouput_dim
        self.conv = nn.Conv2d(in_channel,out_channel, 1, 1)
        self.atrous_block1 = nn.Conv2d(in_channel, out_channel, 1, 1)
        self.atrous_block6 = nn.Conv2d(in_channel, out_channel, 3, 1, padding=6, dilation=6)
        self.atrous_block12 = nn.Conv2d(in_channel, out_channel, 3, 1, padding=12, dilation=12)
        self.atrous_block18 = nn.Conv2d(in_channel, out_channel, 3, 1, padding=18, dilation=18)
        self.conv_1x1_output = nn.Conv2d(out_channel * 5, out_channel, 1, 1)

    def forward(self, x):
        size = x.shape[2:]

        image_features = self.mean(x)
        image_features = self.conv(image_features)
        image_features = F.upsample(image_features, size=size, mode='bilinear')

        atrous_block1 = self.atrous_block1(x)
        atrous_block6 = self.atrous_block6(x)
        atrous_block12 = self.atrous_block12(x)
        atrous_block18 = self.atrous_block18(x)

        net = self.conv_1x1_output(torch.cat([image_features, atrous_block1, atrous_block6,
                                              atrous_block12, atrous_block18], dim=1))
        return net


5、SPPCSPC🌱🌱

SPPCSPC 模块是YOLOv7使用的空间金字塔池化结构,结合了空间金字塔池化(SPP)和 Cross Stage Partial Networks(CSP)的设计理念,用于提高特征提取的多样性和融合能力,适合处理复杂的目标检测任务。这里提供了两个版本的SPPCSPC模块供大家选择。

1)SPPCSPC
在这里插入图片描述

class SPPCSPC(nn.Module):
    # CSP https://github.com/WongKinYiu/CrossStagePartialNetworks
    def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5, k=(5, 9, 13)):
        super(SPPCSPC, self).__init__()
        c_ = int(2 * c2 * e)  # hidden channels
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c1, c_, 1, 1)
        self.cv3 = Conv(c_, c_, 3, 1)
        self.cv4 = Conv(c_, c_, 1, 1)
        self.m = nn.ModuleList([nn.MaxPool2d(kernel_size=x, stride=1, padding=x // 2) for x in k])
        self.cv5 = Conv(4 * c_, c_, 1, 1)
        self.cv6 = Conv(c_, c_, 3, 1)
        self.cv7 = Conv(2 * c_, c2, 1, 1)

    def forward(self, x):
        x1 = self.cv4(self.cv3(self.cv1(x)))
        y1 = self.cv6(self.cv5(torch.cat([x1] + [m(x1) for m in self.m], 1)))
        y2 = self.cv2(x)
        return self.cv7(torch.cat((y1, y2), dim=1))

2) 分组SPPCSPC

class SPPCSPC_group(nn.Module):
    def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5, k=(5, 9, 13)):
        super(SPPCSPC_group, self).__init__()
        c_ = int(2 * c2 * e)  # hidden channels
        self.cv1 = Conv(c1, c_, 1, 1, g=4)
        self.cv2 = Conv(c1, c_, 1, 1, g=4)
        self.cv3 = Conv(c_, c_, 3, 1, g=4)
        self.cv4 = Conv(c_, c_, 1, 1, g=4)
        self.m = nn.ModuleList([nn.MaxPool2d(kernel_size=x, stride=1, padding=x // 2) for x in k])
        self.cv5 = Conv(4 * c_, c_, 1, 1, g=4)
        self.cv6 = Conv(c_, c_, 3, 1, g=4)
        self.cv7 = Conv(2 * c_, c2, 1, 1, g=4)

    def forward(self, x):
        x1 = self.cv4(self.cv3(self.cv1(x)))
        y1 = self.cv6(self.cv5(torch.cat([x1] + [m(x1) for m in self.m], 1)))
        y2 = self.cv2(x)
        return self.cv7(torch.cat((y1, y2), dim=1))


6、SPPFCSPC🌱🌱

该模块源于YOLOv6 3.0版本,在SPPCSPC模块的基础上改进而来。速度较SPPCSPC模块有所提升。
在这里插入图片描述

class SPPFCSPC(nn.Module):
    def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5, k=5):
        super(SPPFCSPC, self).__init__()
        c_ = int(2 * c2 * e)  # hidden channels
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c1, c_, 1, 1)
        self.cv3 = Conv(c_, c_, 3, 1)
        self.cv4 = Conv(c_, c_, 1, 1)
        self.m = nn.MaxPool2d(kernel_size=k, stride=1, padding=k // 2)
        self.cv5 = Conv(4 * c_, c_, 1, 1)
        self.cv6 = Conv(c_, c_, 3, 1)
        self.cv7 = Conv(2 * c_, c2, 1, 1)

    def forward(self, x):
        x1 = self.cv4(self.cv3(self.cv1(x)))
        x2 = self.m(x1)
        x3 = self.m(x2)
        y1 = self.cv6(self.cv5(torch.cat((x1,x2,x3, self.m(x3)),1)))
        y2 = self.cv2(x)
        return self.cv7(torch.cat((y1, y2), dim=1))


7、RFB(Recptive Field Block)🌱🌱

RFB模块是在《Receptive Field Block Net for Accurate and Fast Object Detection》一文中提出的,该模块旨在通过模拟生物视觉系统的感受野特性,提高目标检测的准确性和效率。该模块通过多尺度特征提取和特征融合,显著增强了模型的感受野和特征表达能力,适用于目标检测等需要处理不同尺度目标的任务。
在这里插入图片描述

class BasicConv(nn.Module):

    def __init__(self, in_planes, out_planes, kernel_size, stride=1, padding=0, dilation=1, groups=1, relu=True, bn=True):
        super(BasicConv, self).__init__()
        self.out_channels = out_planes
        if bn:
            self.conv = nn.Conv2d(in_planes, out_planes, kernel_size=kernel_size, stride=stride, padding=padding, dilation=dilation, groups=groups, bias=False)
            self.bn = nn.BatchNorm2d(out_planes, eps=1e-5, momentum=0.01, affine=True)
            self.relu = nn.ReLU(inplace=True) if relu else None
        else:
            self.conv = nn.Conv2d(in_planes, out_planes, kernel_size=kernel_size, stride=stride, padding=padding, dilation=dilation, groups=groups, bias=True)
            self.bn = None
            self.relu = nn.ReLU(inplace=True) if relu else None

    def forward(self, x):
        x = self.conv(x)
        if self.bn is not None:
            x = self.bn(x)
        if self.relu is not None:
            x = self.relu(x)
        return x

class BasicRFB(nn.Module):

    def __init__(self, in_planes, out_planes, stride=1, scale=0.1, map_reduce=8, vision=1, groups=1):
        super(BasicRFB, self).__init__()
        self.scale = scale
        self.out_channels = out_planes
        inter_planes = in_planes // map_reduce

        self.branch0 = nn.Sequential(
            BasicConv(in_planes, inter_planes, kernel_size=1, stride=1, groups=groups, relu=False),
            BasicConv(inter_planes, 2 * inter_planes, kernel_size=(3, 3), stride=stride, padding=(1, 1), groups=groups),
            BasicConv(2 * inter_planes, 2 * inter_planes, kernel_size=3, stride=1, padding=vision, dilation=vision, relu=False, groups=groups)
        )
        self.branch1 = nn.Sequential(
            BasicConv(in_planes, inter_planes, kernel_size=1, stride=1, groups=groups, relu=False),
            BasicConv(inter_planes, 2 * inter_planes, kernel_size=(3, 3), stride=stride, padding=(1, 1), groups=groups),
            BasicConv(2 * inter_planes, 2 * inter_planes, kernel_size=3, stride=1, padding=vision + 2, dilation=vision + 2, relu=False, groups=groups)
        )
        self.branch2 = nn.Sequential(
            BasicConv(in_planes, inter_planes, kernel_size=1, stride=1, groups=groups, relu=False),
            BasicConv(inter_planes, (inter_planes // 2) * 3, kernel_size=3, stride=1, padding=1, groups=groups),
            BasicConv((inter_planes // 2) * 3, 2 * inter_planes, kernel_size=3, stride=stride, padding=1, groups=groups),
            BasicConv(2 * inter_planes, 2 * inter_planes, kernel_size=3, stride=1, padding=vision + 4, dilation=vision + 4, relu=False, groups=groups)
        )

        self.ConvLinear = BasicConv(6 * inter_planes, out_planes, kernel_size=1, stride=1, relu=False)
        self.shortcut = BasicConv(in_planes, out_planes, kernel_size=1, stride=stride, relu=False)
        self.relu = nn.ReLU(inplace=False)

    def forward(self, x):
        x0 = self.branch0(x)
        x1 = self.branch1(x)
        x2 = self.branch2(x)

        out = torch.cat((x0, x1, x2), 1)
        out = self.ConvLinear(out)
        short = self.shortcut(x)
        out = out * self.scale + short
        out = self.relu(out)

        return out


8、SPPELAN🌱🌱

SPPELAN在YOLOv9中提出的最新的空间金字塔池化结构,是一个集成了空间金字塔池化(SPP)和扩展残差网络(ELAN)设计理念的模块。模块通过多尺度池化和特征融合的设计,有效增强了特征提取和表达能力。其模块化的结构设计,使得它在提高检测精度的同时,保持了计算效率的优势,非常适用于需要处理多尺度目标的目标检测任务。
在这里插入图片描述

class SPPELAN(nn.Module):
    """SPP-ELAN."""
    def __init__(self, c1, c2, c3, k=5):
        """Initializes SPP-ELAN block with convolution and max pooling layers for spatial pyramid pooling."""
        super().__init__()
        self.c = c3
        self.cv1 = Conv(c1, c3, 1, 1)
        self.cv2 = nn.MaxPool2d(kernel_size=k, stride=1, padding=k // 2)
        self.cv3 = nn.MaxPool2d(kernel_size=k, stride=1, padding=k // 2)
        self.cv4 = nn.MaxPool2d(kernel_size=k, stride=1, padding=k // 2)
        self.cv5 = Conv(4 * c3, c2, 1, 1)

    def forward(self, x):
        """Forward pass through SPPELAN layer."""
        y = [self.cv1(x)]
        y.extend(m(y[-1]) for m in [self.cv2, self.cv3, self.cv4])
        return self.cv5(torch.cat(y, 1))

二 、改进方法介绍🛩️🛩️

1、复制上述各模块代码至ultralytics/nn/modules/block.py中
2、在ultralytics/nn/modules/block.py、init.py、task.py中加入模块名
3、修改配置文件如下:

backbone:
  # [from, repeats, module, args]
  - [-1, 1, Conv, [64, 3, 2]] # 0-P1/2
  - [-1, 1, Conv, [128, 3, 2]] # 1-P2/4
  - [-1, 3, C2f, [128, True]]
  - [-1, 1, Conv, [256, 3, 2]] # 3-P3/8
  - [-1, 6, C2f, [256, True]]
  - [-1, 1, SCDown, [512, 3, 2]] # 5-P4/16
  - [-1, 6, C2f, [512, True]]
  - [-1, 1, SCDown, [1024, 3, 2]] # 7-P5/32
  - [-1, 3, C2fCIB, [1024, True]]
  - [-1, 1, SPP, [1024]] # 9
  #- [-1, 1, SPPF, [1024, 5]] # 9
  #- [-1, 1, SimSPPF, [1024, 5]] # 9
  #- [-1, 1, ASPP, [512]] # 9
  #- [-1, 1, SPPCSPC, [1024]] # 9  
  #- [-1, 1, SPPFCSPC, [1024, 5]] # 9 
  #- [-1, 1, BasicRFB, [1024]] # 9
  #- [-1, 1, SPPELAN, [1024]] # 9  
  - [-1, 1, PSA, [1024]] # 10

(注:YOLOv10中已集成SPP、SPPF和SPPELAN,可直接调用使用)


本文至此结束,文章持续更新中,敬请期待!!!
请添加图片描述
喜欢的本文的话,请不吝点赞+收藏,感谢各位伙伴的支持🍵🍵
下一篇:【5】YOLOv10添加注意力机制 【手把手教学】【先进模块随心选】🥤🥤【预告】


  • 61
    点赞
  • 66
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值