【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模块主要解决的问题:
- 输入图像尺寸的固定问题
传统卷积神经网络(CNN)要求输入图像具有固定的尺寸。例如,在训练和测试过程中,输入图像的大小通常需要调整为相同的尺寸。然而,在实际应用中,图像的尺寸是多变的。如果强行对图像进行裁剪或缩放,可能会导致图像信息的丢失或失真,从而影响模型的性能。 - 多尺度信息的捕捉
自然图像中的物体可以具有不同的尺度和位置,传统的固定大小的池化层无法充分捕捉这些多尺度信息。SPP通过在不同尺度上进行池化,能够更好地捕捉到图像中的多尺度特征,增强了模型对图像内容的理解能力。 - 特征表示的固定长度问题
为了适应不同尺寸的输入图像,传统CNN在特征提取后,往往需要将特征映射调整为固定长度的向量表示。然而,直接通过裁剪或缩放等方式来实现这一目标,可能会导致特征信息的损失或变形。SPP通过多尺度池化,自然地生成固定长度的特征向量表示,无需对输入图像进行复杂的预处理。 - 预处理复杂性问题
传统方法需要对输入图像进行预处理以确保尺寸统一,这不仅增加了处理的复杂性,还可能引入额外的误差。SPP模块通过其灵活的池化机制,能够直接处理不同尺寸的输入图像,简化了预处理过程,减少了图像信息的丢失和变形。
SPP模块的优势:
- 适应任意尺寸输入:SPP模块能够处理不同尺寸的输入图像,并将其转换为固定长度的表示,解决了传统CNN输入尺寸固定的问题。
- 捕捉多尺度信息:通过在不同尺度上进行池化,SPP模块能够捕捉到图像中的多尺度信息,提高了模型对图像内容的理解能力。
- 增强特征表示:SPP模块通过金字塔结构,提供了更丰富的特征表示,增强了模型的表达能力和分类性能。
- 简化处理流程:无需对输入图像进行裁剪或缩放,简化了预处理流程,减少了图像信息的丢失或失真。
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模块提出的, 通过进一步简化和优化池化过程,减少了计算开销,同时保持了对多尺度特征的捕捉能力。
主要特点
- 优化的池化过程:SPPF 对传统 SPP 模块中的池化步骤进行了优化,减少了冗余计算和内存使用,提高了处理速度。
- 固定长度输出:SPPF 能够处理任意尺寸的输入图像,并生成固定长度的特征表示,适用于各种卷积神经网络结构。
- 多尺度特征表示:通过多尺度池化,SPPF 继续保留了捕捉图像中多尺度特征的能力,提高了模型对图像内容的理解能力。
- 计算效率提升:相比传统 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激活函数,减少了计算开销,并保持了对多尺度特征的捕捉能力。
主要特点
- 高效计算:通过简化池化过程,减少计算量和内存使用,适应实时处理需求。
- 多尺度特征捕捉:通过不同尺度的池化,捕捉图像中的多尺度信息,增强特征表示能力。
- 固定长度输出:无论输入图像尺寸如何,输出特征的长度固定,便于后续处理。
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的优点:
- 多尺度感知:能够在不同的尺度上捕捉特征,增强模型对大小不同物体的识别能力。
- 有效的上下文信息建模:通过不同空洞率的卷积核,能够有效地捕捉到远距离的上下文信息,提高分割的准确度和鲁棒性。
- 计算效率:通过引入空洞卷积,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添加注意力机制 【手把手教学】【先进模块随心选】🥤🥤【预告】