YOLOv8改进 | 主干篇 | 利用SENetV1改进网络结构 (ILSVRC冠军得主)

一、本文介绍

本文给大家带来的改进机制是SENet(Squeeze-and-Excitation Networks)其是一种通过调整卷积网络中的通道关系来提升性能的网络结构。SENet并不是一个独立的网络模型,而是一个可以和现有的任何一个模型相结合的模块(可以看作是一种通道型的注意力机制)。在SENet中,所谓的挤压和激励(Squeeze-and-Excitation)操作是作为一个单元添加到传统的卷积网络结构中,如残差单元中(后面我会把修改好的残差单元给大家大家直接复制粘贴即可使用)。这样可以增强模型对通道间关系的捕获,提升整体的特征表达能力,而不需要从头开始设计一个全新的网络架构。因此,SENet可以看作是对现有网络模型的一种改进和增强(亲测大中小三中目标检测上都有一定程度的涨点效果)。

推荐指数:⭐⭐⭐⭐⭐

涨点效果:⭐⭐⭐⭐⭐

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

训练结果对比图->  

后面我会将这个机制SENetV1和SENetV2添加到多个网络结构中进行融合涨点,可能也会和检测头融合,将模型发布给大家使用。

二、SENetV1框架原理

论文地址:官方论文地址

代码地址:官方代码地址


SENet(Squeeze-and-Excitation Networks)的主要思想在于通过挤压-激励(SE)块强化了网络对通道间依赖性的建模。这一创新的核心在于自适应地重新校准每个通道的特征响应,显著提升了网络对特征的表示能力。SE块的叠加构成了SENet架构,有效提高了模型在不同数据集上的泛化性。SENet的创新点包括其独特的结构设计,它在增加极少计算成本的情况下,为现有CNN模型带来了显著的性能提升,并在国际图像识别竞赛ILSVRC 2017中取得了突破性的成果

上图展示了一个挤压-激励(Squeeze-and-Excitation, SE)块的结构。输入特征图 X 经过一个变换 F_{tr}后产生特征图 U。然后,特征图U被压缩成一个全局描述子,这是通过全局平均池化 F_{sq}实现的,产生一个通道描述子。这个描述子经过两个全连接层 F_{ex},第一个是降维,第二个是升维,并通过激活函数如ReLU和Sigmoid激活。最后,原始特征图 U与学习到的通道权重 F_{scale}相乘,得到重新校准的特征图 hat{X}。这种结构有助于网络通过学习通道间的依赖性,自适应地强化或抑制某些特征通道。 

上面的图片展示了两种神经网络模块的结构图:Inception模块和残差(ResNet)模块。每个模块都有其标准形式和一个修改形式,对比图融入了Squeeze-and-Excitation (SE)块来提升性能。

左面的部分是原始Inception模块(左)和SE-Inception模块(右)。SE-Inception模块通过全局平均池化和两个全连接层(第一个使用ReLU激活函数,第二个使用Sigmoid函数)来生成通道级权重,然后对输入特征图进行缩放。

右面的部分展示了原始残差模块(左)和SE-ResNet模块(右)。SE-ResNet模块在传统的残差连接之后添加了SE块,同样使用全局平均池化和全连接层来获得通道级权重,并对残差模块的输出进行缩放。

这两个修改版模块都旨在增强网络对特征的重要性评估能力,从而提升整体模型的性能。


三、SENetV1核心代码

下面的代码是MSDA的核心代码,我们将其复制导'ultralytics/nn/modules'目录下,在其中创建一个文件,我这里起名为Dilation然后粘贴进去,其余使用方式看章节四。

import torch
from torch import nn
from .conv import Conv
class SELayerV1(nn.Module):
    def __init__(self, channel, reduction=16):
        super(SELayerV1, self).__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 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)

class Bottleneck(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 = Conv(c_, c2, k[1], 1, g=g)
        self.SE = SELayerV1(c2)
        self.add = shortcut and c1 == c2

    def forward(self, x):
        """'forward()' applies the YOLO FPN to input data."""
        return x + self.SE(self.cv2(self.cv1(x))) if self.add else self.SE(self.cv2(self.cv1(x)))


class C2f_SENetV1(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(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."""
        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))


四、手把手教你添加SENetV1模块

4.1 SENetV1添加步骤

4.1.1 步骤一

首先我们找到如下的目录'ultralytics/nn/modules',然后在这个目录下创建一个py文件,名字为你也可以根据你自己的习惯起即可,然后将核心代码复制进去。

4.1.2 步骤二

之后我们找到'ultralytics/nn/tasks.py'文件,在其中注册我们的模块。

首先我们需要在文件的开头导入我们的模块,如下图所示->

4.1.3 步骤三

我们找到parse_model这个方法,可以用搜索也可以自己手动找,大概在六百多行吧。 我们找到如下的地方,然后将模块按照我的方法添加进去即可,模仿我添加即可,其中另外的模块,你没有删除即可,添加红框的内容即可。

到此我们就注册成功了,可以修改yaml文件使用我们添加的模块了。


4.2 SENetV1的yaml文件和训练截图

下面推荐几个版本的yaml文件给大家,大家可以复制进行训练,但是组合用很多具体那种最有效果都不一定,针对不同的数据集效果也不一样,我不可每一种都做实验,所以我下面推荐了几种我自己认为可能有效果的配合方式,你也可以自己进行组合。


4.2.1 SENetV1的yaml版本一(推荐)

下面的版本我在大中小三个检测层的输出部分添加了SENetV1(实验版本也是我根据这个yaml文件跑出来的),大家可以根据自己的需求,减少SENetV1比如你做的小目标检测,那么可以把另外两个去去掉,但是别忘了修改检测通道数,要不然会报错。

# 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 GFLOP

# 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, C2f, [128, True]]
  - [-1, 1, Conv, [256, 3, 2]]  # 3-P3/8
  - [-1, 6, C2f, [256, True]]
  - [-1, 1, Conv, [512, 3, 2]]  # 5-P4/16
  - [-1, 6, C2f, [512, True]]
  - [-1, 1, Conv, [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, SELayerV2, []]  # 16

  - [-1, 1, Conv, [256, 3, 2]]
  - [[-1, 12], 1, Concat, [1]]  # cat head P4
  - [-1, 3, C2f, [512]]  # 19 (P4/16-medium)
  - [-1, 1, SELayerV2, []]  # 20

  - [-1, 1, Conv, [512, 3, 2]]
  - [[-1, 9], 1, Concat, [1]]  # cat head P5
  - [-1, 3, C2f, [1024]]  # 23 (P5/32-large)
  - [-1, 1, SELayerV2, []]  # 24

  - [[16, 20, 24], 1, Detect, [nc]]  # Detect(P3, P4, P5)


4.2.2 SENetV1的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
  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 GFLOP

# 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, C2f, [128, True]]
  - [-1, 1, Conv, [256, 3, 2]]  # 3-P3/8
  - [-1, 6, C2f, [256, True]]
  - [-1, 1, Conv, [512, 3, 2]]  # 5-P4/16
  - [-1, 6, C2f, [512, True]]
  - [-1, 1, Conv, [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_SENetV1, [256]]  # 15 (P3/8-small)

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

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

  - [[11, 14, 17], 1, Detect, [nc]]  # Detect(P3, P4, P5)

4.3 推荐SENetV1可添加的位置 

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

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

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

  3. 能添加的位置很多,一篇文章很难全部介绍到,后期我会发文件里面集成上百种的改进机制,然后还有许多融合模块,给大家。


4.4 SENetV1的训练过程截图 

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

大家可以看下面的运行结果和添加的位置所以不存在我发的代码不全或者运行不了的问题大家有问题也可以在评论区评论我看到都会为大家解答(我知道的)。

​​​​​​


五、本文总结

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

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

​​


http://www.niftyadmin.cn/n/5287943.html

相关文章

小区物业大数据监控服务平台(PSD文件资料)

现分享简约物业大数据平台页面、小区物业大数据监控服务平台模版的UI源文件,供设计师们快速获取PS源文件完成工作。 若需 更多行业 相关的大屏,请移步小7的另一篇文章:200套精选数据可视化大屏,大屏PSD设计(各行业大屏…

Linux:查询当前进程或线程的资源使用情况

目录 一、/proc/[PID]/下的各个文件1、proc简介2、/proc/[PID]/详解 二、通过Linux API获取当前进程或线程的资源使用情况1、getrusage2、sysinfo3、times 在工作中,我们排除app出现的一些性能/资源问题时,通常要先知道当前app的资源使用情况&#xff0c…

jmeter-set up先登录获取token,再测试

一、何为setup 一种特殊类型的线程组,可用于执行预测试操作;简单来讲就是执行测试线程组前,先执行setup 作用 例如前面,我们说到的,压测之前只用JMeter调用业务接口造数或者通过JDBC操作数据库造数,可以放…

MongoDB ReplicaSet 部署

文章目录 前言1. 环境准备2. 生成密钥3. 配置参数4. 创建 ReplicaSet5. 副本集维护5.1 新增成员5.2 移除节点5.4 主节点降级5.5 阻止选举5.6 允许副本节点读5.7 延迟观测 6. 连接副本集 后记 前言 本篇文章介绍 MongoDB ReplicaSet 如何搭建,及常用的维护方法。 1…

10款有趣的前端特效代码分享(附图文效果及在线预览)

分享10款非常有趣的前端特效源码 其中包含css动画特效、js原生特效、svg特效以及小游戏等 下面我会给出特效样式图或演示效果图 但你也可以点击在线预览查看源码的最终展示效果及下载源码资源 纯CSS绘制相机拍照动画 纯CSS绘制相机拍照动画 会有左右调整角度以及打开快门声的…

普中STM32-PZ6806L开发板(烧录方式)

前言 有两种方式, 串口烧录和STLink方式烧录;串口烧录 步骤 开发板USB转串口CH340驱动板接线到USB连接PC使用自带工具普中自动下载软件.exe烧录程序到开发板 ST Link方式 这种方式需要另外进行供电, 我买的如下,当年用于调试STM8的,也可…

29--多线程的创建与生命周期

1、相关概念 1.1 程序、进程与线程 程序(program):为完成特定任务,用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。 进程(process):程序的一次执行过程&#xff…

机器学习笔记(三)简单手写识别

目标 实现一个简单的手写识别的脚本,同样的,流程分五步走: 读入数据初始化模型训练模型训练样本集乱序校验数据有效性 前期准备 前期需要将库导入,还需要进行一些初始化操作 #数据处理部分之前的代码,加入部分数据…