YOLO11改进|卷积篇|引入全维动态卷积ODConv

news/2024/12/21 11:47:58/

在这里插入图片描述

目录

    • 一、【ODConv】全维动态卷积
      • 1.1【ODConv】卷积介绍
      • 1.2【ODConv】核心代码
    • 二、添加【ODConv】卷积
      • 2.1STEP1
      • 2.2STEP2
      • 2.3STEP3
      • 2.4STEP4
    • 三、yaml文件与运行
      • 3.1yaml文件
      • 3.2运行成功截图

一、【ODConv】全维动态卷积

1.1【ODConv】卷积介绍

在这里插入图片描述

ODConv利用一种全新的多维注意力机制和并行策略,在任何卷积层沿内核空间的四个维度学习卷积内核的注意力。 作为常规卷积的替代品,ODConv 可以插入到许多 CNN 架构中。也就是说是的即插即用的模块。通过下图可以看到,新引入的三个注意力,分别沿空域维度、输入通道维度以及输出通道维度
在这里插入图片描述
以下是ODConv的工作流程图以及简单流程介绍

  1. 输入处理阶段:
    GAP (Global Average Pooling):对输入特征 x 进行全局平均池化,生成一个全局的特征向量。此操作减少了空间维度,保留了通道信息的全局统计特征。
    FC (全连接层):全连接层通过对池化后的全局特征向量进行线性变换,将其投射到一个新的特征空间,增强特征的表达能力。
    ReLU 激活函数:对全连接层的输出应用 ReLU 激活函数,增加模型的非线性表示能力。
  2. 多分支注意力模块:
    W1 到 Wn 权重生成:每个分支都生成一组通道权重,通过学习不同的权重矩阵 𝑊𝑖,每个权重矩阵对应不同的通道或空间特征子集。
    α 权重系数:通过多个 Sigmoid 激活的全连接层,对每个分支生成权重系数 α,这些系数用来控制每个分支的特征重要性。
    例如:α_s1, α_s2,… 是针对第一个分支生成的权重,α_c1, α_c2,… 是针对第二个分支的权重,依次类推。
    特征加权:每个分支的权重系数 α 通过逐元素乘法操作,应用到对应的特征上。这样可以在每个分支中对输入特征进行不同的加权处理,突出不同的重要特征。
  3. 加权特征融合阶段:
    Softmax 权重分配:在最后的加权阶段,使用 Softmax 函数对最终的分支权重进行归一化,以确保所有分支的贡献相对平衡。Softmax 可以确保加权的权重和为1,避免过分依赖某一个特定分支。
    加法操作:所有加权后的特征通过逐元素相加操作融合在一起,生成最终的输出 y。
  4. 优势:
    多尺度特征捕捉:每个分支的 权重系数 α 能够分别对不同特征进行加权处理,允许模型捕捉不同尺度、不同细节层次的特征。这使得模型能够在更精细的层次上理解输入数据。
    灵活的特征表达:通过多个分支的设计,该模块可以根据不同的任务要求灵活地调整不同通道或特征子集的权重,从而提升对复杂数据的适应能力。
    特征融合的有效性:Softmax 的归一化处理保证了所有分支的特征都能有效地融合在一起,而不是过分依赖某个特定的特征子集,确保模型在多任务或多模态环境下具备更强的鲁棒性。
    高效的特征筛选:通过 Sigmoid 和 Softmax 的结合,模型能够有效筛选和权衡不同分支的贡献,突出最具代表性的特征,提高对关键特征的捕捉能力。

在这里插入图片描述

1.2【ODConv】核心代码

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.autograddef autopad(k, p=None, d=1):  # kernel, padding, dilation# Pad to 'same' shape outputsif d > 1:k = d * (k - 1) + 1 if isinstance(k, int) else [d * (x - 1) + 1 for x in k]  # actual kernel-sizeif p is None:p = k // 2 if isinstance(k, int) else [x // 2 for x in k]  # auto-padreturn pclass Conv(nn.Module):# Standard convolution with args(ch_in, ch_out, kernel, stride, padding, groups, dilation, activation)default_act = nn.SiLU()  # default activationdef __init__(self, c1, c2, k=1, s=1, p=None, g=1, d=1, act=True):super().__init__()self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p, d), groups=g, dilation=d, bias=False)self.bn = nn.BatchNorm2d(c2)self.act = self.default_act if act is True else act if isinstance(act, nn.Module) else nn.Identity()def forward(self, x):return self.act(self.bn(self.conv(x)))def forward_fuse(self, x):return self.act(self.conv(x))class Attention(nn.Module):def __init__(self, in_planes, out_planes, kernel_size, groups=1, reduction=0.0625, kernel_num=4, min_channel=16):super(Attention, self).__init__()attention_channel = max(int(in_planes * reduction), min_channel)self.kernel_size = kernel_sizeself.kernel_num = kernel_numself.temperature = 1.0self.avgpool = nn.AdaptiveAvgPool2d(1)self.fc = nn.Conv2d(in_planes, attention_channel, 1, bias=False)self.bn = nn.BatchNorm2d(attention_channel)self.relu = nn.ReLU(inplace=True)self.channel_fc = nn.Conv2d(attention_channel, in_planes, 1, bias=True)self.func_channel = self.get_channel_attentionif in_planes == groups and in_planes == out_planes:  # depth-wise convolutionself.func_filter = self.skipelse:self.filter_fc = nn.Conv2d(attention_channel, out_planes, 1, bias=True)self.func_filter = self.get_filter_attentionif kernel_size == 1:  # point-wise convolutionself.func_spatial = self.skipelse:self.spatial_fc = nn.Conv2d(attention_channel, kernel_size * kernel_size, 1, bias=True)self.func_spatial = self.get_spatial_attentionif kernel_num == 1:self.func_kernel = self.skipelse:self.kernel_fc = nn.Conv2d(attention_channel, kernel_num, 1, bias=True)self.func_kernel = self.get_kernel_attentionself._initialize_weights()def _initialize_weights(self):for m in self.modules():if isinstance(m, nn.Conv2d):nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')if m.bias is not None:nn.init.constant_(m.bias, 0)if isinstance(m, nn.BatchNorm2d):nn.init.constant_(m.weight, 1)nn.init.constant_(m.bias, 0)def update_temperature(self, temperature):self.temperature = temperature@staticmethoddef skip(_):return 1.0def get_channel_attention(self, x):channel_attention = torch.sigmoid(self.channel_fc(x).view(x.size(0), -1, 1, 1) / self.temperature)return channel_attentiondef get_filter_attention(self, x):filter_attention = torch.sigmoid(self.filter_fc(x).view(x.size(0), -1, 1, 1) / self.temperature)return filter_attentiondef get_spatial_attention(self, x):spatial_attention = self.spatial_fc(x).view(x.size(0), 1, 1, 1, self.kernel_size, self.kernel_size)spatial_attention = torch.sigmoid(spatial_attention / self.temperature)return spatial_attentiondef get_kernel_attention(self, x):kernel_attention = self.kernel_fc(x).view(x.size(0), -1, 1, 1, 1, 1)kernel_attention = F.softmax(kernel_attention / self.temperature, dim=1)return kernel_attentiondef forward(self, x):x = self.avgpool(x)x = self.fc(x)# x = self.bn(x) # 在外面我提供了一个bn这里会报错x = self.relu(x)return self.func_channel(x), self.func_filter(x), self.func_spatial(x), self.func_kernel(x)class ODConv2d(nn.Module):def __init__(self, in_planes, out_planes, kernel_size=3, stride=1, padding=1, dilation=1, groups=1,reduction=0.0625, kernel_num=4):super(ODConv2d, self).__init__()in_planes = in_planesself.in_planes = in_planesself.out_planes = out_planesself.kernel_size = kernel_sizeself.stride = strideself.padding = paddingself.dilation = dilationself.groups = groupsself.kernel_num = kernel_numself.attention = Attention(in_planes, out_planes, kernel_size, groups=groups,reduction=reduction, kernel_num=kernel_num)self.weight = nn.Parameter(torch.randn(kernel_num, out_planes, in_planes//groups, kernel_size, kernel_size),requires_grad=True)self._initialize_weights()if self.kernel_size == 1 and self.kernel_num == 1:self._forward_impl = self._forward_impl_pw1xelse:self._forward_impl = self._forward_impl_commondef _initialize_weights(self):for i in range(self.kernel_num):nn.init.kaiming_normal_(self.weight[i], mode='fan_out', nonlinearity='relu')def update_temperature(self, temperature):self.attention.update_temperature(temperature)def _forward_impl_common(self, x):# Multiplying channel attention (or filter attention) to weights and feature maps are equivalent,# while we observe that when using the latter method the models will run faster with less gpu memory cost.channel_attention, filter_attention, spatial_attention, kernel_attention = self.attention(x)batch_size, in_planes, height, width = x.size()x = x * channel_attentionx = x.reshape(1, -1, height, width)aggregate_weight = spatial_attention * kernel_attention * self.weight.unsqueeze(dim=0)aggregate_weight = torch.sum(aggregate_weight, dim=1).view([-1, self.in_planes // self.groups, self.kernel_size, self.kernel_size])output = F.conv2d(x, weight=aggregate_weight, bias=None, stride=self.stride, padding=self.padding,dilation=self.dilation, groups=self.groups * batch_size)output = output.view(batch_size, self.out_planes, output.size(-2), output.size(-1))output = output * filter_attentionreturn outputdef _forward_impl_pw1x(self, x):channel_attention, filter_attention, spatial_attention, kernel_attention = self.attention(x)x = x * channel_attentionoutput = F.conv2d(x, weight=self.weight.squeeze(dim=0), bias=None, stride=self.stride, padding=self.padding,dilation=self.dilation, groups=self.groups)output = output * filter_attentionreturn outputdef forward(self, x):return self._forward_impl(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, andexpansion."""super().__init__()c_ = int(c2 * e)  # hidden channelsself.cv1 = Conv(c1, c_, k[0], 1)self.cv2 = ODConv2d(c_, c2, k[1], 1)self.add = shortcut and c1 == c2def 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 C3_ODConv(nn.Module):# CSP Bottleneck with 3 convolutionsdef __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):  # ch_in, ch_out, number, shortcut, groups, expansionsuper().__init__()c_ = int(c2 * e)  # hidden channelsself.cv1 = Conv(c1, c_, 1, 1)self.cv2 = Conv(c1, c_, 1, 1)self.cv3 = Conv(2 * c_, c2, 1)  # optional act=FReLU(c2)self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)))def forward(self, x):return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), 1))

二、添加【ODConv】卷积

2.1STEP1

首先找到ultralytics/nn文件路径下新建一个Add-module的python文件包【这里注意一定是python文件包,新建后会自动生成_init_.py】,如果已经跟着我的教程建立过一次了可以省略此步骤,随后新建一个ODConv.py文件并将上文中提到的注意力机制的代码全部粘贴到此文件中,如下图所示在这里插入图片描述

2.2STEP2

在STEP1中新建的_init_.py文件中导入增加改进模块的代码包如下图所示
在这里插入图片描述

2.3STEP3

找到ultralytics/nn文件夹中的task.py文件,在其中按照下图添加在这里插入图片描述

2.4STEP4

定位到ultralytics/nn文件夹中的task.py文件中的def parse_model(d, ch, verbose=True): # model_dict, input_channels(3)函数添加如图代码,【如果不好定位可以直接ctrl+f搜索定位】

在这里插入图片描述

三、yaml文件与运行

3.1yaml文件

以下是添加【ODConv】卷积在Backbone中的yaml文件,大家可以注释自行调节,效果以自己的数据集结果为准

# Ultralytics YOLO 🚀, AGPL-3.0 license
# YOLO11 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=yolo11n.yaml' will call yolo11.yaml with scale 'n'# [depth, width, max_channels]n: [0.50, 0.25, 1024] # summary: 319 layers, 2624080 parameters, 2624064 gradients, 6.6 GFLOPss: [0.50, 0.50, 1024] # summary: 319 layers, 9458752 parameters, 9458736 gradients, 21.7 GFLOPsm: [0.50, 1.00, 512] # summary: 409 layers, 20114688 parameters, 20114672 gradients, 68.5 GFLOPsl: [1.00, 1.00, 512] # summary: 631 layers, 25372160 parameters, 25372144 gradients, 87.6 GFLOPsx: [1.00, 1.50, 512] # summary: 631 layers, 56966176 parameters, 56966160 gradients, 196.0 GFLOPs# YOLO11n backbone
backbone:# [from, repeats, module, args]- [-1, 1, Conv, [64, 3, 2]] # 0-P1/2- [-1, 1, ODConv2d, [128, 3, 2]] # 1-P2/4- [-1, 2, C3k2, [256, False, 0.25]]- [-1, 1, ODConv2d, [256, 3, 2]] # 3-P3/8- [-1, 2, C3k2, [512, False, 0.25]]- [-1, 1, ODConv2d, [512, 3, 2]] # 5-P4/16- [-1, 2, C3k2, [512, True]]- [-1, 1, ODConv2d, [1024, 3, 2]] # 7-P5/32- [-1, 2, C3k2, [1024, True]]- [-1, 1, SPPF, [1024, 5]] # 9- [-1, 2, C2PSA, [1024]] # 10# YOLO11n head
head:- [-1, 1, nn.Upsample, [None, 2, "nearest"]]- [[-1, 6], 1, Concat, [1]] # cat backbone P4- [-1, 2, C3k2, [512, False]] # 13- [-1, 1, nn.Upsample, [None, 2, "nearest"]]- [[-1, 4], 1, Concat, [1]] # cat backbone P3- [-1, 2, C3k2, [256, False]] # 16 (P3/8-small)- [-1, 1, Conv, [256, 3, 2]]- [[-1, 13], 1, Concat, [1]] # cat head P4- [-1, 2, C3k2, [512, False]] # 19 (P4/16-medium)- [-1, 1, Conv, [512, 3, 2]]- [[-1, 10], 1, Concat, [1]] # cat head P5- [-1, 2, C3k2, [1024, True]] # 22 (P5/32-large)- [[16, 19, 22], 1, Detect, [nc]] # Detect(P3, P4, P5)

以上添加位置仅供参考,具体添加位置以及模块效果以自己的数据集结果为准

3.2运行成功截图

在这里插入图片描述
这里可以看到Gflops降低了,如果想将模型的轻量化作为一个优势的话可以考虑这个卷积,而且亲测在我的数据集上涨点又轻量

OK 以上就是添加【ODConv】卷积的全部过程了,后续将持续更新尽情期待

在这里插入图片描述


http://www.ppmy.cn/news/1534922.html

相关文章

CICD Jenkins实现Pipline

一、安装 1、由于 Jenkins 是基于 Java 的,首先需要确保你的系统中安装了 Java。推荐使用 OpenJDK 11。可以通过以下命令安装: apt update apt install openjdk-11-jdk2、在安装 Jenkins 之前,你需要将其仓库添加到你的系统中。首先&#x…

蛋白质结构中原子坐标转换

在蛋白质结构分析中,原子坐标经过旋转矩阵和平移向量的转换是常见操作。一般情况下,假设一个原子在结构 A 中的坐标为 (x, y, z),在经过旋转矩阵 u 和平移向量 t 的变换后,得到新的坐标 (X, Y, Z)。然后,再将新的坐标反向映射回原始坐标系。 基本数学公式 1. 变换公式:…

scrapy爬取汽车、车评数据【中】

这个爬虫我想分三期来写: ✅ 第一期写如何爬取汽车的车型信息; ✅ 第二期写如何爬取汽车的车评; ✅ 第三期写如何对车评嵌入情感分析结果,以及用简单的方法把数据插入mysql中; 技术基于scrapy框架、BERT语言模型、mysq…

AI不可尽信

看到某项目有类似这样的一段代码 leaves : make([]int, 10) leaves leaves[:0]没理解这样的连续两行,有何作用? 初始化一个长度和容量都为10的切片,接着把切片长度设置为0 即如下demo: (在线地址) package mainimport "fmt"func main() {leaves : make([]int, 1…

增强现实中的物体识别与跟踪

增强现实(AR)中的物体识别与跟踪是实现虚拟内容与现实世界无缝融合的关键技术。以下是该领域的主要技术和方法概述: 1. 物体识别 1.1 特征提取 SIFT、SURF、ORB:传统的特征提取算法用于识别图像中的关键点并生成描述符&#xf…

STM32-HAL库驱动DHT11温湿度传感器 --2024.9.28

目录 一、教程简介 二、驱动原理讲解 (一)通信4步骤 (二)传感器数据解析 三、CubeMX生成底层代码 (一)基础配置 (二)配置DHT11的驱动引脚 (三)配置串口 四…

选择网络安全模式启动Windows系统,解决PC无法连接网络问题

目录 1、电脑无法连接网络 2、发现C:\Windows\System32\drivers路径下的很多文件不见了 3、使用360安全卫士中的断网急救箱工具修复,也就解决不了问题 4、重启系统,以网络安全模式启动系统,修复系统网络模块,完美解决问题 5、…

Java中的数据格式转换:JSON、XML与Protobuf的应用与选择

Java中的数据格式转换:JSON、XML与Protobuf的应用与选择 大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!今天我们要聊的主题是Java开发中经常涉及到的一个重要问题——数据格式转换。…