yolov8添加注意力机制模块-ShuffleAttention

2024-02-27 03:04

本文主要是介绍yolov8添加注意力机制模块-ShuffleAttention,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

修改

原本打算把ShuffleAttention模块先写进conv.py文件中,然后在引入tasks.py文件中。但是不知道咋回事,在tasks.py文件中引入报红。所以干脆直接把ShuffleAttention模块写进了tasks.py文件中。

from torch.nn import init
from torch.nn.parameter import Parameterclass ShuffleAttention(nn.Module):def __init__(self, channel=512, reduction=16, G=8):super().__init__()self.G = Gself.channel = channelself.avg_pool = nn.AdaptiveAvgPool2d(1)self.gn = nn.GroupNorm(channel // (2 * G), channel // (2 * G))self.cweight = Parameter(torch.zeros(1, channel // (2 * G), 1, 1))self.cbias = Parameter(torch.ones(1, channel // (2 * G), 1, 1))self.sweight = Parameter(torch.zeros(1, channel // (2 * G), 1, 1))self.sbias = Parameter(torch.ones(1, channel // (2 * G), 1, 1))self.sigmoid = 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)@staticmethoddef channel_shuffle(x, groups):b, c, h, w = x.shapex = x.reshape(b, groups, -1, h, w)x = x.permute(0, 2, 1, 3, 4)# flattenx = x.reshape(b, -1, h, w)return xdef forward(self, x):b, c, h, w = x.size()# group into subfeaturesx = x.view(b * self.G, -1, h, w)  # bs*G,c//G,h,w# channel_splitx_0, x_1 = x.chunk(2, dim=1)  # bs*G,c//(2*G),h,w# channel attentionx_channel = self.avg_pool(x_0)  # bs*G,c//(2*G),1,1x_channel = self.cweight * x_channel + self.cbias  # bs*G,c//(2*G),1,1x_channel = x_0 * self.sigmoid(x_channel)# spatial attentionx_spatial = self.gn(x_1)  # bs*G,c//(2*G),h,wx_spatial = self.sweight * x_spatial + self.sbias  # bs*G,c//(2*G),h,wx_spatial = x_1 * self.sigmoid(x_spatial)  # bs*G,c//(2*G),h,w# concatenate along channel axisout = torch.cat([x_channel, x_spatial], dim=1)  # bs*G,c//G,h,wout = out.contiguous().view(b, -1, h, w)# channel shuffleout = self.channel_shuffle(out, 2)return out

tasks.py文件中,在指定位置添加如下代码。在函数parse_model处。

        elif m is ShuffleAttention:c1, c2 = ch[f], args[0]if c2 != nc:c2 = make_divisible(min(c2, max_channels) * width, 8)args = [c1, *args[1:]]

修改yolov8.yaml文件。改动的地方为标红的地方。

# Ultralytics YOLO  , GPL-3.0 license
# YOLOv8 object detection model with P3-P5 outputs. For Usage examples see https://docs.ultralytics.com/tasks/detect# Parameters
nc: 2  # 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 GFLOPss: [ 0.33, 0.50, 1024 ]  # YOLOv8s summary: 225 layers, 11166560 parameters, 11166544 gradients,  28.8 GFLOPsm: [ 0.67, 0.75, 768 ]   # YOLOv8m summary: 295 layers, 25902640 parameters, 25902624 gradients,  79.3 GFLOPsl: [ 1.00, 1.00, 512 ]   # YOLOv8l summary: 365 layers, 43691520 parameters, 43691504 gradients, 165.7 GFLOPsx: [ 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, 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, 12 ], 1, Concat, [ 1 ] ]  # cat head P4- [ -1, 3, C2f, [ 512 ] ]  # 18 (P4/16-medium)- [ -1, 1, Conv, [ 512, 3, 2 ] ]- [ [ -1, 9 ], 1, Concat, [ 1 ] ]  # cat head P5- [ -1, 3, C2f, [ 1024 ] ]  # 21 (P5/32-large)- [ -1, 3, ShuffleAttention, [ 1024 ] ]- [ [ 15, 18, 22 ], 1, Detect, [ nc ] ]  # Detect(P3, P4, P5)

测试打印网络。

分析

下面对ShuffleAttention模块一部分一部分进行解读。

    def __init__(self, channel=512, reduction=16, G=8):super().__init__()self.G = Gself.channel = channelself.avg_pool = nn.AdaptiveAvgPool2d(1)self.gn = nn.GroupNorm(channel // (2 * G), channel // (2 * G))self.cweight = Parameter(torch.zeros(1, channel // (2 * G), 1, 1))self.cbias = Parameter(torch.ones(1, channel // (2 * G), 1, 1))self.sweight = Parameter(torch.zeros(1, channel // (2 * G), 1, 1))self.sbias = Parameter(torch.ones(1, channel // (2 * G), 1, 1))self.sigmoid = nn.Sigmoid()

  • self.G:存储组的数量。
  • self.channel:存储输入特征的通道数。
  • self.avg_pool:自适应平均池化层,输出大小为1x1,用于全局池化操作。
  • self.gn:分组归一化层。
  • self.cweightself.cbias:通道注意力的可学习权重和偏置。
  • self.sweightself.sbias:空间注意力的可学习权重和偏置。
  • self.sigmoid:Sigmoid激活函数。

注:分组归一化层self.gn,是对输入的x,按照通道分成几组,然后在每组里在分别进行归一化。经过这一层,会改变其中的值,但不改变形状。Parameter()方法,使用Parameter包装一个张量表示这个张量是模型中的一个参数,它会在模型的训练过程中被优化器更新。

    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)

init_weights 方法是用于初始化神经网络模型中不同类型层的权重和偏置参数的函数。遍历模型中的所有模块,对于nn.Conv2d,nn.BatchNorm2d,nn.Linear分别进行不同的初始化操作。

    def channel_shuffle(x, groups):b, c, h, w = x.shapex = x.reshape(b, groups, -1, h, w)x = x.permute(0, 2, 1, 3, 4)# flattenx = x.reshape(b, -1, h, w)return x

在一个卷积神经网络中对输入的4维张量(通常代表图像批次)的通道进行混洗。这种操作通常用于那些使用分组卷积的网络架构(例如ShuffleNet)中来提升模型性能,它通过在通道间进行信息的交换来增强特征的表达能力。

逐行进行解释:

  1. b, c, h, w = x.shape:这行代码获取输入张量 x 的形状,其中 b 是批次大小,c 是通道数,h 是特征图的高度,w 是特征图的宽度。

  2. x = x.reshape(b, groups, -1, h, w):这里,张量 x 被重新塑形(reshape)为一个新的形状。它首先按照批次大小 b,然后是分组数 groups 进行分割。-1 表示自动计算该维度的大小,具体来说,这里的 -1 表示每个分组的通道数(即 c // groups)。最后两维 h 和 w 保持不变。这一步准备将通道分为 groups 组,每组具有相等数量的通道。

  3. x = x.permute(0, 2, 1, 3, 4):permute 函数用于对张量的维度进行重新排列。这里,它将分组的维度(索引为1的维度)和通道的维度(索引为2的维度)调换位置。经过这一步操作后,张量的形状将变为 (b, -1, groups, h, w)

  4. x = x.reshape(b, -1, h, w):在上一步维度调换之后,此行代码再次将张量 x 进行重新塑形,使其变回原始的4维形状 (b, c, h, w),其中分组内的通道现在已经被混洗。由于维度调换的操作,原先属于同一组的通道现在分散到了不同的位置,从而完成了通道混洗的过程。

  5. return x:最后,返回了经过通道混洗后的张量 x

下面对forward中部分代码进行解析

x = x.view(b * self.G, -1, h, w)

这里将输入x的形状重构,将批次内的图像分成G组,每组的通道数变为原通道数除以G

x_0, x_1 = x.chunk(2, dim=1)  # 将输入x沿着通道维度分成两块

这里使用chunk函数沿着通道维度将输入x分成两部分,x_0x_1。每部分包含原本通道数的一半。

# channel attentionx_channel = self.avg_pool(x_0)  # 对x_0进行全局平均池化,输出维度变为(batch_size * G, new_channels, 1, 1)x_channel = self.cweight * x_channel + self.cbias  # 应用学习到的权重和偏置x_channel = x_0 * self.sigmoid(x_channel)  # 通过Sigmoid函数后与x_0相乘实现通道注意力

这是通道注意力机制的实现。首先,对x_0应用全局平均池化(avg_pool)来得到每个通道的全局特征,然后通过一个权重cweight和偏置cbias进行线性变换(这些可能是在类的初始化中定义的参数)。接着,通过Sigmoid函数激活这个通道特征图(x_channel),并将它与原始的x_0相乘,实现对不同通道的不同权重分配,即通道注意力。

# spatial attentionx_spatial = self.gn(x_1)  # 对x_1进行分组归一化x_spatial = self.sweight * x_spatial + self.sbias  # 应用学习到的权重和偏置x_spatial = x_1 * self.sigmoid(x_spatial)  # 通过Sigmoid函数后与x_1相乘实现空间注意力

这是空间注意力机制的实现。首先,对x_1应用分组归一化(gn),然后通过一个权重sweight和偏置sbias进行线性变换。最后,相同地,通过Sigmoid函数激活并与x_1相乘,实现对空间位置不同重要性的加权,即空间注意力。

# concatenate along channel axisout = torch.cat([x_channel, x_spatial], dim=1)  # 将通道和空间注意力的结果在通道维度上拼接out = out.contiguous().view(b, -1, h, w)  # 重构形状为(batch_size, channels, height, width)

这里将通道注意力和空间注意力处理后的两部分沿通道维度拼接回一个完整的张量。然后改变其形状,以确保它与原始输入的批次大小、高度、宽度一致。

    # channel shuffleout = self.channel_shuffle(out, 2)return out

最后,进行通道混洗操作,改善跨通道的信息流通。

这篇关于yolov8添加注意力机制模块-ShuffleAttention的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/751018

相关文章

java中反射(Reflection)机制举例详解

《java中反射(Reflection)机制举例详解》Java中的反射机制是指Java程序在运行期间可以获取到一个对象的全部信息,:本文主要介绍java中反射(Reflection)机制的相关资料... 目录一、什么是反射?二、反射的用途三、获取Class对象四、Class类型的对象使用场景1五、Class

Python使用date模块进行日期处理的终极指南

《Python使用date模块进行日期处理的终极指南》在处理与时间相关的数据时,Python的date模块是开发者最趁手的工具之一,本文将用通俗的语言,结合真实案例,带您掌握date模块的六大核心功能... 目录引言一、date模块的核心功能1.1 日期表示1.2 日期计算1.3 日期比较二、六大常用方法详

python中time模块的常用方法及应用详解

《python中time模块的常用方法及应用详解》在Python开发中,时间处理是绕不开的刚需场景,从性能计时到定时任务,从日志记录到数据同步,时间模块始终是开发者最得力的工具之一,本文将通过真实案例... 目录一、时间基石:time.time()典型场景:程序性能分析进阶技巧:结合上下文管理器实现自动计时

Nginx之upstream被动式重试机制的实现

《Nginx之upstream被动式重试机制的实现》本文主要介绍了Nginx之upstream被动式重试机制的实现,可以通过proxy_next_upstream来自定义配置,具有一定的参考价值,感兴... 目录默认错误选择定义错误指令配置proxy_next_upstreamproxy_next_upst

Node.js net模块的使用示例

《Node.jsnet模块的使用示例》本文主要介绍了Node.jsnet模块的使用示例,net模块支持TCP通信,处理TCP连接和数据传输,具有一定的参考价值,感兴趣的可以了解一下... 目录简介引入 net 模块核心概念TCP (传输控制协议)Socket服务器TCP 服务器创建基本服务器服务器配置选项服

Spring排序机制之接口与注解的使用方法

《Spring排序机制之接口与注解的使用方法》本文介绍了Spring中多种排序机制,包括Ordered接口、PriorityOrdered接口、@Order注解和@Priority注解,提供了详细示例... 目录一、Spring 排序的需求场景二、Spring 中的排序机制1、Ordered 接口2、Pri

Python利用自带模块实现屏幕像素高效操作

《Python利用自带模块实现屏幕像素高效操作》这篇文章主要为大家详细介绍了Python如何利用自带模块实现屏幕像素高效操作,文中的示例代码讲解详,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1、获取屏幕放缩比例2、获取屏幕指定坐标处像素颜色3、一个简单的使用案例4、总结1、获取屏幕放缩比例from

MySQL 缓存机制与架构解析(最新推荐)

《MySQL缓存机制与架构解析(最新推荐)》本文详细介绍了MySQL的缓存机制和整体架构,包括一级缓存(InnoDBBufferPool)和二级缓存(QueryCache),文章还探讨了SQL... 目录一、mysql缓存机制概述二、MySQL整体架构三、SQL查询执行全流程四、MySQL 8.0为何移除查

nginx-rtmp-module模块实现视频点播的示例代码

《nginx-rtmp-module模块实现视频点播的示例代码》本文主要介绍了nginx-rtmp-module模块实现视频点播,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习... 目录预置条件Nginx点播基本配置点播远程文件指定多个播放位置参考预置条件配置点播服务器 192.

一文详解Java Condition的await和signal等待通知机制

《一文详解JavaCondition的await和signal等待通知机制》这篇文章主要为大家详细介绍了JavaCondition的await和signal等待通知机制的相关知识,文中的示例代码讲... 目录1. Condition的核心方法2. 使用场景与优势3. 使用流程与规范基本模板生产者-消费者示例