YOLOv8魔改系列:使用RFAConv模块,实现YOLOv8快速涨点

本文介绍了RFAConv模块,一种改进的空间注意力机制,尤其适用于大尺寸卷积核。RFAConv在不显著增加计算成本和参数的情况下,通过关注感受野空间特征,有效解决了卷积核参数共享问题,提升了网络性能。实验在ImageNet-1k、COCO和VOC数据集上验证了其优越性。作者提供了基于YOLOv5n和VisDrone数据集的实验结果,展示RFAConv在目标检测上的优势,并给出了在YOLOv8模型中应用RFAConv的改进策略。
摘要由CSDN通过智能技术生成

目录

Abstract

详细结构与测试结果

改进策略


RFAConv模块原论文链接:https://arxiv.org/pdf/2304.03198.pdf

Abstract

       空间注意力已被广泛用于改进卷积神经网络的性能。然而,它具有一定的局限性。在RFAConv模块原论文中,作者提出了一个关于空间注意力效果的新视角,即空间注意力机制本质上是解决卷积核参数共享的问题。然而,空间注意力生成的注意力图中包含的信息对于大尺寸卷积核来说是不足的。因此,作者提出了一种新颖的注意力机制,称为感受野注意力(RFA)。现有的空间注意力,如卷积块注意力模块(CBAM)和协同注意力(CA)只关注空间特征,这并不能完全解决卷积核参数共享的问题。相比之下,RFA不仅关注感受野空间特征,还为大尺寸卷积核提供有效的注意力权重。由RFA开发的感受野注意力卷积操作(RFAConv)代表了一种替代标准卷积操作的新方法。它几乎没有增加计算成本和参数,同时显著提高了网络性能。作者在ImageNet-1k、COCO和VOC数据集上进行了一系列实验,证明了此方法的优越性。特别重要的是,作者认为现在是将焦点从空间特征转移到感受野空间特征的时候了,通过这种方式,我们可以进一步提高网络性能,取得更好的结果。相关任务的代码和预训练模型可以在https://github.com/Liuchen1997/RFAConv中找到。

详细结构与测试结果

        关于感受野空间特征,我们提出了感受野注意力(RFA)。这种方法不仅强调感受野滑块内不同特征的重要性,还优先考虑感受野空间特征。通过这种方法,卷积核参数共享的问题被完全解决。感受野空间特征根据卷积核的大小动态生成,因此,RFA是卷积的固定组合,不能脱离卷积操作的帮助,同时依赖于RFA来提高性能,因此我们提出了感受野注意力卷积(RFAConv)。具有3×3大小卷积核的RFAConv的总体结构如图所示。由RFAConv的详细结构可知,它动态地决定了每个特征在接受域中的重要性,并解决了参数共享的问题。

如下图所示基于YOLOv5n和VisDrone数据集的对象检测实验,说明了在GroupConv上构建的RFAConv的优势。

      作者在COCO2017数据集上进行目标检测实验,重新评估作者所提出的方法。COCO2017包含118287个训练集和5000个验证集。我们选择了YOLOv5n、YOLOv7-tiny和YOLOv8n模型进行一系列实验。除了迭代次数和批量大小外,所有参数均设置为默认值。作者将每个模型训练300个epochs,批量大小为32。为了与分类相似,作者用注意力机制构建的新型卷积操作替换基线模型中的一些卷积操作。具体来说,作者用注意力卷积替换了YOLOv5和YOLOv8的yaml文件中的所有3×3卷积操作。对于YOLOv7,作者替换了骨干网络中所有ELAN [34]中的第一个3×3卷积操作。按照之前的工作,并分别报告AP50、AP75、AP、APS、APM和APL。此外,为了更好地显示不同网络的性能,作者选择了YOLOv5n的训练过程进行可视化,展示了AP50随迭代次数的变化情况。结果图如下图所示:

由不同注意卷积构造的YOLOv5n在训练过程中AP50的变化如下图所示:

改进策略

ultralytics/nn/modules/conv.py 中增加如下代码:

class CAConv(nn.Module):
    def __init__(self, channel, reduction=32):
        super(CAConv, self).__init__()
        self.pool_h = nn.AdaptiveAvgPool2d((None, 1))
        self.pool_w = nn.AdaptiveAvgPool2d((1, None))

        mip = max(8, channel // reduction)

        self.conv1 = nn.Conv2d(channel, mip, kernel_size=1, stride=1, padding=0)
        self.bn1 = nn.BatchNorm2d(mip)
        self.act = nn.Hardswish()

        self.conv_h = nn.Conv2d(mip, channel, kernel_size=1, stride=1, padding=0)
        self.conv_w = nn.Conv2d(mip, channel, kernel_size=1, stride=1, padding=0)


    def forward(self, x):
        identity = x
        n, c, h, w = x.size()
        x_h = self.pool_h(x)
        x_w = self.pool_w(x).permute(0, 1, 3, 2)
        y = torch.cat([x_h, x_w], dim=2)
        y = self.conv1(y)
        y = self.bn1(y)
        y = self.act(y)
        x_h, x_w = torch.split(y, [h, w], dim=2)
        x_w = x_w.permute(0, 1, 3, 2)
        a_h = self.conv_h(x_h).sigmoid()
        a_w = self.conv_w(x_w).sigmoid()
        out = identity * a_w * a_h

        return out


class RFACA(nn.Module):
    def __init__(self, c1, c2, k=1, s=1, p=None, g=1, d=1, act=True):
        super(RFACA, self).__init__()
        self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p, d), groups=g, dilation=d, bias=False)
        self.avg_pool = nn.AdaptiveAvgPool2d(1)
        self.group_conv1 = nn.Conv2d(c2, 9 *c2,1, 1, autopad(1, None, 1), groups=c2)
self.group_conv2 = nn.Conv2d(c2, 9 *c2,3, 1, autopad(3, None, 1), groups=c2)
        self.group_conv3 = nn.Conv2d(c2, 9 *c2,5, 1, autopad(5, None, 1), groups=c2)
        self.softmax = nn.Softmax(dim=1)
        self.group_conv4 = Conv(c2, 9 * c2, k=k, g=c2)
        self.group_conv5 = Conv(c2, 9 * c2, k=k, g=c2)
        self.group_conv6 = Conv(c2, 9 * c2, k=k, g=c2)
        self.CA = CAConv(c2)
        self.convDown = Conv(c2, c2, k=3, s=3)
    def forward(self, x):
        x=self.conv(x)
        y = self.avg_pool(x)
        group1 = self.softmax(self.group_conv1(y))
        group2 = self.softmax(self.group_conv2(y))
        group3 = self.softmax(self.group_conv3(y))
        g1 = self.group_conv4(x)
        g2 = self.group_conv5(x)
        g3 = self.group_conv6(x)
        out1 = g1 * group1.expand_as(g1)
        out2 = g2 * group2.expand_as(g2)
        out3 = g3 * group3.expand_as(g3)
        out = out1+out2+out3
        batch_size, channels, height, width = out.shape
        output_channels = channels // 9
        out = out.view(batch_size, output_channels, 3,3, height,width).permute(0, 1, 4,2,5,3).reshape(batch_size,
        output_channels, 3 * height, 3 * width)
        out = self.CA(out)
        out = self.convDown(out)
        return out
然后, ultralytics/nn/modules/__init__.py 中,将 RFACA 导入,代码如下:
from .block import (C1, C2, C3, C3TR, DFL, SPP, SPPF, Bottleneck, BottleneckCSP, C2f, C3Ghost, C3x, GhostBottleneck, HGBlock, HGStem, Proto, RepC3)
from .conv import (CBAM, ChannelAttention, Concat, Conv, Conv2, ConvTranspose, DWConv, DWConvTranspose2d, Focus, GhostConv, LightConv, RepConv, SpatialAttention,RFACA)
from .head import Classify, Detect, Pose, RTDETRDecoder, Segment
from .transformer import (AIFI, MLP, DeformableTransformerDecoder,
DeformableTransformerDecoderLayer, LayerNorm2d,
                        MLPBlock, MSDeformAttn, TransformerBlock,
TransformerEncoderLayer, TransformerLayer)


__all__ = [
    'Conv', 'Conv2', 'LightConv', 'RepConv', 'DWConv', 'DWConvTranspose2d', 'ConvTranspose', 'Focus', 'GhostConv',
    'ChannelAttention', 'SpatialAttention', 'CBAM', 'Concat',
'TransformerLayer', 'TransformerBlock', 'MLPBlock',
    'LayerNorm2d', 'DFL', 'HGBlock', 'HGStem', 'SPP', 'SPPF', 'C1', 'C2', 'C3',
'C2f', 'C3x', 'C3TR', 'C3Ghost',
    'GhostBottleneck', 'Bottleneck', 'BottleneckCSP', 'Proto', 'Detect', 'Segment', 'Pose', 'Classify',
    'TransformerEncoderLayer', 'RepC3', 'RTDETRDecoder', 'AIFI', 'DeformableTransformerDecoder',
    'DeformableTransformerDecoderLayer', 'MSDeformAttn', 'MLP',"RFACA"]
接下来,在 ultralytics/nn/task.py 中,导入 RFACA,代码如下所示:
from ultralytics.nn.modules import (AIFI, C1, C2, C3, C3TR, SPP, SPPF, Bottleneck, BottleneckCSP, C2f, C3Ghost, C3x, Classify, Concat, Conv, Conv2, ConvTranspose,
Detect, DWConv, DWConvTranspose2d, Focus, GhostBottleneck, GhostConv, HGBlock, HGStem, Pose, RepC3, RepConv, RTDETRDecoder, Segment,RFACA)

另外在task.py中if m in {Classify,Conv,ConvTranspose,GhostConv, .......的最后添加RFACA

最后修改yolov8.yaml文件,将backbone中的Conv替换为RFACA

backbone:
    # [from, repeats, module, args]
    - [-1, 1, RFACA, [64, 3, 2]] # 0-P1/2
    - [-1, 1, RFACA, [128, 3, 2]] # 1-P2/4
    - [-1, 3, C2f, [128, True]]
    - [-1, 1, RFACA, [256, 3, 2]] # 3-P3/8
    - [-1, 6, C2f, [256, True]]
    - [-1, 1, RFACA, [512, 3, 2]] # 5-P4/16
    - [-1, 6, C2f, [512, True]]
    - [-1, 1, RFACA, [1024, 3, 2]] # 7-P5/32
    - [-1, 3, C2f, [1024, True]]
    - [-1, 1, SPPF, [1024, 5]] # 9

这样我们就完成了使用RFAConv代替Conv,希望对你有所帮助,实现快速涨点。

评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

&瞎学的小曹ζ

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

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

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

打赏作者

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

抵扣说明:

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

余额充值