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

相关文章

Python通用唯一标识符模块uuid使用案例详解

《Python通用唯一标识符模块uuid使用案例详解》Pythonuuid模块用于生成128位全局唯一标识符,支持UUID1-5版本,适用于分布式系统、数据库主键等场景,需注意隐私、碰撞概率及存储优... 目录简介核心功能1. UUID版本2. UUID属性3. 命名空间使用场景1. 生成唯一标识符2. 数

Android ClassLoader加载机制详解

《AndroidClassLoader加载机制详解》Android的ClassLoader负责加载.dex文件,基于双亲委派模型,支持热修复和插件化,需注意类冲突、内存泄漏和兼容性问题,本文给大家介... 目录一、ClassLoader概述1.1 类加载的基本概念1.2 android与Java Class

Spring事务传播机制最佳实践

《Spring事务传播机制最佳实践》Spring的事务传播机制为我们提供了优雅的解决方案,本文将带您深入理解这一机制,掌握不同场景下的最佳实践,感兴趣的朋友一起看看吧... 目录1. 什么是事务传播行为2. Spring支持的七种事务传播行为2.1 REQUIRED(默认)2.2 SUPPORTS2

MySQL中的锁机制详解之全局锁,表级锁,行级锁

《MySQL中的锁机制详解之全局锁,表级锁,行级锁》MySQL锁机制通过全局、表级、行级锁控制并发,保障数据一致性与隔离性,全局锁适用于全库备份,表级锁适合读多写少场景,行级锁(InnoDB)实现高并... 目录一、锁机制基础:从并发问题到锁分类1.1 并发访问的三大问题1.2 锁的核心作用1.3 锁粒度分

Python中re模块结合正则表达式的实际应用案例

《Python中re模块结合正则表达式的实际应用案例》Python中的re模块是用于处理正则表达式的强大工具,正则表达式是一种用来匹配字符串的模式,它可以在文本中搜索和匹配特定的字符串模式,这篇文章主... 目录前言re模块常用函数一、查看文本中是否包含 A 或 B 字符串二、替换多个关键词为统一格式三、提

Redis的持久化之RDB和AOF机制详解

《Redis的持久化之RDB和AOF机制详解》:本文主要介绍Redis的持久化之RDB和AOF机制,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录概述RDB(Redis Database)核心原理触发方式手动触发自动触发AOF(Append-Only File)核

一文深入详解Python的secrets模块

《一文深入详解Python的secrets模块》在构建涉及用户身份认证、权限管理、加密通信等系统时,开发者最不能忽视的一个问题就是“安全性”,Python在3.6版本中引入了专门面向安全用途的secr... 目录引言一、背景与动机:为什么需要 secrets 模块?二、secrets 模块的核心功能1. 基

PostgreSQL中MVCC 机制的实现

《PostgreSQL中MVCC机制的实现》本文主要介绍了PostgreSQL中MVCC机制的实现,通过多版本数据存储、快照隔离和事务ID管理实现高并发读写,具有一定的参考价值,感兴趣的可以了解一下... 目录一 MVCC 基本原理python1.1 MVCC 核心概念1.2 与传统锁机制对比二 Postg

Maven 配置中的 <mirror>绕过 HTTP 阻断机制的方法

《Maven配置中的<mirror>绕过HTTP阻断机制的方法》:本文主要介绍Maven配置中的<mirror>绕过HTTP阻断机制的方法,本文给大家分享问题原因及解决方案,感兴趣的朋友一... 目录一、问题场景:升级 Maven 后构建失败二、解决方案:通过 <mirror> 配置覆盖默认行为1. 配置示

Redis过期删除机制与内存淘汰策略的解析指南

《Redis过期删除机制与内存淘汰策略的解析指南》在使用Redis构建缓存系统时,很多开发者只设置了EXPIRE但却忽略了背后Redis的过期删除机制与内存淘汰策略,下面小编就来和大家详细介绍一下... 目录1、简述2、Redis http://www.chinasem.cn的过期删除策略(Key Expir