使用RCSOSA替换C2f模块

前言

YOLOv8作为目标检测领域的经典模型,凭借其高效的检测能力在诸多应用场景中得到了广泛应用。然而,随着任务复杂性的增加,如在医学影像中的脑肿瘤检测,标准的YOLOv8模型可能会遇到一些性能瓶颈。在这篇博客中,我将展示如何通过将YOLOv8中的C2f模块替换为RCSOSA模块来改进模型性能,并提升其在复杂场景下的表现。

什么是RCSOSA模块?

RCSOSA模块简介

RCSOSA模块全称是“基于通道混洗和一击聚合的重参数化卷积”(Reparameterized Convolution based on channel Shuffle and One-Shot Aggregation),是RCS-YOLO模型中的一个核心组件。它的设计初衷是通过减少推理时的计算负担来加速处理,同时在训练阶段增强特征提取能力。这种模块在医学影像处理,特别是脑肿瘤检测等复杂任务中,表现出了极高的有效性。

为什么选择RCSOSA模块?

在传统YOLO模型中,C2f模块是常用的特征处理组件。然而,在处理复杂场景或小目标时,C2f模块的表现可能有所欠缺。RCSOSA模块的引入正是为了弥补这些不足,具体来说,它带来了以下几方面的改进:

  1. 结构重参数化:这种技术在训练阶段允许模型学习更多的细节特征,然后在推理阶段通过简化结构来降低计算量,从而既保证了速度又不损失精度。

  2. 通道混洗(Channel Shuffle):RCSOSA模块通过通道混洗技术,使得不同通道的特征信息能够更好地交流,从而提高了模型在处理复杂图像时的准确性。

  3. 一击聚合(One-Shot Aggregation):通过“一击聚合”,RCSOSA模块可以在最后一步高效地汇总所有重要信息,这不仅提升了模型处理的效率,还确保了特征信息的充分利用。

RCSOSA模块在脑肿瘤检测中的应用

在医学影像处理,尤其是脑肿瘤检测任务中,RCSOSA模块表现出了显著的优势。实验结果显示,与YOLOv8模型相比,RCS-YOLO模型的检测速度提高了60%,每秒可以处理大约114张图像,精度也提升了1%。这些改进使得RCS-YOLO模型在医学图像检测任务中脱颖而出,特别是在需要高度精细检测的场景中。

如何在YOLOv8中替换C2f模块

接下来,我们将介绍如何在YOLOv8中使用RCSOSA模块替换C2f模块。这个过程分为以下几步:

第一步:在 block.py 中注册 RCSOSA 模块

1. 打开 ultralytics/nn/modules/block.py 文件。

2. 在文件的顶部,通常在其他模块导入的区域,添加以下代码以导入 RCSOSA 模块,并注册它的名称。

"RCSOSA",

第二步:在 ultralytics/nn/modules/block.py 中添加 RCSOSA 模块代码

在第一步中,我们已经在 ultralytics/nn/modules/block.py 文件的顶部注册了 RCSOSA 模块。现在,我们要将 SRRCSOSA 类的实现代码添加到 block.py 文件中。

 1. 打开 ultralytics/nn/modules/block.py 文件。

 2. 在 block.py 文件中,添加以下代码:


class SR(nn.Module):
    # Shuffle RepVGG
    def __init__(self, c1, c2):
        super().__init__()
        c1_ = int(c1 // 2)
        c2_ = int(c2 // 2)
        self.repconv = RepConv(c1_, c2_, bn=True)

    def forward(self, x):
        x1, x2 = x.chunk(2, dim=1)
        out = torch.cat((x1, self.repconv(x2)), dim=1)
        out = self.channel_shuffle(out, 2)
        return out

    def channel_shuffle(self, x, groups):
        batchsize, num_channels, height, width = x.data.size()
        channels_per_group = num_channels // groups
        x = x.view(batchsize, groups, channels_per_group, height, width)
        x = torch.transpose(x, 1, 2).contiguous()
        x = x.view(batchsize, -1, height, width)
        return x

class RCSOSA(nn.Module):
    # VoVNet with Res Shuffle RepVGG
    def __init__(self, c1, c2, n=1, se=False, g=1, e=0.5):
        super().__init__()
        n_ = n // 2
        c_ = make_divisible(int(c1 * e), 8)
        self.conv1 = RepConv(c1, c_, bn=True)
        self.conv3 = RepConv(int(c_ * 3), c2, bn=True)
        self.sr1 = nn.Sequential(*[SR(c_, c_) for _ in range(n_)])
        self.sr2 = nn.Sequential(*[SR(c_, c_) for _ in range(n_)])

        self.se = None
        if se:
            self.se = SEAttention(c2)

    def forward(self, x):
        x1 = self.conv1(x)
        x2 = self.sr1(x1)
        x3 = self.sr2(x2)
        x = torch.cat((x1, x2, x3), 1)
        return self.conv3(x) if self.se is None else self.se(self.conv3(x))

  3. 保存 block.py 文件。

第三步:在 ultralytics-main/ultralytics/utils/torch_utils.py 中添加 make_divisible 函数

你需要在 ultralytics-main/ultralytics/utils/torch_utils.py 文件中添加 make_divisible 函数,并确保该函数在其他模块中可以正确导入和使用。按照以下步骤进行操作:

1. 打开 torch_utils.py 文件:进入 ultralytics-main/ultralytics/utils/ 目录,找到并打开 torch_utils.py 文件。

2. 添加 make_divisible 函数:在文件中合适的位置添加 make_divisible 函数。你可以将其放置在 def scale_img(img, ratio=1.0, same_shape=False, gs=32): 函数之后,或在文件的其他合适位置。


def scale_img(img, ratio=1.0, same_shape=False, gs=32):
    """Scales and pads an image tensor of shape img(bs,3,y,x) based on given ratio and grid size gs, optionally
    retaining the original shape.
    """
    if ratio == 1.0:
        return img
    h, w = img.shape[2:]
    s = (int(h * ratio), int(w * ratio))  # new size
    img = torch.nn.functional.interpolate(img, size=s, mode="bilinear", align_corners=False)  # resize
    if not same_shape:  # pad/crop img
        h, w = (math.ceil(x * ratio / gs) * gs for x in (h, w))
    return torch.nn.functional.pad(img, [0, w - s[1], 0, h - s[0]], value=0.447)  # value = imagenet mean

def make_divisible(x, divisor):#添加该函数
    """Returns nearest x divisible by divisor."""
    if isinstance(divisor, torch.Tensor):
        divisor = int(divisor.max())  # 将 Tensor 转换为 int
    return math.ceil(x / divisor) * divisor

3. 确保在其他模块中正确导入 make_divisible 函数:确保你在 ultralytics/nn/modules/block.py 文件中通过以下方式正确导入 make_divisible 函数:

from ultralytics.utils.torch_utils import make_divisible  # 从 torch_utils 导入 make_divisible 函数

第四步:将 SEAttention 类添加到 common.py 并在 block.py 中导入

在第四步中,你需要将 SEAttention 类添加到 ultralytics-main/ultralytics/nn/modules/common.py 文件中,并在 block.py 文件中正确导入该类。请按照以下步骤操作:

1. 在 common.py 中添加 SEAttention
  1. 打开 common.py 文件

    • 进入 ultralytics-main/ultralytics/nn/modules/ 目录,找到并打开 common.py 文件。如果 common.py 文件不存在,可以创建一个新的文件。
  2. 添加 SEAttention

    • common.py 文件中,添加以下 SEAttention 类的定义:
      import torch
      import torch.nn as nn
      import torch.nn.init as init
      
      class SEAttention(nn.Module):
          def __init__(self, channel=512, reduction=16):
              super().__init__()
              self.avg_pool = nn.AdaptiveAvgPool2d(1)
              self.fc = nn.Sequential(
                  nn.Linear(channel, channel // reduction, bias=False),
                  nn.ReLU(inplace=True),
                  nn.Linear(channel // reduction, channel, bias=False),
                  nn.Sigmoid()
              )
      
          def init_weights(self):
              for m in self.modules():
                  if isinstance(m, nn.Conv2d):
                      init.kaiming_normal_(m.weight, mode='fan_out')
                      if m.bias is not None:
                          init.constant_(m.bias, 0)
                  elif isinstance(m, nn.BatchNorm2d):
                      init.constant_(m.weight, 1)
                      init.constant_(m.bias, 0)
                  elif isinstance(m, nn.Linear):
                      init.normal_(m.weight, std=0.001)
                      if m.bias is not None:
                          init.constant_(m.bias, 0)
      
          def forward(self, x):
              b, c, _, _ = x.size()
              y = self.avg_pool(x).view(b, c)
              y = self.fc(y).view(b, c, 1, 1)
              return x * y.expand_as(x)
      
2. 在 block.py 文件中导入 SEAttention
  1. 打开 block.py 文件

    • 进入 ultralytics-main/ultralytics/nn/modules/ 目录,找到并打开 block.py 文件。
  2. 添加导入语句

    • block.py 文件的顶部,添加以下导入语句以使用 SEAttention 
      from ultralytics.nn.modules.common import SEAttention
      

第五步:在 __init__.py 中注册 RCSOSA 模块

1. 打开 ultralytics/nn/modules/__init__.py 文件。

2. 在文件中注册 RCSOSA 模块名。

__init__.py 文件中,找到其他模块注册的地方,然后添加 RCSOSA 模块的导入和注册。

 3. 保存文件。

第六步:在 tasks.py 顶部注册 RCSOSA 模块

  1. 打开 ultralytics/nn/tasks.py 文件。

  2. 在文件顶部导入 RCSOSA 模块。

第七步:在 parse_model 函数中添加对 RCSOSA 模块的支持

  1. 找到 parse_model 函数:打开 ultralytics/nn/tasks.py 文件,并找到 parse_model 函数的定义。这通常是处理模型配置文件的函数。

  2. parse_model 中添加 RCSOSA 模块

    parse_model 函数中,你会看到一段代码负责根据配置文件中的模块名创建相应的模块对象。在处理 C2f 模块的代码附近,添加对 RCSOSA 模块的支持。

  3. 第一个需要添加的地方:

    parse_model 函数中,当我们处理模块时,确保 RCSOSA 被包含在处理 C2fC3 等类似模块的地方。

    if m in {
        RCSOSA,  # 添加RCSOSA支持
        Classify,
        Conv,
        ConvTranspose,
        GhostConv,
        Bottleneck,
        GhostBottleneck,
        SPP,
        SPPF,
        DWConv,
        Focus,
        BottleneckCSP,
        C1,
        C2,
        C2f,
        RepNCSPELAN4,
        ELAN1,
        ADown,
        AConv,
        SPPELAN,
        C2fAttn,
        C3,
        C3TR,
        C3Ghost,
        nn.ConvTranspose2d,
        DWConvTranspose2d,
        C3x,
        RepC3,
        PSA,
        SCDown,
        C2fCIB,
    }:
    

    2. 第二个需要添加的地方:

    在将 args 插入到模块构造函数的参数中时,确保 RCSOSA 模块与其他类似模块一起处理。

    if m in {BottleneckCSP, C1, C2, C2f, C2fAttn, C3, C3TR, C3Ghost, C3x, RepC3, C2fCIB, RCSOSA}:  # 添加RCSOSA支持
        args.insert(2, n)  # number of repeats
        n = 1
    

    完整代码片段示例

    def parse_model(d, ch, verbose=True):  # model_dict, input_channels(3)
        """Parse a YOLO model.yaml dictionary into a PyTorch model."""
        import ast
    
        # Args
        max_channels = float("inf")
        nc, act, scales = (d.get(x) for x in ("nc", "activation", "scales"))
        depth, width, kpt_shape = (d.get(x, 1.0) for x in ("depth_multiple", "width_multiple", "kpt_shape"))
        if scales:
            scale = d.get("scale")
            if not scale:
                scale = tuple(scales.keys())[0]
                LOGGER.warning(f"WARNING ⚠️ no model scale passed. Assuming scale='{scale}'.")
            depth, width, max_channels = scales[scale]
    
        if act:
            Conv.default_act = eval(act)  # redefine default activation, i.e. Conv.default_act = nn.SiLU()
            if verbose:
                LOGGER.info(f"{colorstr('activation:')} {act}")  # print
    
        if verbose:
            LOGGER.info(f"\n{'':>3}{'from':>20}{'n':>3}{'params':>10}  {'module':<45}{'arguments':<30}")
        ch = [ch]
        layers, save, c2 = [], [], ch[-1]  # layers, savelist, ch out
        for i, (f, n, m, args) in enumerate(d["backbone"] + d["head"]):  # from, number, module, args
            m = getattr(torch.nn, m[3:]) if "nn." in m else globals()[m]  # get module
            for j, a in enumerate(args):
                if isinstance(a, str):
                    with contextlib.suppress(ValueError):
                        args[j] = locals()[a] if a in locals() else ast.literal_eval(a)
    
            n = n_ = max(round(n * depth), 1) if n > 1 else n  # depth gain
            if m in {
                RCSOSA,  # 添加RCSOSA支持
                Classify,
                Conv,
                ConvTranspose,
                GhostConv,
                Bottleneck,
                GhostBottleneck,
                SPP,
                SPPF,
                DWConv,
                Focus,
                BottleneckCSP,
                C1,
                C2,
                C2f,
                RepNCSPELAN4,
                ELAN1,
                ADown,
                AConv,
                SPPELAN,
                C2fAttn,
                C3,
                C3TR,
                C3Ghost,
                nn.ConvTranspose2d,
                DWConvTranspose2d,
                C3x,
                RepC3,
                PSA,
                SCDown,
                C2fCIB,
            }:
                c1, c2 = ch[f], args[0]
                if c2 != nc:  # if c2 not equal to number of classes (i.e. for Classify() output)
                    c2 = make_divisible(min(c2, max_channels) * width, 8)
                if m is C2fAttn:
                    args[1] = make_divisible(min(args[1], max_channels // 2) * width, 8)  # embed channels
                    args[2] = int(
                        max(round(min(args[2], max_channels // 2 // 32)) * width, 1) if args[2] > 1 else args[2]
                    )  # num heads
    
                args = [c1, c2, *args[1:]]
                if m in {BottleneckCSP, C1, C2, C2f, C2fAttn, C3, C3TR, C3Ghost, C3x, RepC3, C2fCIB, RCSOSA}:  # 添加RCSOSA支持
                    args.insert(2, n)  # number of repeats
                    n = 1
    

    通过完成这两个修改点,RCSOSA 模块已经被成功添加到 parse_model 函数中,并且在YOLOv8配置文件被解析时能够正确地处理和使用该模块。

第八步:创建或修改 yolov8-RCSOSA.yaml 配置文件

  1. 创建或打开配置文件:

    ultralytics/cfg/models/v8/ 目录下,创建一个名为 yolov8-RCSOSA.yaml 的新文件,或者你可以复制一个现有的配置文件(如 yolov8n.yaml),并在此基础上进行修改。

  2. 修改配置文件以使用 RCSOSA 模块:

    在配置文件中,我们将原来的 C2f 模块替换为 RCSOSA 模块。以下是一个示例配置文件的结构:

    # Ultralytics YOLO 🚀, AGPL-3.0 license
    # YOLOv8 object detection model with P3-P5 outputs. For Usage examples see https://docs.ultralytics.com/tasks/detect
    
    # Parameters
    nc: 80  # number of classes
    scales: # model compound scaling constants, i.e. 'model=yolov8n.yaml' will call yolov8.yaml with scale 'n'
      # [depth, width, max_channels]
      n: [0.33, 0.25, 1024]  # YOLOv8n summary: 225 layers,  3157200 parameters,  3157184 gradients,   8.9 GFLOPs
      s: [0.33, 0.50, 1024]  # YOLOv8s summary: 225 layers, 11166560 parameters, 11166544 gradients,  28.8 GFLOPs
      m: [0.67, 0.75, 768]   # YOLOv8m summary: 295 layers, 25902640 parameters, 25902624 gradients,  79.3 GFLOPs
      l: [1.00, 1.00, 512]   # YOLOv8l summary: 365 layers, 43691520 parameters, 43691504 gradients, 165.7 GFLOPs
      x: [1.00, 1.25, 512]   # YOLOv8x summary: 365 layers, 68229648 parameters, 68229632 gradients, 258.5 GFLOPs
    
    # YOLOv8.0n backbone
    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, RCSOSA, [128]]
      - [-1, 1, Conv, [256, 3, 2]]  # 3-P3/8
      - [-1, 6, RCSOSA, [256]]
      - [-1, 1, Conv, [512, 3, 2]]  # 5-P4/16
      - [-1, 6, RCSOSA, [512, True]]
      - [-1, 1, Conv, [1024, 3, 2]]  # 7-P5/32
      - [-1, 3, RCSOSA, [1024, True]]
      - [-1, 1, SPPF, [1024, 5]]  # 9
    
    # YOLOv8.0n head
    head:
      - [-1, 1, nn.Upsample, [None, 2, 'nearest']]
      - [[-1, 6], 1, Concat, [1]]  # cat backbone P4
      - [-1, 3, RCSOSA, [512]]  # 12
    
      - [-1, 1, nn.Upsample, [None, 2, 'nearest']]
      - [[-1, 4], 1, Concat, [1]]  # cat backbone P3
      - [-1, 3, RCSOSA, [256]]  # 15 (P3/8-small)
    
      - [-1, 1, Conv, [256, 3, 2]]
      - [[-1, 12], 1, Concat, [1]]  # cat head P4
      - [-1, 3, RCSOSA, [512]]  # 18 (P4/16-medium)
    
      - [-1, 1, Conv, [512, 3, 2]]
      - [[-1, 9], 1, Concat, [1]]  # cat head P5
      - [-1, 3, RCSOSA, [1024]]  # 21 (P5/32-large)
    
      - [[15, 18, 21], 1, Detect, [nc]]  # Detect(P3, P4, P5)
    

使用上文配置文件开始训练

### DWR Module 替代C2F的方案 在Web开发环境中,DWR (Direct Web Remoting) 模块通常用于简化服务器端Java对象与客户端JavaScript之间的交互。然而,在此上下文中讨论的是将DWR模块作为计算机视觉领域中的一种改进组件来替换YOLOv8架构里的C2f模块。 #### 使用DWR模块的优势 通过引入DWR模块代替传统的C2f模块,可以在不牺牲推理速度的情况下显著提升模型性能[^1]。具体来说: - **增强的感受野**:利用膨胀卷积(dilated convolution),能够扩大网络高层部分的感受野范围而不增加计算量。 - **残差连接机制**:继承自ResNet的思想,允许更深层次的信息传递,有助于缓解梯度消失问题并促进训练过程中的稳定性。 ```python import torch.nn as nn class DWRLayer(nn.Module): def __init__(self, in_channels, out_channels, dilation=2): super(DWRLayer, self).__init__() self.conv = nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=3, padding=dilation, dilation=dilation) def forward(self, x): return self.conv(x) ``` 上述代码展示了如何构建一个简单的基于PyTorch框架下的DWR层实例。该实现采用了膨胀率为`2`的三维卷积操作,并设置了相应的填充参数以维持输入输出尺寸一致。 对于希望进一步优化YOLOv8检测效果的应用场景而言,采用经过调整后的SIR(Simple Inverted Residual)模块处理底层特征图,则能更好地支持顶层DWR模块的工作效率和准确性。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值