目标检测算法改进系列之添加变核卷积AKConv模块

2023-12-05

AKConv变核卷积

KConv的主要思想:AKConv(可变核卷积)主要提供一种灵活的卷积机制,允许卷积核具有任意数量的参数和采样形状。这种方法突破了传统卷积局限于固定局部窗口和固定采样形状的限制,从而使得卷积操作能够更加精准地适应不同数据集和不同位置的目标。
AKConv的改进点:
(1)灵活的卷积核设计:AKConv允许卷积核具有任意数量的参数,这使得其可以根据实际需求调整大小和形状,从而更有效地适应目标的变化。
(2)初始采样坐标算法:针对不同大小的卷积核,AKConv提出了一种新的算法来生成初始采样坐标,这进一步增强了其在处理各种尺寸目标时的灵活性。
(3)适应性采样位置调整:为适应目标的不同变化,AKConv通过获得的偏移量调整不规则卷积核的采样位置,从而提高了特征提取的准确性。
(4)减少模型参数和计算开销:AKConv支持线性增减卷积参数的数量,有助于在硬件环境中优化性能,尤其适合于轻量级模型的应用。

原文地址: AKConv: Convolutional Kernel with Arbitrary Sampled Shapes and Arbitrary Number of Parameters

AKConv结构图

改进实现过程

AKConv定义

import torch.nn as nn
import torch
from einops import rearrange
import math
 
 
class AKConv(nn.Module):
    def __init__(self, inc, outc, num_param, 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), indexing='xy')
        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),indexing='xy')
 
            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),indexing='xy')
 
        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)
 
        # 根据实际情况调整
        index = index.clamp(min=0, max=x.shape[-1] - 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

添加过程

添加教程这里不再重复介绍、因为专栏内容有许多,添加过程又需要截特别图片会导致文章大家读者也不通顺如果你已经会添加注意力机制了,可以跳过本章节,如果你还不会,大家可以看我下面的文章,里面详细的介绍了其他卷积模块的添加方法,可以参考一下,大致都是添加到C2f、Bottleneck、Ncek、DetectHead部分。
目标检测算法改进系列之添加SCConv空间和通道重构卷积

配置文件

# Ultralytics YOLO ????, AGPL-3.0 license
# YOLOv8 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=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 GFLOPs
  s: [0.33, 0.50, 1024]  # YOLOv8s summary: 225 layers, 11166560 parameters, 11166544 gradients,  28.8 GFLOPs
  m: [0.67, 0.75, 768]   # YOLOv8m summary: 295 layers, 25902640 parameters, 25902624 gradients,  79.3 GFLOPs
  l: [1.00, 1.00, 512]   # YOLOv8l summary: 365 layers, 43691520 parameters, 43691504 gradients, 165.7 GFLOPs
  x: [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, AKConv, [128, 3, 2]]  # 1-P2/4
  - [-1, 3, C2f, [128, True]]
  - [-1, 1, AKConv, [256, 3, 2]]  # 3-P3/8
  - [-1, 6, C2f, [256, True]]
  - [-1, 1, AKConv, [512, 3, 2]]  # 5-P4/16
  - [-1, 6, C2f, [512, True]]
  - [-1, 1, AKConv, [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, AKConv, [256, 3, 2]]
  - [[-1, 12], 1, Concat, [1]]  # cat head P4
  - [-1, 3, C2f, [512]]  # 18 (P4/16-medium)
 
  - [-1, 1, AKConv, [512, 3, 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)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

目标检测算法改进系列之添加变核卷积AKConv模块 的相关文章

随机推荐

  • Jquery如何获取和设置元素内容?

    在jQuery中 可以使用以下方法来获取和设置元素的内容 获取元素内容 text 获取元素的文本内容 包括其所有子元素的文本 var content div text html 获取元素的HTML内容 包括其所有子元素的HTML标记 var
  • U-BOOT移植的第一天

    编译NXP的UBOOT成功后 我们需要修改LCD 网络 DDR 接下来我们要在u boot添加自己的开发板 1 添加开发板默认配置文件 先在 configs 目录下创建默认配置文件 复制 mx6ull 14x14 evk emmc defc
  • Linux下设置redis临时密码和长期密码

    临时密码 第一步 先启动redis 命令 src redis server redis conf 第二步 进入redis 命令 src redis cli 第三步 查看密码 命令 config get requirepass 如果你redi
  • 基于Python手把手教你实现flappy bird游戏

    目录 前言 开始前的准备工作 进入正题 结束语 前言 想必玩过游戏的都知道 Flappy Bird是一款简单却富有挑战性的经典的小鸟飞行游戏 让许多玩家为之痴迷 而作为开发者 那肯定要通过技术手段来再做一遍这款经典游戏 那么本文就来通过万能
  • 英伟达高薪抢夺中国自动驾驶人才!吴新宙牵头,25大岗位!

    作者 有据无车 编辑 智能车参考 点击下方 卡片 关注 自动驾驶之心 公众号 ADAS巨卷干货 即可获取 点击进入 自动驾驶之心 求职交流 技术交流群 本文只做学术分享 如有侵权 联系删文 英伟达 开始在中国加大自动驾驶布局 官方刚刚发布了
  • Linux下activemq的安装与安装成功确认

    一 下载 apache activemq 5 14 0 bin tar gz 二 安装 将压缩包拷入linux内 进行解压 tar zxvf apache activemq 5 14 0 bin tar gz 与redis nginx不同的
  • CnosDB 科技春晚暨CnosDB 2.4.0 Milky Way发布会|我们程序员也有自己的节目啦

    CnosDB即将举办科技春晚 也是CnosDB 2 4 0版本发布会啦 举办地点就由各位爱码士选在电影院 在此也感谢大家的支持和参与 01 场地剧透 本次发布会正式选择电影院为春晚主办地的现在 就让我们先来一场Venue Tour吧 以上是
  • MX6ULL学习笔记 (七) 中断实验

    前言 本章我们就来学习一 下如何在 Linux 下使用中断 在linux内核里面使用中断 不同于我们以往在别的裸机开发一样 需要进行各种寄存器的配置 中断使能之类的 而在Linux 内核中 提供了完善的中断框架 我们只需要申请中断 然后注
  • 【UE5】使用场系统炸毁一堵墙

    效果 步骤 1 新建一个空白项目 2 新建一个Basic关卡 然后添加一个第三人称游戏和初学者内容包到内容浏览器 3 在场景中添加一堵墙 4 选项模式选择 破裂 点击新建 新建一个文件夹用于存储几何体集 点击 统一 最小和最大Voronoi
  • activemq启动成功但web管理页面却无法访问

    前提 在linux启动activemq成功 本地能ping通linux 处理方案 确定防火墙是否关闭 有两种处理方案 第一种 关闭防火墙 第二种 暴漏8161和61616两个端口 netstat lnpt 查看8161和61616端口 注意
  • 时间序列数据压缩算法简述

    本文简单介绍了时间序列压缩任务的来源 压缩算法的分类 并对常见压缩算法的优缺点进行了简介 爱码士们快来一探究竟呀 引言 时间序列数据是在许多应用程序和领域中生成的一种基本数据类型 例如金融 医疗保健 交通和智慧城市 1 时间序列分析对于各种
  • Docker容器状态显示

    个人笔记 努力奋斗 文章目录 docker ps docker stats 总结 docker ps Docker中 你可以使用以下命令来查看容器的状态 docker ps 这个命令用于列出正在运行的容器 默认情况下 它只显示正在运行的容器
  • 企业ERP软件定制开发对企业的优势|app小程序搭建

    企业ERP软件定制开发对企业的优势 app小程序搭建 ERP Enterprise Resource Planning 软件定制开发是根据企业的具体需求和业务流程特点 定制开发的一种软件解决方案 相比于通用的ERP软件 定制开发可以更好地满
  • 常用的jQuery事件有几种?

    jQuery提供了多种事件处理方法 常用的jQuery事件包括以下几种 click事件 当元素被点击时触发 button click function 点击事件处理逻辑 hover事件 当鼠标悬停在元素上时触发 div hover func
  • 算法与数据结构(二十五)TopK问题:基于快排的Python模板

    首先 先写partition模板 def partition nums left right pivot nums left 初始化一个待比较数据 i j left right while i lt j while i
  • easyrecovery2024绿色版中文语言电脑数据恢复工具

    平时很多人都会把自己工作时 或者生活中的数据存储在我们的电脑上 很多时候 由于我们的误操作或者是其它某些问题 很容易就会误删除一些文件数据了 尤其是一些电脑出现故障 总是会导致数据丢失 这让人非常烦恼 需要重装系统的时候 往往一些文件就无法
  • 2、Linux_远程操作

    远程操作 1 配置ifconfig 1 1输入 ifconfig 查看 ip 的命令 ifconfig 1 2搜索 ifconfig 命令 yum search ifconfig 1 3配置网卡 进入如下目录配置网卡 cd etc sysc
  • 2024不收费的数据恢复软件EasyRecovery16

    EasyRecovery2024是一款操作安全 用户可自主操作的数据恢复方案 它支持从各种各样的存储介质恢复删除或者丢失的文件 其支持的媒体介质包括 硬盘驱动器 光驱 闪存 硬盘 光盘 U盘 移动硬盘 数码相机 手机以及其它多媒体移动设备
  • matplotlib多子图

    matplotlib画图中一个轴占据多个子图 知乎 import matplotlib pyplot as plt fig plt figure gs fig add gridspec 2 4 ax1 fig add subplot gs
  • 目标检测算法改进系列之添加变核卷积AKConv模块

    AKConv变核卷积 KConv的主要思想 AKConv 可变核卷积 主要提供一种灵活的卷积机制 允许卷积核具有任意数量的参数和采样形状 这种方法突破了传统卷积局限于固定局部窗口和固定采样形状的限制 从而使得卷积操作能够更加精准地适应不同数