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

news2025/1/18 21:03:56

目录

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 rearrange



class AKConv(nn.Module):
    def __init__(self, inc, outc, num_param=5, stride=1, bias=None):
        super(AKConv, self).__init__()
        self.num_param = num_param
        self.stride = stride
        self.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)

    @staticmethod
    def _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 + 1

        q_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 p
        p = 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)

        # bilinear
        x_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_rt

        x_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_int
        mod_number = self.num_param % base_int
        p_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-padding
    def _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_0

    def _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 + offset
        return p

    def _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.
    @staticmethod
    def _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 训练 

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1370419.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

eclipse正则表达式替换 Find/Replace

Find/Replace 对话框中使用正则表达式 CTRLF 打开 Find/Replace 对话框勾选 Regular expressions ​ 匹配注释 下图中的Find:/.*/ ​ 匹配换行符 换行符:\R 下图中的Find表达式:\R.*Excel.* ​ 新增空行 /** 替换为 \R\t/** ​ 选…

c++学习:智能指针的底层作用原理+用法

目录 智能指针作用原理 作用 原理 模仿int*类型的智能指针 模仿所有类型的智能指针(模板) 共享智能指针类 思考;如果多个智能指针同时指向同一个堆空间,怎么只执行一次析构函数进行释放空间 (共享智能指针类&…

井盖异动传感器,守护脚下安全

随着城市化进程的加速,城市基础设施的安全问题日益受到关注。其中,井盖作为城市地下管道的重要入口,其安全问题不容忽视。然而,传统的井盖监控方式往往存在盲区,无法及时发现井盖的异常移动。为此,我们推出…

Cesium笔记 初始化 使用Vue-Cesium 组件

参考 A Vue 3 based component library of CesiumJS for developers | Vue for CesiumVue for Cesium, a Vue 3.x based component library of CesiumJS for GISerhttps://zouyaoji.top/vue-cesium/#/zh-CN/component/quickstart

HTML---JQurey的基本使用

文章目录 前言一、pandas是什么?二、使用步骤 1.引入库2.读入数据总结 本章目标 (1)能够搭建jQuery开发环境 (2)使用ready( )方法加载页面、掌握jQuery语法 使用addClass( )方法和css( )方法为元素添加CSS样式使用n…

vue elementUI Tree 树形控件的使用方法

用清晰的层级结构展示信息&#xff0c;可展开或折叠。 效果演示 trees.vue代码 <template><div><!-- 树形控件 --><el-tree :data"treesList" :props"treesProps" show-checkbox node-key"id"default-expand-all :defau…

【C++】十大排序算法

文章目录 十大排序算法插入排序O(n^2^)冒泡排序O(n^2^)选择排序O(n^2^)希尔排序——缩小增量排序O(nlogn)快速排序O(nlogn)堆排序O(nlogn)归并排序(nlogn)计数排序O(nk)基数排序O(n*k)桶排序O(nk) 十大排序算法 排序算法的稳定性&#xff1a;在具有多个相同关键字的记录中&…

如何实现两台Linux虚拟机ssh免密登录

实验开始前 1.准备好两台虚拟机&#xff08;下载好镜像文件的&#xff09; 2.实验步骤 公钥验证&#xff1a;&#xff08;免密登陆验证方式&#xff09; &#xff08;1&#xff09;生成非对称秘钥 [rootclient ~]# ssh-keygen -t rsa Generating public/private rsa key pai…

系统性介绍MoE模型架构,以及在如今大模型方向的发展现状

知乎&#xff1a;Verlocksss编辑&#xff1a;马景锐链接&#xff1a;https://zhuanlan.zhihu.com/p/675216281 1 学习动机 第一次了解到MoE&#xff08;Mixture of experts&#xff09;&#xff0c;是在GPT-4模型架构泄漏事件&#xff0c;听说GPT-4的架构是8个GPT-3级别大小的模…

IBM DS5020硬盘状态Impending failure(reported by controller)

这个状态说明硬盘还没有完全坏掉&#xff0c;但是也需要注意更换新硬盘了 磁盘状态详细信息&#xff1a; 报错信息 按照恢复步骤&#xff1a; 选中该硬盘&#xff0c;手动failed&#xff0c;之后可以将该硬盘拔掉&#xff0c;重新插入新硬盘 此时&#xff0c;会有一块热备盘启…

【npm link】Node命令中的npm link命令的使用,还有CLI全局命令的使用,开发命令行工具必不可少的部分

&#x1f601; 作者简介&#xff1a;一名大四的学生&#xff0c;致力学习前端开发技术 ⭐️个人主页&#xff1a;夜宵饽饽的主页 ❔ 系列专栏&#xff1a;NodeJs &#x1f450;学习格言&#xff1a;成功不是终点&#xff0c;失败也并非末日&#xff0c;最重要的是继续前进的勇气…

Linux网络的命令和配置

目录 一、网络配置命令 1、配置和管理网络接口 1.1 ifconfig 1.2 ip 1.2.1 ip link 1.2.2 ip addr 1.3 修改网络接口名 1.3.1 临时修改网络接口名 1.3.2 永久修改网络接口名 1.4 永久配置单网卡 1.5 永久配置双网卡 1.6 ethtool 2、查看和设置主机中路由表信息…

使用kubesphere的devops部署SpringCloud项目

devops部署SpringCloud项目 环境说明部署流程创建DevOps工程填写流水线信息创建流水线jenkinsfileDockerfiledeploy.yaml 环境说明 已经安装kubesphere的devops组件安装教程可参考官方文档:https://v3-1.docs.kubesphere.io/zh/docs/pluggable-components/devops/ 部署流程 创…

Ubuntu系统下安装TDengine Database

记录一下使用Ubuntu系统的安装TDengine Database管理软件工具 先查看一下系统的版本&#xff0c;可以看到这里使用的是Ubuntu20.04版本&#xff0c;版本代号focal mywmyw-S451LN:~$ uname -a Linux myw-S451LN 6.2.0-39-generic #40~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu …

一键搭建elk

一键启动elk 1. 生成环境的脚本 setup.sh #!/usr/bin/bash# logstash enviroment mkdir -p logstash touch logstash/logstash.conf # shellcheck disableSC1078 echo input {tcp {mode > "server"host > "0.0.0.0"port > 4560codec > jso…

Kubernetes WebHook 入门 -- 入门案例: apiserver 接入 github

博客原文 文章目录 k8s 集群配置介绍Admission WebhookWebHook 入门实践: github 认证接入web 服务器Dockerfile 镜像制作amd64x86_64构造镜像检验镜像 Makefilewebhook 接入 apiserverwebhook.yamlapiserver 挂载 webconfig在 github 中创建认证 token将 token 添加到 kubecon…

以某一web系统为测试对象(若依RuoYi为例)-软件测试实验报告作业

题目:以某一 web系统为测试对象&#xff0c;完成以下文档的编写(满分 100分) (1)产品规格说明书 (SPEC) 要求:功能完整(完成产品需求 70%以上)、UI 优良 (每个页面均有字段约 束和合理的出错提示)、流程完整 (一一对应功能)流程合理 (处理逻辑非直线) (2)测试计划要求 :测试进…

一、数据结构基本概念

数据结构基本概念 一、数据结构基本概念1.基本概念和术语1.1数据&#xff08;Data&#xff09;1.2 数据元素&#xff08;Data element&#xff09;1.3 数据项 &#xff08;Data Item&#xff09;1.4 数据对象 &#xff08;Data Object&#xff09;1.5 数据结构 &#xff08;Dat…

移除两个双向链表中的重复元素,每个链表中的元素不重复

移除两个双向链表中的重复元素&#xff0c;每个链表中的元素不重复&#xff0c;请给出算法。 ans: 该问题比单向链表要更加复杂一些&#xff0c;必须考虑并更新前向节点的指向情况&#xff0c;具体编码中存在一些难度&#xff0c;加上链表调试相对不容易&#xff0c;因此难度系…

Qt之有趣的数字钟

一.效果 基于网络代码修改,支持时、分、秒;支持滑动、翻页和旋转。 二.实现 #include <QtCore> #include <QPainter> #include <QAction> #include <QWidget> #include <QMainWindow> #include <QTimer> #include <QKeyEvent> #…