优化改进YOLOv8算法之AKConv(可改变核卷积),即插即用的卷积,效果秒杀DSConv

本文主要是介绍优化改进YOLOv8算法之AKConv(可改变核卷积),即插即用的卷积,效果秒杀DSConv,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1 AKConv原理

1.1 Define the initial sampling position

1.2 Alterable convolutional operation

1.3 Extended AKConv

2 YOLOv8中加入AKConv模块

2.1 AKConv.py文件配置

2.2 task.py配置

2.3 创建添加优化点模块的yolov8-AKConv.yaml

2.4 训练 


1 AKConv原理

AKConv: Convolutional Kernel with Arbitrary Sampled Shapes andArbitrary Number of Parameters

摘要:基于卷积运算的神经网络在深度学习领域取得了令人瞩目的成果,但标准卷积运算存在两个固有的缺陷。一方面,卷积运算仅限于局部窗口,无法捕获其他位置的信息, 并且它的采样形状是固定的。 另一方面,卷积核的大小固定为k×k,是一个固定的正方形,参数的数量往往随大小呈平方增长。 很明显,不同数据集和不同位置的目标的形状和大小是不同的。 具有固定样本形状和正方形的卷积核不能很好地适应不断变化的目标。 针对上述问题,本工作探索了可改变核卷积(AKConv),它赋予卷积核任意数量的参数和任意采样形状,为网络开销和性能之间的权衡提供更丰富的选择。 在 AKConv 中,我们通过新的坐标生成算法定义任意大小的卷积核的初始位置。 为了适应目标的变化,我们引入了偏移量来调整每个位置的样本形状。 此外,我们通过使用具有相同大小和不同初始采样形状的 AKConv 来探索神经网络的效果。 AKConv 通过不规则卷积运算完成高效特征提取的过程,为卷积采样形状带来更多探索选择。 在代表性数据集 COCO2017、VOC 7+12 和 VisDrone-DET2021 上进行的物体检测实验充分展示了 AKConv 的优势。 AKConv可以作为即插即用的卷积运算来替代卷积运算来提高网络性能。

1.1 Define the initial sampling position

定义初始采样位置卷积神经网络基于卷积运算,通过规则采样网格将特征定位在相应位置。在规则采样网格中给出了3×3卷积运算。设R表示采样网格,则R表示如下:

然而,采样网格是规则的,而AKConv的目标是不规则形状的卷积核。因此,为了允许不规则卷积核具有采样网格,我们创建了一种任意大小卷积的算法,该算法生成卷积核Pn的初始采样坐标。首先,我们将采样网格生成为规则采样网格,然后为剩余的采样点创建不规则网格,最后,我们将它们缝合以生成整体采样网格。伪代码如算法1所示。

如图2所示,它表明初始采样坐标是为任意大小的卷积生成的。正则卷积的采样网格以(0,0)点为中心。虽然不规则卷积在许多大小上都没有中心,但为了适应所使用的卷积的大小,我们在算法中设置左上角(0,0)点作为采样原点。 

在定义了不规则卷积的初始坐标Pn之后,位置P0处的相应卷积运算可以定义如下:

 这里,w表示卷积参数。然而,不规则的卷积运算是不可能实现的,因为不规则的采样坐标不能与相应大小的卷积运算相匹配,例如,大小为5、7和13的卷积。聪明的是,我们提出的AKConv实现了这一点。

1.2 Alterable convolutional operation

很明显,标准卷积采样位置是固定的,这导致卷积只能提取当前窗口的局部信息,而不能捕获其他位置的信息。可变形Conv通过卷积运算来学习偏移,以调整初始规则模式的采样网格。该方法在一定程度上弥补了卷积运算的不足。然而,标准卷积和可变形卷积是规则采样网格,不允许具有任意数量参数的卷积核。此外,随着卷积核的大小增加,它们的卷积参数的数量往往会增加一个平方,这对硬件环境来说是不友好的。因此,我们提出了一种新的可变卷积运算(AKConv)。如图3所示,它展示了尺寸为5的AKConv的整体结构。

与可变形Conv类似,在AKConv中,首先通过卷积运算获得相应核的偏移量,卷积运算的维数为(B,2N,H,W),其中N是卷积核的大小。以图3为例,N=5。然后,通过对偏移和原始坐标(P0+Pn)求和来获得修改后的坐标。最后通过插值和重采样获得相应位置的特征。很难提取与不规则卷积核的采样位置相对应的特征。为了解决这个问题,经过深入思考,我们发现有很多方法可以解决。在可变形Conv和RFAConv中,他们在空间维度上堆叠了3×3卷积特征。然后,使用步长为3的卷积运算来提取特征。但是,此方法针对方形采样形状。因此,可以将特征堆叠在行或列上,以使用列卷积或行卷积来提取与不规则采样形状相对应的特征。提取特征以使用适当大小和步长的卷积核。此外,我们可以将特征转换为四个维度(C,N,H,W),然后使用具有步长和卷积大小(N,1,1)的Conv3d来提取特征。当然,我们也可以将通道维度上的特征叠加到(CN,H,W),然后使用1×1卷积将维度降到(C,H,W)。因此,上述所有方法都可以提取与不规则采样形状相对应的特征。只需要重塑特征并使用相应的卷积运算。因此,在图3中,最后的“重塑”和“Conv”表示上述任何方法。此外,为了清楚地显示AKConv的过程,在图3中重新采样后,我们将卷积对应的特征的维度放在第三个维度中。然而,当代码被实现时,它位于最后一个维度。

根据RFAConv和Deformable Conv,我们在列方向上堆叠重新采样的特征,然后使用大小为(N,1)和步长为(N,1)的行卷积。因此,AKConv可以完美地完成不规则卷积特征提取过程。AKConv通过不规则卷积完成特征提取过程,可以根据偏移量灵活调整样本形状,为卷积采样形状带来更多探索选择。与标准卷积和可变形卷积不同,它们受到规则卷积核思想的限制。

1.3 Extended AKConv

我们认为AKConv的设计是一种新颖的设计,它实现了从不规则和任意采样的形状卷积核中提取特征的壮举。即使不使用可变形卷积中的偏移思想,AKConv仍然可以制作各种卷积核形状。因为,AKConv可以使用初始坐标进行重新采样,以显示各种更改。如图4所示,我们为大小为5的卷积设计了各种初始采样形状。在图4中,我们只展示了一些尺寸为5的示例。然而,AKConv的大小可以是任意的,因此,随着大小的增加,AKConv的初始卷积采样形状变得更加丰富甚至无限。考虑到目标形状在数据集之间变化,设计与采样形状相对应的卷积运算至关重要。AKConv通过根据相位特定域设计具有相应形状的卷积运算来完全实现这一点。它也可以类似于可变形Conv,通过添加可学习的偏移来动态适应对象的变化。对于特定的任务,卷积核的初始采样位置的设计是一个重要的问题,因为它是一个先验知识。与Qi等人一样,他们为细长管状结构分割任务提出了具有相应形状的采样坐标,但他们的形状选择仅适用于细长管状结构。

AKConv真正实现了对任意数量的任意形状进行卷积核运算的过程,它可以使卷积核呈现出各种形状。可变形卷积是为了弥补规则卷积的缺点而设计的。而DSConv是为特定的物体形状而设计的。他们没有探索任意大小的卷积和任意样本形状的卷积。AKConv的设计通过允许卷积运算通过Offset有效地提取不规则样本形状的特征来解决这些问题。AKConv允许卷积具有任意数量的卷积参数,并允许卷积呈现各种各样的形状。 

2 YOLOv8中加入AKConv模块

2.1 AKConv.py文件配置

下载YOLOv8官方代码,在ultralytics/nn文件夹新建extra_modules文件夹并新建AKConv.py文件

AKconv.py文件代码参考官方提供代码,如下

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.utils.checkpoint as checkpoint
import math
import numpy as np
from einops import rearrangeclass AKConv(nn.Module):def __init__(self, inc, outc, num_param=5, stride=1, bias=None):super(AKConv, self).__init__()self.num_param = num_paramself.stride = strideself.conv = nn.Sequential(nn.Conv2d(inc, outc, kernel_size=(num_param, 1), stride=(num_param, 1), bias=bias),nn.BatchNorm2d(outc),nn.SiLU())  # the conv adds the BN and SiLU to compare original Conv in YOLOv5.self.p_conv = nn.Conv2d(inc, 2 * num_param, kernel_size=3, padding=1, stride=stride)nn.init.constant_(self.p_conv.weight, 0)# self.p_conv.register_full_backward_hook(self._set_lr)@staticmethoddef _set_lr(module, grad_input, grad_output):grad_input = (grad_input[i] * 0.1 for i in range(len(grad_input)))grad_output = (grad_output[i] * 0.1 for i in range(len(grad_output)))def forward(self, x):# N is num_param.offset = self.p_conv(x)dtype = offset.data.type()N = offset.size(1) // 2# (b, 2N, h, w)p = self._get_p(offset, dtype)# (b, h, w, 2N)p = p.contiguous().permute(0, 2, 3, 1)q_lt = p.detach().floor()q_rb = q_lt + 1q_lt = torch.cat([torch.clamp(q_lt[..., :N], 0, x.size(2) - 1), torch.clamp(q_lt[..., N:], 0, x.size(3) - 1)],dim=-1).long()q_rb = torch.cat([torch.clamp(q_rb[..., :N], 0, x.size(2) - 1), torch.clamp(q_rb[..., N:], 0, x.size(3) - 1)],dim=-1).long()q_lb = torch.cat([q_lt[..., :N], q_rb[..., N:]], dim=-1)q_rt = torch.cat([q_rb[..., :N], q_lt[..., N:]], dim=-1)# clip pp = torch.cat([torch.clamp(p[..., :N], 0, x.size(2) - 1), torch.clamp(p[..., N:], 0, x.size(3) - 1)], dim=-1)# bilinear kernel (b, h, w, N)g_lt = (1 + (q_lt[..., :N].type_as(p) - p[..., :N])) * (1 + (q_lt[..., N:].type_as(p) - p[..., N:]))g_rb = (1 - (q_rb[..., :N].type_as(p) - p[..., :N])) * (1 - (q_rb[..., N:].type_as(p) - p[..., N:]))g_lb = (1 + (q_lb[..., :N].type_as(p) - p[..., :N])) * (1 - (q_lb[..., N:].type_as(p) - p[..., N:]))g_rt = (1 - (q_rt[..., :N].type_as(p) - p[..., :N])) * (1 + (q_rt[..., N:].type_as(p) - p[..., N:]))# resampling the features based on the modified coordinates.x_q_lt = self._get_x_q(x, q_lt, N)x_q_rb = self._get_x_q(x, q_rb, N)x_q_lb = self._get_x_q(x, q_lb, N)x_q_rt = self._get_x_q(x, q_rt, N)# bilinearx_offset = g_lt.unsqueeze(dim=1) * x_q_lt + \g_rb.unsqueeze(dim=1) * x_q_rb + \g_lb.unsqueeze(dim=1) * x_q_lb + \g_rt.unsqueeze(dim=1) * x_q_rtx_offset = self._reshape_x_offset(x_offset, self.num_param)out = self.conv(x_offset)return out# generating the inital sampled shapes for the AKConv with different sizes.def _get_p_n(self, N, dtype):base_int = round(math.sqrt(self.num_param))row_number = self.num_param // base_intmod_number = self.num_param % base_intp_n_x, p_n_y = torch.meshgrid(torch.arange(0, row_number),torch.arange(0, base_int))p_n_x = torch.flatten(p_n_x)p_n_y = torch.flatten(p_n_y)if mod_number > 0:mod_p_n_x, mod_p_n_y = torch.meshgrid(torch.arange(row_number, row_number + 1),torch.arange(0, mod_number))mod_p_n_x = torch.flatten(mod_p_n_x)mod_p_n_y = torch.flatten(mod_p_n_y)p_n_x, p_n_y = torch.cat((p_n_x, mod_p_n_x)), torch.cat((p_n_y, mod_p_n_y))p_n = torch.cat([p_n_x, p_n_y], 0)p_n = p_n.view(1, 2 * N, 1, 1).type(dtype)return p_n# no zero-paddingdef _get_p_0(self, h, w, N, dtype):p_0_x, p_0_y = torch.meshgrid(torch.arange(0, h * self.stride, self.stride),torch.arange(0, w * self.stride, self.stride))p_0_x = torch.flatten(p_0_x).view(1, 1, h, w).repeat(1, N, 1, 1)p_0_y = torch.flatten(p_0_y).view(1, 1, h, w).repeat(1, N, 1, 1)p_0 = torch.cat([p_0_x, p_0_y], 1).type(dtype)return p_0def _get_p(self, offset, dtype):N, h, w = offset.size(1) // 2, offset.size(2), offset.size(3)# (1, 2N, 1, 1)p_n = self._get_p_n(N, dtype)# (1, 2N, h, w)p_0 = self._get_p_0(h, w, N, dtype)p = p_0 + p_n + offsetreturn pdef _get_x_q(self, x, q, N):b, h, w, _ = q.size()padded_w = x.size(3)c = x.size(1)# (b, c, h*w)x = x.contiguous().view(b, c, -1)# (b, h, w, N)index = q[..., :N] * padded_w + q[..., N:]  # offset_x*w + offset_y# (b, c, h*w*N)index = index.contiguous().unsqueeze(dim=1).expand(-1, c, -1, -1, -1).contiguous().view(b, c, -1)x_offset = x.gather(dim=-1, index=index).contiguous().view(b, c, h, w, N)return x_offset#  Stacking resampled features in the row direction.@staticmethoddef _reshape_x_offset(x_offset, num_param):b, c, h, w, n = x_offset.size()# using Conv3d# x_offset = x_offset.permute(0,1,4,2,3), then Conv3d(c,c_out, kernel_size =(num_param,1,1),stride=(num_param,1,1),bias= False)# using 1 × 1 Conv# x_offset = x_offset.permute(0,1,4,2,3), then, x_offset.view(b,c×num_param,h,w)  finally, Conv2d(c×num_param,c_out, kernel_size =1,stride=1,bias= False)# using the column conv as follow, then, Conv2d(inc, outc, kernel_size=(num_param, 1), stride=(num_param, 1), bias=bias)x_offset = rearrange(x_offset, 'b c h w n -> b c (h n) w')return x_offset

2.2 task.py配置

然后找到ultralytics/nn/tasks.py文件下里的parse_model函数,将类名加入进去,如下所示

2.3 创建添加优化点模块的yolov8-AKConv.yaml

# Ultralytics YOLO 🚀, GPL-3.0 license# Parameters
nc: 80  # number of classes
depth_multiple: 0.33  # scales module repeats
width_multiple: 0.25  # scales convolution channels# YOLOv8.0n backbone
backbone:# [from, repeats, module, args]- [-1, 1, Conv, [64, 3, 2]]  # 0-P1/2- [-1, 1, AKConv, [128, 6, 2]]  # 1-P2/4- [-1, 3, C2f, [128, True]]- [-1, 1, AKConv, [256, 6, 2]]  # 3-P3/8- [-1, 6, C2f, [256, True]]- [-1, 1, AKConv, [512, 6, 2]]  # 5-P4/16- [-1, 6, C2f, [512, True]]- [-1, 1, AKConv, [1024, 6, 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, AKConv, [256, 6, 2]]- [[-1, 12], 1, Concat, [1]]  # cat head P4- [-1, 3, C2f, [512]]  # 18 (P4/16-medium)- [-1, 1, AKConv, [512, 6, 2]]- [[-1, 9], 1, Concat, [1]]  # cat head P5- [-1, 3, C2f, [1024]]  # 21 (P5/32-large)- [[15, 18, 21], 1, Detect, [nc]]  # Detect(P3, P4, P5)

2.4 训练 

完成上述配置之后,可以对数据集进行训练

这篇关于优化改进YOLOv8算法之AKConv(可改变核卷积),即插即用的卷积,效果秒杀DSConv的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

HDFS—存储优化(纠删码)

纠删码原理 HDFS 默认情况下,一个文件有3个副本,这样提高了数据的可靠性,但也带来了2倍的冗余开销。 Hadoop3.x 引入了纠删码,采用计算的方式,可以节省约50%左右的存储空间。 此种方式节约了空间,但是会增加 cpu 的计算。 纠删码策略是给具体一个路径设置。所有往此路径下存储的文件,都会执行此策略。 默认只开启对 RS-6-3-1024k

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

MySQL高性能优化规范

前言:      笔者最近上班途中突然想丰富下自己的数据库优化技能。于是在查阅了多篇文章后,总结出了这篇! 数据库命令规范 所有数据库对象名称必须使用小写字母并用下划线分割 所有数据库对象名称禁止使用mysql保留关键字(如果表名中包含关键字查询时,需要将其用单引号括起来) 数据库对象的命名要能做到见名识意,并且最后不要超过32个字符 临时库表必须以tmp_为前缀并以日期为后缀,备份