本文介绍:本专栏第一篇,文章不会讲太复杂的定义,将从小白的角度去进行讲解,为大家节省时间,高效去做实验,文章将会简单介绍一下相关模块(也会附带相关论文链接,有兴趣可以阅读,可以作为论文文献),本专栏主要专注于YOLO改进实战!适用于小白入门也可以水论文,大佬跳过!!!
文章适用人群:研一、准研究生(研0)、少部分本科生、着急毕业水论文的各位
更新时间:每天1~3篇左右,本专栏文章至少50+篇,如果需要单独设计可以联系我,咨询一下
售后:购买专栏后在运行时有问题直接私聊我,大部分都是环境配置问题,我会录教程带大家配置,保证代码畅通无阻!
新专栏福利:帮助入门小白缝合模块,创新模块(先到先得,名额有限,论文从此不缺创新点)!
专栏通知:专栏文章大于20篇时将涨价,早买有优惠,感谢各位读者支持!(更新时间:2024/11/18)
文章目录
- “SENet”同学简介
- 一、如何使用SENet
- 二、将主角“SENet”加入到YOLO大家庭(代码)中
- 三、模型文件中加入SENet注意力机制
- 四、直接开跑
- 五、附赠SENet(改进版)效果好可做创新点
- 总结
“SENet”同学简介
个人简介:大家好!
我是 SENet注意力机制,一个帮助计算机更聪明地看图的工具。有点像是给计算机配了一副“透视眼镜”。 在一张图片里,有很多很多的细节,而计算机并不总是知道该关注什么。我就像一个厉害的助手,能帮它找到哪些部分是重点,哪些可以略过。比如在一张有人、山、天空的照片中,我会告诉计算机:“嘿,关注那个站在山顶的人!” 具体来说,我的大招是“权重调整”:当我分析图像时,我会给图片里的各部分打分,更重要的部分得高分,这些部分的细节会被放大和强调。不那么重要的,就让它们安静地待在背景。有了这种帮助,计算机就能更好地识别和理解图像里的内容。 所以,如果说计算机是一个正在练习看图识字的小朋友,那我就算是它的小老师,站在旁边给它一点提示,确保它不会遗漏任何重要的东西。
专业版介绍(我知道大家肯定不爱看但可以理解用于写论文):
1、作用
SENet通过引入一个新的结构单元——“Squeeze-and-Excitation”(SE)块——来增强卷积神经网络的代表能力。是提高卷积神经网络(CNN)的表征能力,通过显式地建模卷积特征通道之间的依赖关系,从而在几乎不增加计算成本的情况下显著提升网络性能。SE模块由两个主要操作组成:压缩(Squeeze)和激励(Excitation)
2、机制
(1)、压缩操作:
SE模块首先通过全局平均池化操作对输入特征图的空间维度(高度H和宽度W)进行聚合,为每个通道生成一个通道描述符。这一步有效地将全局空间信息压缩成一个通道向量,捕获了通道特征响应的全局分布。这一全局信息对于接下来的重新校准过程至关重要。
(2)、激励操作:
在压缩步骤之后,应用一个激励机制,该机制本质上是由两个全连接(FC)层和一个非线性激活函数(通常是sigmoid)组成的自门控机制。第一个FC层降低了通道描述符的维度,应用ReLU非线性激活,随后第二个FC层将其投影回原始通道维度。这个过程建模了通道间的非线性交互,并产生了一组通道权重。
(3)、特征重新校准:
激励操作的输出用于重新校准原始输入特征图。输入特征图的每个通道都由激励输出中对应的标量进行缩放。这一步骤有选择地强调信息丰富的特征,同时抑制不太有用的特征,使模型能够专注于任务中最相关的特征。
3、独特优势
(1)、通道间依赖的显式建模:
SE Net的核心贡献是通过SE块显式建模通道间的依赖关系,有效地提升了网络对不同通道特征重要性的适应性和敏感性。这种方法允许网络学会动态地调整各个通道的特征响应,以增强有用的特征并抑制不那么重要的特征。
(2)、轻量级且高效:
尽管SE块为网络引入了额外的计算,但其设计非常高效,额外的参数量和计算量相对较小。这意味着SENet可以在几乎不影响模型大小和推理速度的情况下,显著提升模型性能。
(3)、模块化和灵活性:
SE块可以视为一个模块,轻松插入到现有CNN架构中的任何位置,包括ResNet、Inception和VGG等流行模型。这种模块化设计提供了极大的灵活性,使得SENet可以广泛应用于各种架构和任务中,无需对原始网络架构进行大幅度修改。
(4)、跨任务和跨数据集的泛化能力:
SENet在多个基准数据集上展现出了优异的性能,包括图像分类、目标检测和语义分割等多个视觉任务。这表明SE块不仅能提升特定任务的性能,还具有良好的泛化能力,能够跨任务和跨数据集提升模型的效果。
(5)、增强的特征表征能力:
通过调整通道特征的重要性,SENet能够更有效地利用模型的特征表征能力。这种增强的表征能力使得模型能够在更细粒度上理解图像内容,从而提高决策的准确性和鲁棒性。
一、如何使用SENet
SENet代码展示:
#-----------------SENet-------------------#
import numpy as np
import torch
from torch import nn
from torch.nn import init
class SEAttention(nn.Module):
# 初始化SE模块,channel为通道数,reduction为降维比率
def __init__(self, channel=512, reduction=16):
super().__init__()
self.avg_pool = nn.AdaptiveAvgPool2d(1) # 自适应平均池化层,将特征图的空间维度压缩为1x1
self.fc = nn.Sequential( # 定义两个全连接层作为激励操作,通过降维和升维调整通道重要性
nn.Linear(channel, channel // reduction, bias=False), # 降维,减少参数数量和计算量
nn.ReLU(inplace=True), # ReLU激活函数,引入非线性
nn.Linear(channel // reduction, channel, bias=False), # 升维,恢复到原始通道数
nn.Sigmoid() # Sigmoid激活函数,输出每个通道的重要性系数
)
# 权重初始化方法
def init_weights(self):
for m in self.modules(): # 遍历模块中的所有子模块
if isinstance(m, nn.Conv2d): # 对于卷积层
init.kaiming_normal_(m.weight, mode='fan_out') # 使用Kaiming初始化方法初始化权重
if m.bias is not None:
init.constant_(m.bias, 0) # 如果有偏置项,则初始化为0
elif isinstance(m, nn.BatchNorm2d): # 对于批归一化层
init.constant_(m.weight, 1) # 权重初始化为1
init.constant_(m.bias, 0) # 偏置初始化为0
elif isinstance(m, nn.Linear): # 对于全连接层
init.normal_(m.weight, std=0.001) # 权重使用正态分布初始化
if m.bias is not None:
init.constant_(m.bias, 0) # 偏置初始化为0
# 前向传播方法
def forward(self, x):
b, c, _, _ = x.size() # 获取输入x的批量大小b和通道数c
y = self.avg_pool(x).view(b, c) # 通过自适应平均池化层后,调整形状以匹配全连接层的输入
y = self.fc(y).view(b, c, 1, 1) # 通过全连接层计算通道重要性,调整形状以匹配原始特征图的形状
return x * y.expand_as(x) # 将通道重要性系数应用到原始特征图上,进行特征重新校准
# 示例使用
if __name__ == '__main__':
input = torch.randn(50, 512, 7, 7) # 随机生成一个输入特征图
se = SEAttention(channel=512, reduction=8) # 实例化SE模块,设置降维比率为8
output = se(input) # 将输入特征图通过SE模块进行处理
print(output.shape) # 打印处理后的特征图形状,验证SE模块的作用
使用步骤:
- 在任意路径下新建一个"SENet.py"将上述代码粘贴进去出现下面,不报错说明没问题,否则不可直接用,如图1(提供的代码准确无误),环境一定要调成本专栏专用的!!!
图1(上图)
二、将主角“SENet”加入到YOLO大家庭(代码)中
使用步骤:
- 找到并打开“YOLOv8\ultralytics-main\ultralytics\nn\modules\block.py”文件,在箭头位置添加SENet模块,如图2
- 复制提供的SENet代码粘贴进block.py的55行(你们的不一定是55行,注意附近内容),如图3
- 在包结构中导入SEAttention类,注意看,这步最容易错,图中有详解,注意在哪个文件里改,如图4和图5
- 找到task.py导入你的新模块,task.py位置和模块导入方法,如图6
- 编写parse_model函数,首先你需要按住Ctrl和F,搜索def parse_model并跳转到这个函数的定义,然后开始往下滑找到如图7所示的位置(行数不一定相同,注意周围内容),并且输入解析函数,解析函数如下:
elif m is SEAttention:
c1 = ch[f]
args = [c1,args[1]]
图2(上图)
图3(上图)
图4(上图)
图5(上图)
图6(上图)
图7(上图)
三、模型文件中加入SENet注意力机制
步骤:
1.如图8所示,在相应位置创建ADD文件夹(用于存放新建的模型文件)和yolov8-SENet.yaml模型文件
2.复制yolov8-SENet.yaml文件内容到新建的yolov8-SENet.yaml文件中并保存。
图8(上图)
yolov8-SENet.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 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, 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, SEAttention, [512, 16]] # 插入SEAttention模块,假设这里输入通道数为512,reduction为16
- [-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, Conv, [256, 3, 2]]
- [[-1, 13], 1, Concat, [1]] # cat head P4
- [-1, 3, C2f, [512]] # 18 (P4/16-medium)
- [-1, 1, Conv, [512, 3, 2]]
- [[-1, 10], 1, Concat, [1]] # cat head P5
- [-1, 3, C2f, [1024]] # 21 (P5/32-large)
- [[16, 19, 22], 1, Detect, [nc]] # Detect(P3, P4, P5)
四、直接开跑
步骤:
1.运行train.py出现如图9所示即为运行成功
图9(上图)
五、附赠SENet(改进版)效果好可做创新点
代码如下所示:(添加方式与上面相同,不会的私信我!)
# ------------------------------------SENet改进版(有需要可用增强版)--------------------------------------------#
"""
如果增强版对你的任务map和精度有提高的话可以应用于论文,改进皆为原创!
增强版模块使用前需要注册模块哦!如果想使用增强版可以私聊我,人多的话出教程,人少的话就私聊了!
"""
import torch
from torch import nn
class DynamicSEAttention(nn.Module):
def __init__(self, channel=512, min_reduction=4, max_reduction=16):
super().__init__()
self.avg_pool = nn.AdaptiveAvgPool2d(1)
# Dynamic layer to learn adaptive reduction ratio
self.dynamic_fc = nn.Linear(channel, 1, bias=False)
self.fc = nn.Sequential(
nn.ReLU(inplace=True),
nn.Sigmoid()
)
self.min_reduction = min_reduction
self.max_reduction = max_reduction
def forward(self, x):
b, c, _, _ = x.size()
avg_out = self.avg_pool(x).view(b, c)
# Calculate reduction ratio and clamp it
reduction_ratio = self.dynamic_fc(avg_out).view(b)
reduction_ratio = torch.clamp(reduction_ratio, self.min_reduction, self.max_reduction)
# Dynamic fully connected layers based on reduction ratio
importance = torch.zeros_like(avg_out)
for i in range(b):
reduced_features = nn.Linear(c, c // int(reduction_ratio[i]))(avg_out[i].view(1, -1))
scaled_features = nn.Linear(c // int(reduction_ratio[i]), c)(nn.ReLU()(reduced_features))
importance[i] = self.fc(scaled_features).view(-1)
importance = importance.view(b, c, 1, 1)
return x * importance
class MultiScaleSEAttention(nn.Module):
def __init__(self, channel=512):
super().__init__()
self.avg_pool = nn.AdaptiveAvgPool2d(1)
self.scale1 = nn.Conv2d(channel, channel, kernel_size=1)
self.scale3 = nn.Conv2d(channel, channel, kernel_size=3, padding=1)
self.scale5 = nn.Conv2d(channel, channel, kernel_size=5, padding=2)
self.fc = nn.Sequential(
nn.Linear(channel * 3, channel, bias=False), # Combine three scales
nn.ReLU(inplace=True),
nn.Linear(channel, channel, bias=False),
nn.Sigmoid()
)
def forward(self, x):
b, c, h, w = x.size()
scale1_out = self.scale1(x).view(b, c, -1)
scale3_out = self.scale3(x).view(b, c, -1)
scale5_out = self.scale5(x).view(b, c, -1)
combined = torch.cat((scale1_out, scale3_out, scale5_out), dim=1)
importance = self.fc(combined.mean(dim=-1)).view(b, c, 1, 1)
return x * importance
# Example usage
if __name__ == '__main__':
input = torch.randn(50, 512, 7, 7)
dynamic_se = DynamicSEAttention(channel=512)
multi_scale_se = MultiScaleSEAttention(channel=512)
output_dynamic = dynamic_se(input)
output_multiscale = multi_scale_se(input)
print("Output shape with dynamic SE: ", output_dynamic.shape)
print("Output shape with multi-scale SE: ", output_multiscale.shape)
模块改进点:
动态缩放机制:
- 动态的降维率:我们引入了一个
dynamic_fc
层,用于动态计算各输入样本的降维率。这使得我们的模块能根据输入自动决定处理的“宽松”或“严格”程度,从而提高兼容性和灵活性。- 创新点:这种自适应方法在图像特征的聚合和扩展中提供了更好的适配性,能够适应不同复杂度和特征规模的输入图像。
多尺度特征整合:
- 多尺度卷积核:使用不同尺寸的卷积核(1x1、3x3和5x5)来抽取不同尺度的特征,分别进行加权,然后结合输出。通过多尺度的视角,模型能够更全面地捕捉图像中的关键信息。
- 创新点:此方法扩展了原始SE模块的识别能力,使其能更好地处理各种大小的图像特征,提高对复杂场景的识别能力。
上下文感知:
- 增加特征整合:虽然未在代码中完全实现,但可以进一步扩展到增加位置编码或空间注意机制来提升模块对上下文信息的处理能力。
- 创新点:考虑空间关系和上下文信息能帮助模块在复杂图像处理任务中获取更好的性能。
总结:
SENet 的适用范围:
-
图像分类:
- 适用于需要提高分类精度的图像分类任务,如物体识别和动物分类。
- 尤其适合在标准化数据集(如 ImageNet)上提升模型性能。
-
简单场景识别:
- 对于背景简单、目标明确的图像类型,SENet 可以加强对关键特征的提取。
改进的 SENet 的适用范围:
-
多任务学习:
- 适用于多任务架构中的任务,例如同时处理分类、检测和分割任务。
-
图像检测:
- 在目标检测任务中,可以更准确地识别和定位各种大小的物体。
-
复杂场景与多尺度分析:
- 适合处理复杂背景或聚合多尺度特征的场景,例如交通监控中的多车道检测。
- 在需要捕捉多个尺度上下文的图像中表现优异。
-
动态图像和视频分析:
- 适应实时视频流中的多变模式,能够在视频帧处理上提供优势。
-
环境和生态监测:
- 在需要监测复杂生态系统的多样性任务中,能更好地识别不同生态特征。