YOLOv8改进 | 卷积模块 | SAConv可切换空洞卷积

秋招面试专栏推荐 :深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 


💡💡💡本专栏所有程序均经过测试,可成功执行💡💡💡


专栏目录 :《YOLOv8改进有效涨点》专栏介绍 & 专栏目录 | 目前已有40+篇内容,内含各种Head检测头、损失函数Loss、Backbone、Neck、NMS等创新点改进——点击即可跳转


许多现代目标检测器通过使用“观察和思考两次”的机制展现了卓越的性能。我们在目标检测的主干设计中探讨了这种机制。在宏观层面上,提出了递归特征金字塔(Recursive Feature Pyramid),它将额外的反馈连接从特征金字塔网络整合到底向上的主干层中。在微观层面上,提出了可切换空洞卷积(Switchable Atrous Convolution),它用不同的空洞率对特征进行卷积,并使用切换函数收集结果。将它们结合起来就得到了DetectoRS,它显著提高了目标检测的性能。 文章在介绍主要的原理后,将手把手教学如何进行模块的代码添加和修改,并将修改后的完整代码放在文章的最后,方便大家一键运行,小白也可轻松上手实践。以帮助您更好地学习深度学习目标检测YOLO系列的挑战。

目录

1. 原理

2. 将PConv添加到YOLOv8代码

2.1 PConv代码实现

2.2 更改init.py文件 

2.3 新增yaml文件

2.4 注册模块

2.5 执行程序

3. 完整代码分享

4. GFLOPs

5. 进阶

6. 总结


1. 原理

论文地址:DetectoRS: Detecting Objects with Recursive Feature Pyramid and Switchable Atrous Convolutio——点击即可跳转
官方代码:官方代码仓库——点击即可跳转

可切换空洞卷积 (SAC) 是一种复杂的卷积技术,用于增强深度学习模型的性能,特别是在对象检测和分割等任务中。以下是根据提供的文档对其主要原理的细分:

空洞卷积

空洞卷积,也称为扩张卷积,用于增加过滤器的视野,而不会增加参数数量或计算量。这是通过在连续的过滤器值之间引入零来实现的。插入这些零的速率称为空洞速率 ( r )。对于大小为 ( k \times k ) 的过滤器,有效内核大小变为 ( k + (k-1)(r-1) )。这允许网络捕获多尺度信息,这对于检测不同大小的物体特别有用。

可切换空洞卷积 (SAC)

SAC 以空洞卷积为基础,引入了一种在卷积操作期间动态切换不同空洞率的机制。其核心思想是允许卷积层根据输入特征自适应地选择适当的空洞率。这种适应性有助于模型更好地处理同一图像中不同尺度的对象。

SAC 的组件

SAC 由三个主要组件组成:主 SAC 组件和两个附加在 SAC 组件之前和之后的全局上下文模块。

  • 主 SAC 组件:这是实际发生可切换空洞卷积的核心部分。它涉及两个具有不同空洞率的卷积操作的加权组合。

SAC 的数学公式如下: y = S(x) \cdot \text{Conv}(x, w, 1) + (1 - S(x)) \cdot \text{Conv}(x, w + \Delta w, r) 其中:

  • ( x ) 是输入。

  • ( w )( w + \Delta w ) 是卷积运算的权重。

  • ( r ) 是空洞率。

  • ( S(x) ) 是一个开关函数,实现为一个平均池化层,具有 5x5 内核,后跟一个 1x1 卷积层,确定两个卷积运算之间的平衡。

  • 全局上下文模块:这些模块在 SAC 操作前后为特征添加了图像级上下文,增强了模型理解图像全局结构的能力。

SAC的优势

  1. 多尺度检测:通过动态切换不同的空洞率,SAC 使模型能够更好地检测各种尺寸的物体。

  2. 参数效率:SAC 在不增加额外参数的情况下增加了感受野,同时保持了计算效率。

  3. 适应性:切换功能允许卷积层根据输入进行适应,使模型更加灵活,能够处理不同的图像和物体尺度。

实现示例

SAConv结构

骨干网络(例如 ResNet)中的每个 3x3 卷积层都转换为 SAC,然后执行具有不同空洞率的卷积的加权组合。

综上所述,可切换空洞卷积结合了空洞卷积和动态适应性的优点,增强了深度学习模型,提高了其处理不同物体尺度图像的能力,同时保持参数和计算方面的效率。

2. 将PConv添加到YOLOv8代码

2.1 PConv代码实现

关键步骤一:将下面代码粘贴到在/ultralytics/ultralytics/nn/modules/conv.py中,并在该文件的__all__中添加“SAConv”

class ConvAWS2d(nn.Conv2d):
    def __init__(self,
                 in_channels,
                 out_channels,
                 kernel_size,
                 stride=1,
                 padding=0,
                 dilation=1,
                 groups=1,
                 bias=True):
        super().__init__(
            in_channels,
            out_channels,
            kernel_size,
            stride=stride,
            padding=padding,
            dilation=dilation,
            groups=groups,
            bias=bias)
        self.register_buffer('weight_gamma', torch.ones(self.out_channels, 1, 1, 1))
        self.register_buffer('weight_beta', torch.zeros(self.out_channels, 1, 1, 1))

    def _get_weight(self, weight):
        weight_mean = weight.mean(dim=1, keepdim=True).mean(dim=2,
                                                            keepdim=True).mean(dim=3, keepdim=True)
        weight = weight - weight_mean
        std = torch.sqrt(weight.view(weight.size(0), -1).var(dim=1) + 1e-5).view(-1, 1, 1, 1)
        weight = weight / std
        weight = self.weight_gamma * weight + self.weight_beta
        return weight

    def forward(self, x):
        weight = self._get_weight(self.weight)
        return super()._conv_forward(x, weight, None)

    def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict,
                              missing_keys, unexpected_keys, error_msgs):
        self.weight_gamma.data.fill_(-1)
        super()._load_from_state_dict(state_dict, prefix, local_metadata, strict,
                                      missing_keys, unexpected_keys, error_msgs)
        if self.weight_gamma.data.mean() > 0:
            return
        weight = self.weight.data
        weight_mean = weight.data.mean(dim=1, keepdim=True).mean(dim=2,
                                                                 keepdim=True).mean(dim=3, keepdim=True)
        self.weight_beta.data.copy_(weight_mean)
        std = torch.sqrt(weight.view(weight.size(0), -1).var(dim=1) + 1e-5).view(-1, 1, 1, 1)
        self.weight_gamma.data.copy_(std)


class SAConv2d(ConvAWS2d):
    def __init__(self,
                 in_channels,
                 out_channels,
                 kernel_size,
                 s=1,
                 p=None,
                 g=1,
                 d=1,
                 act=True,
                 bias=True):
        super().__init__(
            in_channels,
            out_channels,
            kernel_size,
            stride=s,
            padding=autopad(kernel_size, p),
            dilation=d,
            groups=g,
            bias=bias)
        self.switch = torch.nn.Conv2d(
            self.in_channels,
            1,
            kernel_size=1,
            stride=s,
            bias=True)
        self.switch.weight.data.fill_(0)
        self.switch.bias.data.fill_(1)
        self.weight_diff = torch.nn.Parameter(torch.Tensor(self.weight.size()))
        self.weight_diff.data.zero_()
        self.pre_context = torch.nn.Conv2d(
            self.in_channels,
            self.in_channels,
            kernel_size=1,
            bias=True)
        self.pre_context.weight.data.fill_(0)
        self.pre_context.bias.data.fill_(0)
        self.post_context = torch.nn.Conv2d(
            self.out_channels,
            self.out_channels,
            kernel_size=1,
            bias=True)
        self.post_context.weight.data.fill_(0)
        self.post_context.bias.data.fill_(0)

        self.bn = nn.BatchNorm2d(out_channels)
        self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())

    def forward(self, x):
        # pre-context
        avg_x = torch.nn.functional.adaptive_avg_pool2d(x, output_size=1)
        avg_x = self.pre_context(avg_x)
        avg_x = avg_x.expand_as(x)
        x = x + avg_x
        # switch
        avg_x = torch.nn.functional.pad(x, pad=(2, 2, 2, 2), mode="reflect")
        avg_x = torch.nn.functional.avg_pool2d(avg_x, kernel_size=5, stride=1, padding=0)
        switch = self.switch(avg_x)
        # sac
        weight = self._get_weight(self.weight)
        out_s = super()._conv_forward(x, weight, None)
        ori_p = self.padding
        ori_d = self.dilation
        self.padding = tuple(3 * p for p in self.padding)
        self.dilation = tuple(3 * d for d in self.dilation)
        weight = weight + self.weight_diff
        out_l = super()._conv_forward(x, weight, None)
        out = switch * out_s + (1 - switch) * out_l
        self.padding = ori_p
        self.dilation = ori_d
        # post-context
        avg_x = torch.nn.functional.adaptive_avg_pool2d(out, output_size=1)
        avg_x = self.post_context(avg_x)
        avg_x = avg_x.expand_as(out)
        out = out + avg_x
        return self.act(self.bn(out))

可切换空洞卷积 (SAconv) 通过动态调整卷积层中的空洞率来增强图像处理,从而实现更有效的多尺度特征提取。 SAconv 处理图像的主要工作流程概述:

SAconv 的主要工作流程

  1. 输入图像: 该过程从输入到神经网络的输入图像开始。

  2. 初始卷积层: 图像首先经过几个标准卷积层,提取边缘、纹理和基本形状等低级特征。这些层通常具有固定的内核大小和步长值。

  3. 可切换空洞卷积 (SAC) 模块: 核心组件 SAC 模块取代了网络主干中的传统卷积层(例如 ResNet)。以下是 SAC 操作的详细分解:

  • 特征提取: 输入特征 ( x ) 首先通过两个不同的卷积操作进行处理,权重分别为 ( w ) 和 ( w + \Delta w ),其中 ( \Delta w )表示对权重的调整。这些卷积具有不同的空洞率,使它们能够捕获多个尺度的特征。

  • 切换函数: 切换函数 S(x)  确定两个卷积之间的平衡。它通常实现为具有 5x5 内核的平均池化层,后跟 1x1 卷积层。切换函数的输出是一组权重,用于平衡两个卷积的贡献。

  • 加权组合: SAC 的最终输出是两个卷积的加权组合: y = S(x) \cdot \text{Conv}(x, w, 1) + (1 - S(x)) \cdot \text{Conv}(x, w + \Delta w, r) 这里,( \text{Conv}(x, w, 1) ) 表示标准卷积,( \text{Conv}(x, w + \Delta w, r) )表示速率为 ( r ) 的空洞卷积。

  1. 全局上下文模块: 在 SAC 模块之前和之后,使用全局上下文模块来合并图像级上下文。这些模块通常涉及全局平均池化和全连接层等操作,以捕获图像的整体结构。这有助于细化特征并为后续层提供更好的上下文。

  2. 中间层: SAC 模块和全局上下文模块的输出被馈送到网络的进一步层,其中可能包括额外的卷积、规范化和激活函数。这些层继续细化和处理 SAC 模块提取的特征。

  3. 输出层: 最后,处理后的特征到达输出层,生成所需的结果。这可能是分类标签、分割掩码或用于对象检测的边界框,具体取决于网络设计用于的任务。

工作流程说明

在提供的文档中,图 4 显示了集成到 ResNet 架构中的 SAC 模块的示例。在此图中,ResNet 主干中的每个 3x3 卷积层都被 SAC 模块替换。此修改允许网络根据输入特征动态调整其感受野,从而提高需要多尺度特征检测的任务的性能。

2.2 更改init.py文件 

关键步骤二:修改modules文件夹下的__init__.py文件,先导入函数

然后在下面的__all__中声明函数

2.3 新增yaml文件

关键步骤三:在 \ultralytics\ultralytics\cfg\models\v8下新建文件 yolov8_SAConv.yaml并将下面代码复制进去

# 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

# YOLOv8.0n backbone
backbone:
  # [from, repeats, module, args]
  - [ -1, 1, SAConv2d, [ 64, 3, 2 ] ]  # 0-P1/2
  - [ -1, 1, SAConv2d, [ 128, 3, 2 ] ]  # 1-P2/4
  - [ -1, 3, C2f, [ 128, True ] ]
  - [ -1, 1, SAConv2d, [ 256, 3, 2 ] ]  # 3-P3/8
  - [ -1, 6, C2f, [ 256, True ] ]
  - [ -1, 1, SAConv2d, [ 512, 3, 2 ] ]  # 5-P4/16
  - [ -1, 6, C2f, [ 512, True ] ]
  - [ -1, 1, SAConv2d, [ 1024, 3, 2 ] ]  # 7-P5/32
  - [ -1, 3, C2f, [ 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, C2f, [ 512 ] ]  # 12

  - [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ]
  - [ [ -1, 4 ], 1, Concat, [ 1 ] ]  # cat backbone P3
  - [ -1, 3, C2f, [ 256 ] ]  # 15 (P3/8-small)

  - [ -1, 1, SAConv2d, [ 256, 3, 2 ] ]
  - [ [ -1, 12 ], 1, Concat, [ 1 ] ]  # cat head P4
  - [ -1, 3, C2f, [ 512 ] ]  # 18 (P4/16-medium)

  - [ -1, 1, SAConv2d, [ 512, 3, 2 ] ]
  - [ [ -1, 9 ], 1, Concat, [ 1 ] ]  # cat head P5
  - [ -1, 3, C2f, [ 1024 ] ]  # 21 (P5/32-large)

  - [ [ 15, 18, 21 ], 1, Detect, [ nc ] ]  # Detect(P3, P4, P5)

温馨提示:因为本文只是对yolov8基础上添加模块,如果要对yolov8n/l/m/x进行添加则只需要指定对应的depth_multiple 和 width_multiple。 


# YOLOv8n
depth_multiple: 0.33  # model depth multiple
width_multiple: 0.25  # layer channel multiple
max_channels: 1024 # max_channels
 
# YOLOv8s
depth_multiple: 0.33  # model depth multiple
width_multiple: 0.50  # layer channel multiple
max_channels: 1024 # max_channels
 
# YOLOv8l 
depth_multiple: 1.0  # model depth multiple
width_multiple: 1.0  # layer channel multiple
max_channels: 512 # max_channels
 
# YOLOv8m
depth_multiple: 0.67  # model depth multiple
width_multiple: 0.75  # layer channel multiple
max_channels: 768 # max_channels
 
# YOLOv8x
depth_multiple: 1.33  # model depth multiple
width_multiple: 1.25  # layer channel multiple
max_channels: 512 # max_channels

2.4 注册模块

关键步骤四:在parse_model函数中进行注册,添加SAConv,

2.5 执行程序

在train.py中,将model的参数路径设置为yolov8_SAConv.yaml的路径

建议大家写绝对路径,确保一定能找到

from ultralytics import YOLO
 
# Load a model
# model = YOLO('yolov8n.yaml')  # build a new model from YAML
# model = YOLO('yolov8n.pt')  # load a pretrained model (recommended for training)
 
model = YOLO(r'/projects/ultralytics/ultralytics/cfg/models/v8/yolov8_PConv.yaml')  # build from YAML and transfer weights
 
# Train the model
model.train(batch=16)

🚀运行程序,如果出现下面的内容则说明添加成功🚀 

3. 完整代码分享

https://pan.baidu.com/s/1z2KWa9guFsyHTETM9gXdPA?pwd=kbu5

 提取码: kbu5 

4. GFLOPs

关于GFLOPs的计算方式可以查看百面算法工程师 | 卷积基础知识——Convolution

未改进的YOLOv8nGFLOPs

改进后的GFLOPs

5. 进阶

可以与其他的注意力机制或者损失函数等结合,进一步提升检测效果

6. 总结

可切换空洞卷积 (SAConv) 可动态调整卷积层中的空洞率,通过基于输入特征平衡其贡献的切换函数组合不同的空洞卷积,使网络能够更有效地捕获多尺度特征。这种适应性增强了网络检测不同大小物体的能力,并提高了物体检测和分割等任务的性能,而无需添加额外的参数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/767533.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

市场规模5万亿,护理员缺口550万,商业护理企业如何解决服务供给难题?

干货抢先看 1. 据统计,我国失能、半失能老人数量约4400万,商业护理服务市场规模达5万亿。然而,当前养老护理员缺口巨大,人员的供需不匹配是很多养老服务企业需要克服的难题。 2. 当前居家护理服务的主要市场参与者分为两类&…

儿童无语言是否等同于自闭症?全面解析与认识自闭症

在探讨自闭症与儿童语言发展之间的关系时,我们首先需要明确的是,自闭症并非单一由语言缺失所定义,而是一个复杂的神经发育障碍,其核心特征包括社交互动和沟通能力的显著受损,以及重复、刻板的行为模式、兴趣或活动。 …

基于SpringBoot高校体育运动会管理系统设计和实现(源码+LW+调试文档+讲解等)

💗博主介绍:✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者,博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌💗 🌟文末获取源码数据库🌟 感兴趣的可以先收藏起来,…

【HICE】基于httpd下的web服务器搭建

1.下载httpd: dnf install httpd -y 2.进入httpd中: cd /etc/httpd cd conf.d 3.编辑一个新的vhost.conf 4.重启httpd服务 systemctl restart httpd 5.关闭防火墙 systemctl stop firewalld setenforce 0 6.文本写入(网页编辑&…

第6章:结构化开发方法

第6章:结构化开发方法 系统设计基本原理 1、抽象 抽象是一种设计技术,重点说明一个实体的本质方面,而忽略或者掩盖不是很重要或非本质的方面。 模块化 模块化是指将一个待开发的软件分解成若干个小的、简单的部分一模块,每个模…

尝试修改苍穹外卖为”李小罗餐厅“

学习苍穹外卖后,将其修改为自己所需要的项目,也是对苍穹外卖项目的加深理解 对项目之间的连接等关系进一步清晰,那么便开始吧 d1_开始修改 修改名字为”李小罗餐厅“ src\views\login\index.vue src\router.ts 结果展示 修改进来之后的展示…

从0到1手写vue源码

模版引擎 数组join法(字符串) es6反引号法(模版字符串换行) mustache (小胡子) 引入mustache 模版引擎的使用 mustache.render(templatestr,data)

【linux学习---1】点亮一个LED---驱动一个GPIO

文章目录 1、原理图找对应引脚2、IO复用3、IO配置4、GPIO配置5、GPIO时钟使能6、总结 1、原理图找对应引脚 从上图 可以看出, 蜂鸣器 接到了 BEEP 上, BEEP 就是 GPIO5_IO05 2、IO复用 查找IMX6UL参考手册 和 STM32一样,如果某个 IO 要作为…

ctfshow sql注入

开启其他注入 web221 limit注入 给出查询语句 以及过滤逻辑为空 获取数据库名即可 limit 用于控制返回结果行数 limit后面似乎只能跟PROCEDURE ANALYSE( ) 函数了 PROCEDURE ANALYSE( ) 函数用于分析查询结果的函数 参数是用来控制函数的 这个参数的位置 可以放入报错函数 原…

centos7.9 python3环境(virtualenv)搭建及所遇错误

人望山,鱼窥荷,真正喜欢想要的,没有一样可以轻易得到。 目录 # 1. 解决版本冲突问题--建议不要跳过(一定要查看软链接是否链接正确) # 2. python3(virtualenv)环境搭建 # 3. virtualenv常用命令 # 4. 所遇错误解析 ## 4.1 遇到 No modul…

关于python编程从入门到实践书中的外星人项目的 if event.key == pygame.K_q: sys.exit()失败问题

按q没有退出程序。原因是输入法中英文问题。 本人默认输入法是搜狗,其他的输入法如微软拼音等都行,但是注意运行的时候切换为英文。千万记得运行时不是中,而是英,按q才能退出

【数据结构】堆栈

目录 一、堆栈的基本概念 1.1 堆栈定义 1.2 堆栈操作 1.3 堆栈应用 二、顺序栈 2.1 定义 2.2 操作 2.3 C语言实现 三、共享栈 3.1 定义 3.2 操作 3.3 C语言实现 四、链式栈 4.1 定义 4.2 操作 4.3 C语言实现 五、总结 堆栈(Stack)重要的数据结构,它…

Python--线程基础

相关概念 线程是"轻量级进程",是计算机中CPU进行任务调度的最小单位。 线程属于进程的一部分,一个线程只能属于一个进程,而一个进程可以有多个线程,且至少有一个线程。 每个进程开始的创建的时候,都会随之创建一个主线程。 进程负责分配和隔离资源(CPU, 内存…

机器学习辅助的乙醇浓度检测

目录 1.为什么要机器学习 2. 神经网络一般组成 3.BP神经网络工作过程 4.评价指标 5.实操代码 1.为什么要用机器学习 人工分析大量的谐振模式,建立各种WGM的响应与未知目标之间的关系,是一个很大的挑战。机器学习(ML)能够自行识别全谱的全部特征。作为…

【Python】Python中的常量与变量

常量与变量 导读一、新建项目二、常量2.1 字面常量2.2 特殊常量 三、变量3.1 变量的定义3.2 变量的命名3.2.1 关键字 结语 导读 大家好,很高兴又和大家见面啦!!! 在上一篇内容中我们详细介绍了Python环境的搭建过程,…

《代号鸢》国服,能否推动国乙市场重新洗牌?

灵犀互娱《如鸢》顺利拿到版号,再次搅浑了国乙市场这潭水。 六月份游戏版号审批公布后,灵犀互娱运营的《如鸢》引起了关注,这个与《代号鸢》原名《三国志如鸢》雷同的名字,竟然让《代号鸢》玩家大面积破防了。 其实目前关于《如…

游戏冻结工具 -- 雪藏HsFreezer v1.78

软件简介 HsFreezer是一款多功能游戏冻结工具,它允许用户随意暂停和继续游戏,同时具备系统优化和进程管理的功能。这款软件特别适合希望在游戏加载时间节省或在游戏与其他任务之间快速切换的用户。其主要特点包括快捷键操作、单锁模式的丝滑切换&#x…

湖北建筑安全员A证跨省调出审核不通过?可能是这些原因

湖北建筑安全员A证跨省调出审核不通过?可能是这些原因 湖北建筑安全员A证跨省调出审核不通过怎么办? 湖北建筑安全员ABC正常情况下都是可以跨省调出的,现在建筑三类人员安全员ABC在全国工程质量安全监管信息平台都是可以查询的,在…

《中国化工贸易》是什么级别的期刊?是正规期刊吗?能评职称吗?

​问题解答 问:《中国化工贸易》是不是核心期刊? 答:不是,是知网收录的第一批认定学术期刊。 问:《中国化工贸易》级别? 答:国家级。主管单位:中国石油和化学工业联合会 主办单…

据阿谱尔统计,全球mRNA原料酶市场预计2024年达到11.98亿美元

Codexis 宣布与 Aldevron 达成协议,授予 Aldevron Codexis 的 Codex HiCap RNA 聚合酶的全球制造和商业化权利。 Applied DNA、Maravai LifeSciences (MRVI) 和 Alphazyme 达成协议,Alphazyme 将扩大 Applied DNA 专有 Linea™ RNA 聚合酶 (RNAP) 的生产…