YOLOv8改进 | SAConv可切换空洞卷积(附修改后的C2f+Bottleneck)

news2024/11/28 12:37:30

论文地址:官方论文地址

代码地址:官方代码地址

一、本文介绍

本文给大家带来的改进机制是可切换的空洞卷积(Switchable Atrous Convolution, SAC)是一种创新的卷积网络机制,专为增强物体检测分割任务中的特征提取而设计。SAC的核心思想是在相同的输入特征上应用不同的空洞率进行卷积,并通过特别设计的开关函数来融合这些不同卷积的结果。这种方法使得网络能够更灵活地适应不同尺度的特征,从而更准确地识别和分割图像中的物体。 通过本文你能够了解到:可切换的空洞卷积的基本原理和框架,能够在你自己的网络结构中进行添加(值得一提的是一个SAConv大概可以降低0.3GFLOPs)。

  专栏回顾:YOLOv8改进系列专栏——本专栏持续复习各种顶会内容——科研必备 

实验效果对比->

因为资源有限我发的文章都要做对比实验所以本次实验我只用了一百张图片检测的是安全帽训练了一百个epoch,该结果只能展示出该机制有效,但是并不能产生决定性结果,因为具体的效果还要看你的数据集和实验环境所影响。 

下图分析->这个SAConv精度提升是有一定效果的,但是其主要的优势我在于是可以减少参数量和轻量化的作用。 

目录

一、本文介绍

二、SAConv的机制原理介绍

三、SAConv代码复现 

3.1 SAConv

3.2 替换SAConv的C2f和Bottleneck

四、手把手教你添加SAConv 

4.1 SAConv的添加教程

4.2 SAConv的yaml文件和训练截图

5.2.1 SAConv的yaml文件

5.2.2 SAConv的训练过程截图 

五、SAConv可添加的位置

5.1 推荐SAConv可添加的位置 

5.2图示LSKAttention可添加的位置 

六、本文总结


 

二、SAConv的机制原理介绍

可切换的空洞卷积(Switchable Atrous Convolution,简称SAC)是一种高级的卷积机制,用于在物体检测和分割任务中增强特征提取。以下是SAC的主要原理和机制:

1. 不同空洞率的应用: SAC的核心思想是对相同的输入特征应用不同的空洞率进行卷积。空洞卷积通过在卷积核中引入额外的空间(即空洞),扩大了感受野,而不增加参数数量或计算量。SAC利用这一点来捕获不同尺度的特征。

2. 开关函数的使用: SAC的另一个关键特点是使用开关函数来组合不同空洞率卷积的结果。这些开关函数是空间依赖的,意味着特征图的每个位置可能有不同的开关来控制SAC的输出,从而使网络对于特征的大小和尺度更加灵活。

3. 转换机制: SAC能够将传统的卷积层转换为SAC层。这是通过在不同空洞率的卷积操作中使用相同的权重(除了一个可训练的差异)来实现的。这种转换机制包括一个平均池化层和一个1x1卷积层,以实现开关功能。

4. 结构设计: SAC的架构包括三个主要部分:两个全局上下文模块分别位于SAC组件的前后。这些模块有助于更全面地理解图像内容,使SAC组件能够在更宽泛的上下文中有效地工作。

总结:SAC通过这些创新的设计和机制,提高了网络在处理不同尺度和复杂度的特征时的适应性和准确性,从而在物体检测和分割领域显示出显著的性能提升。

 上图我们能看到其中的关键点如下->

  • 双重观察机制: SAC特别设计了一种机制,它能够对输入特征进行两次观察,但每次使用不同的空洞率。这意味着,同一组输入特征会被两种不同配置的卷积核处理,其中每种配置对应一种特定的空洞率。这样做可以捕获不同尺度的特征信息,从而更全面地理解和分析输入数据。

  • 开关函数的应用: 不同空洞率得到的输出结果随后通过开关函数结合在一起。这些开关决定了如何从两次卷积中选择或融合信息,从而生成最终的输出特征。开关的运作方式可能依赖于特征本身的特性,如其空间位置等。

总结:SAC通过这种“双重观察并结合”的策略,能够有效地处理复杂的特征模式,特别是在尺度变化较大的情况下。这种方法不仅提高了特征提取的灵活性和适应性,而且还提升了物体检测和分割任务中的准确性和效率。

在上图中展示了可切换的空洞卷积(Switchable Atrous Convolution, SAC)的具体实现方式。这里的关键点包括:

  1. 转换传统卷积层为SAC: 他们将骨干网络ResNet中的每一个3x3卷积层都转换为SAC。这种转换使得卷积计算可以在不同的空洞率之间软切换。

  2. 权重共享与训练差异: 重要的一点是,尽管SAC在不同的空洞率间进行切换,但所有这些操作共享相同的权重,只有一个可训练的差异。这种设计减少了模型复杂性,同时保持了灵活性。

  3. 全局上下文模块: SAC结构还包括两个全局上下文模块,这些模块为特征添加了图像级的信息。全局上下文模块有助于网络更好地理解和处理图像的整体内容,从而提高特征提取的质量和准确性。

总结:SAC通过这些机制,允许网络在不同的空洞率之间灵活切换,同时通过全局上下文模块和共享权重的策略,有效地提升了特征的提取和处理能力。这些特性使得SAC在物体检测和分割任务中表现出色。

下面是部分的检测效果图-> 

 

三、SAConv代码复现 

3.1 SAConv

import torch
import torch.nn as nn
from ultralytics.nn.modules.conv import autopad, Conv


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, d),
            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 = Conv.default_act 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))

3.2 替换SAConv的C2f和Bottleneck

class Bottleneck_SAConv(nn.Module):
    """Standard bottleneck."""

    def __init__(self, c1, c2, shortcut=True, g=1, k=(3, 3), e=0.5):
        """Initializes a bottleneck module with given input/output channels, shortcut option, group, kernels, and
        expansion.
        """
        super().__init__()
        c_ = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, c_, k[0], 1)
        self.cv2 = SAConv2d(c_, c2, k[1], 1, g=g)
        self.add = shortcut and c1 == c2

    def forward(self, x):
        """'forward()' applies the YOLO FPN to input data."""
        return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))
    
    
class C2f_SAConv(nn.Module):
    """Faster Implementation of CSP Bottleneck with 2 convolutions."""

    def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5):
        """Initialize CSP bottleneck layer with two convolutions with arguments ch_in, ch_out, number, shortcut, groups,
        expansion.
        """
        super().__init__()
        self.c = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, 2 * self.c, 1, 1)
        self.cv2 = Conv((2 + n) * self.c, c2, 1)  # optional act=FReLU(c2)
        self.m = nn.ModuleList(Bottleneck_SAConv(self.c, self.c, shortcut, g, k=((3, 3), (3, 3)), e=1.0) for _ in range(n))

    def forward(self, x):
        """Forward pass through C2f layer."""
        x = self.cv1(x)
        x = x.chunk(2, 1)
        y = list(x)
        # y = list(self.cv1(x).chunk(2, 1))
        y.extend(m(y[-1]) for m in self.m)
        return self.cv2(torch.cat(y, 1))

    def forward_split(self, x):
        """Forward pass using split() instead of chunk()."""
        y = list(self.cv1(x).split((self.c, self.c), 1))
        y.extend(m(y[-1]) for m in self.m)
        return self.cv2(torch.cat(y, 1))

四、手把手教你添加SAConv 

4.1 SAConv的添加教程

添加教程这里不再重复介绍、因为专栏内容有许多,添加过程又需要截特别图片会导致文章大家读者也不通顺如果你已经会添加注意力机制了,可以跳过本章节,如果你还不会,大家可以看我下面的文章,里面详细的介绍了拿到一个任意机制(C2f、Conv、Bottleneck、Loss、DetectHead)如何添加到你的网络结构中去。

这个卷积也可以放在C2f和Bottleneck中进行使用可以即插即用,个人觉得放在Bottleneck中效果比较好。

添加教程->YOLOv8改进 | 如何在网络结构中添加注意力机制、C2f、卷积、Neck、检测头

4.2 SAConv的yaml文件和训练截图

5.2.1 SAConv的yaml文件

下面的是放在Neck部分的截图,参数我以及设定好了,无需进行传入会根据模型输入自动计算,帮助大家省了一些事。

下面的是放在C2f中的yaml配置。 

5.2.2 SAConv的训练过程截图 

下面是添加了SAConv的训练截图。

下面的是将SAConv机制添加到了C2f和Bottleneck。

 

五、SAConv可添加的位置

5.1 推荐SAConv可添加的位置 

SAConv可以是一种即插即用的卷积,其可以添加的位置有很多,添加的位置不同效果也不同,所以我下面推荐几个添加的位,置大家可以进行参考,当然不一定要按照我推荐的地方添加。

  1. 残差连接中:在残差网络的残差连接中加入SAConv

  2. Neck部分:YOLOv8的Neck部分负责特征融合,这里添加修改后的C2f和SAConv可以帮助模型更有效地融合不同层次的特征。

  3. 检测头中的卷积:在最终的输出层前加入SAConv可以使模型在做出最终预测之前,更加集中注意力于最关键的特征。

文字大家可能看我描述不太懂,大家可以看下面的网络结构图中我进行了标注。

5.2图示LSKAttention可添加的位置 

六、本文总结

到此本文的正式分享内容就结束了,在这里给大家推荐我的YOLOv8改进有效涨点专栏,本专栏目前为新开的平均质量分98分,后期我会根据各种最新的前沿顶会进行论文复现,也会对一些老的改进机制进行补充,目前本专栏免费阅读(暂时,大家尽早关注不迷路~),如果大家觉得本文帮助到你了,订阅本专栏,关注后续更多的更新~

专栏回顾:YOLOv8改进系列专栏——本专栏持续复习各种顶会内容——科研必备

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Flutter | TextField长按时选项菜单复制、粘贴显示为英文问题解决

Flutter | TextField长按时选项菜单复制、粘贴显示为英文问题解决 问题描述: 长按TextField后,显示剪切、复制等选项为英文,如下图所示,这是因为问未设置语言本地化,我们需要进行设置。 首先在pubspec.yaml加入以下依赖…

Dempster-Shafer(D-S)证据理论的基本定义和详细分析,优点,缺点,应用!!(系列1)

文章目录 前言一、D-S证据理论的应用:二、D-S证据理论的优点:三、D-S证据理论的缺陷:四、D-S组合规则:总结 前言 Dempster-Shafer(D-S)证据理论是一种不精确推理理论,也称为Dempster/Shafer证据…

BC76 [NOIP2008]ISBN号码

#include<stdio.h> int main() {char arr[13]; //存放13位的ISBNint i, j;scanf("%s",arr);int s 0;for(i0, j1; i<11; i){if(arr[i] ! -){s (arr[i]-0)*j; //将字符换成int累加&#xff1a;0162……29158j; //执行if的时候加&#xff0c;不执行不加…

P14 C++局部静态变量static延长生命周期

目录 01 前言 02 变量的作用域与生命周期 2.1 什么是作用域&#xff1a; 2.2 什么是变量的生命周期&#xff1a; 03 局部静态 3.1非静态变量例子 3.2静态变量例子 04 全局变量 05 后话 01 前言 在前几期里&#xff0c;我们了解了static关键字在特定上下文中的含义。 …

大厂工作的跨域架构师,最好掌握这4个原则

今天学习的主要内容如下&#xff1a; 什么是跨域架构师&#xff0c;在职责上跟单域架构师有什么区别&#xff1f;跨域架构师的勇气重要&#xff0c;还是技术重要&#xff1f;如何面对多领域之间的冲突&#xff0c;又如何解决冲突&#xff1f;我是一名跨域架构师&#xff0c;可以…

JavaScript 表达式

JavaScript 表达式 目录 JavaScript 表达式 一、赋值表达式 二、算术表达式 三、布尔表达式 四、字符串表达式 表达式是一个语句的集合&#xff0c;计算结果是个单一值。 在JavaScript中&#xff0c;常见的表达式有4种&#xff1a; &#xff08;1&#xff09;赋值表达式…

Xshell连接VMware虚拟机中的CentOS

Xshell连接VMware虚拟机中的CentOShttps://www.cnblogs.com/niuben/p/13157291.html 步骤&#xff1a; 1. 检查Linux虚拟机的网络连接模式&#xff0c;确保它是NAT模式。&#xff08;由于只在本机进行连接&#xff0c;所以没有选择桥接模式。当然&#xff0c;桥接模式的配置会…

中东客户亲临广东育菁装备参观桌面型数控机床生产

近日&#xff0c;中东地区的一位重要客户在广东育菁装备有限公司的热情接待下&#xff0c;深入了解了该公司生产的桌面型数控机床。这次会面不仅加强了双方在业务领域的交流&#xff0c;也为中国与中东地区的经济合作描绘出更美好的前景。 在育菁装备公司各部门主要负责人及工作…

[Java]线程详解

Java线程 一、线程介绍 程序 是为完成特定任务、用某种语言编写的一组指令的集合&#xff08;简单来说就是写的代码&#xff09;。 进程 进程是指运行中的程序&#xff0c;比如我们使用的QQ&#xff0c;就启动了一个进程&#xff0c;操作系统会对该进程分配内存空间。当我…

使用不平衡数据集练习机器学习

一、介绍 在当今世界&#xff0c;机器学习和人工智能几乎被广泛应用于每个领域&#xff0c;以提高绩效和结果。但如果没有数据&#xff0c;它们还有用吗&#xff1f;答案是否定的。机器学习算法严重依赖我们提供给它们的数据。我们提供给算法的数据质量在很大程度上决定了机器学…

LangChain 11实现思维树Implementing the Tree of Thoughts in LangChain’s Chain

思维之树&#xff08; Tree of Thoughts ToT&#xff09;是一个算法&#xff0c;它结合了普林斯顿大学和谷歌DeepMind在本文中提出的大型语言模型&#xff08;LLMs&#xff09;和启发式搜索。看起来这个算法正在被实现到谷歌正在开发的多模式生成AI Gemini中。 现在&#xff0…

vue2-009——mixin混入

一、基础使用案例 1.1、定义混合 1.2、引入混合 1.2.1、局部混入 1.2.2、全局引入 二、注意点 1、组件和混合有同名变量时&#xff0c;以组件的变量为主 2、组件和混合有同名生命周期钩子时&#xff0c;两者都会生效

c盘清理——常用方法和工具整理

背景 最近c盘满了&#xff0c;只剩下1-2G&#xff0c;周末有空清理一下。对这块不太熟悉&#xff0c;下面只是把今天网上看到的比较好用的工具整理一下。 使用工具 磁盘大小查看工具——TreeSize&#xff08;收费&#xff09; 之前都是右键一个个看每个文件的大小&#xff0…

Flutter桌面应用开发之毛玻璃效果

目录 效果实现方案依赖库支持平台实现步骤注意事项话题扩展 毛玻璃效果&#xff1a;毛玻璃效果是一种模糊化的视觉效果&#xff0c;常用于图像处理和界面设计中。它可以通过在图像或界面元素上应用高斯模糊来实现。使用毛玻璃效果可以增加图像或界面元素的柔和感&#xff0c;同…

什么是 Node.js?

在 Node.js 出现之前&#xff0c;最常见的 JavaScript 运行时环境是浏览器&#xff0c;也叫做 JavaScript 的宿主环境。浏览器为 JavaScript 提供了 DOM API&#xff0c;能够让 JavaScript 操作浏览器环境&#xff08;JS 环境&#xff09;。 2009 年初 Node.js 出现了&#xf…

USB简介系列-02

系列文章目录 USB简介之二 文章目录 系列文章目录USB数据流一、USB总线二、USB收发器三、USB速率识别三、USB总线状态总结USB数据流 本部分讨论USB低速和全速模式下的数据流。 一、USB总线 想象一下USB主机根集器下级联了集线器和设备的设置,如下图示。我们需要记住的是,在…

FFmpeg零基础学习(二)——视频文件信息获取

目录 前言正文一、获取宽高信息1、核心代码2、AVFormatContext3、avformat_alloc_context4、avformat_open_input5、avformat_find_stream_info6、av_dump_format7、av_find_best_stream End、遇到的问题1、Qt Debug模式avformat_alloc_context 无法分配对象&#xff0c;而Rele…

go对rabbitmq基本操作

一、安装rabbitmq 1、直接使用docker拉取镜像 docker pull rabbitmq:3.82、启动容器 docker run \-e RABBITMQ_DEFAULT_USERadmin \-e RABBITMQ_DEFAULT_PASS123456 \-v mq-plugins:/plugins \--name rabbit01 \--hostname rabbit01 --restartalways \-p 15672:15672 \-p 5672:…

为什么高斯核是实现尺度空间变换的唯一变换核,并且是唯一的线性核?再研究

请先看&#xff0c;我们前面一篇&#xff0c;尺度为什么是sigma。 下面要说的是&#xff0c;我们研究的是:g&#xff08;x,y,sigma&#xff09;和g&#xff08;x,y,k*sigma&#xff09;的关系 而不是&#xff1a;I(x,y)和g&#xff08;x,y,sigma&#xff09;之间的关系 也不…

十分钟让你搞懂JVM中的GC垃圾回收机制(分代回收)

文章目录 0. 为什么要有垃圾回收?1. 垃圾回收哪个内存区域?2. 如何找到垃圾(死亡对象的判断)2.1 引用计数法2.2 可达性分析法2.3 两种算法的差别 3. 如何清理垃圾(死亡对象的回收)3.1 标记-清楚法3.2 复制法3.3 标记-整理法 4. JVM使用的回收方法4.1 什么是分代回收4.2 哪些对…