YOLOv8改进 | DAttention (DAT)注意力机制实现极限涨点

论文地址: DAT论文地址

官方地址:官方代码的地址

代码地址:文末有修改了官方代码BUG的代码块复制粘贴即可

一、本文介绍

本文给大家带来的是YOLOv8改进DAT(Vision Transformer with Deformable Attention)的教程,其发布于2022年CVPR2022上同时被评选为Best Paper,由此可以证明其是一种十分有效的改进机制,其主要的核心思想是:引入可变形注意力机制和动态采样点(听着是不是和可变形动态卷积DCN挺相似)。同时在网络结构中引入一个DAT计算量由8.9GFLOPs涨到了9.4GFLOPs。本文的讲解主要包含三方面:DAT的网络结构思想、DAttention的代码复现,如何添加DAttention到你的结构中实现涨点,下面先来分享我测试的对比图(因为资源有限,我只用了100张图片的数据集进行了100个epoch的训练,虽然这个实验不能产生确定性的结论,但是可以作为一个参考)。

适用检测对象->各种检测目标都可以使用,并不针对于某一特定的目标有效。

视频讲解->暂未更新

前文回顾->YOLOv8改进有效涨点专栏->持续复现各种最新机制

目录

一、本文介绍

二、DAT的网络结构思想

2.1 DAT的主要思想和改进

2.2 DAT的网络结构图 

2.3 DAT和其他机制的对比

三、DAT即插即用的代码块

四、添加DAT到你的网络中

五、DAT可添加的位置

5.1推荐DAT可添加的位置 

5.2图示DAT可添加的位置 

六、本文总结 


二、DAT的网络结构思想

2.1 DAT的主要思想和改进

DAT(Vision Transformer with Deformable Attention)是一种引入了可变形注意力机制的视觉Transformer,DAT的核心思想主要包括以下几个方面:

  1. 可变形注意力(Deformable Attention):传统的Transformer使用标准的自注意力机制,这种机制会处理图像中的所有像素,导致计算量很大。而DAT引入了可变形注意力机制,它只关注图像中的一小部分关键区域。这种方法可以显著减少计算量,同时保持良好的性能。

  2. 动态采样点:在可变形注意力机制中,DAT动态地选择采样点,而不是固定地处理整个图像。这种动态选择机制使得模型可以更加集中地关注于那些对当前任务最重要的区域。

  3. 即插即用:DAT的设计允许它适应不同的图像大小和内容,使其在多种视觉任务中都能有效工作,如图像分类、对象检测等。

总结:DAT通过引入可变形注意力机制,改进了视觉Transformer的效率和性能,使其在处理复杂的视觉任务时更加高效和准确。

2.2 DAT的网络结构图 

(a) 展示了可变形注意力的信息流。左侧部分,一组参考点均匀地放置在特征图上,这些点的偏移量是由查询通过偏移网络学习得到的。然后,如右侧所示,根据变形点从采样特征中投影出变形的键和值。相对位置偏差也通过变形点计算,增强了输出转换特征的多头注意力。为了清晰展示,图中仅显示了4个参考点,但在实际实现中实际上有更多的点。

(b) 展示了偏移生成网络的详细结构,每层输入和输出特征图的大小都有标注(这个Offset network在网络的代码中需要控制可添加可不添加)。

通过上面的方式产生多种参考点分布在图像上,从而提高检测的效率,最终的效果图如下->

2.3 DAT和其他机制的对比

DAT与其他视觉Transformer模型和CNN模型中的DCN(可变形卷积网络)的对比图如下,突出了它们处理查询的不同方法(图片展示的很直观,不给大家描述过程了)

三、DAT即插即用的代码块

下面的代码是DAT的网络结构代码,官方的代码中存在许多bug而且参数都未定义,这里我替大家都行了修改而且在使用时无需手动添加任何参数,我都设置了通过模型进行了自动计算,使用方法看章节四。

import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import einops
from timm.models.layers import to_2tuple, trunc_normal_class LayerNormProxy(nn.Module):def __init__(self, dim):super().__init__()self.norm = nn.LayerNorm(dim)def forward(self, x):x = einops.rearrange(x, 'b c h w -> b h w c')x = self.norm(x)return einops.rearrange(x, 'b h w c -> b c h w')class DAttentionBaseline(nn.Module):def __init__(self, q_size=(224,224), kv_size=(224,224), n_heads=8, n_head_channels=32, n_groups=1,attn_drop=0.0, proj_drop=0.0, stride=1,offset_range_factor=-1, use_pe=True, dwc_pe=True,no_off=False, fixed_pe=False, ksize=9, log_cpb=False):super().__init__()n_head_channels = int(q_size / 8)q_size = (q_size, q_size)self.dwc_pe = dwc_peself.n_head_channels = n_head_channelsself.scale = self.n_head_channels ** -0.5self.n_heads = n_headsself.q_h, self.q_w = q_size# self.kv_h, self.kv_w = kv_sizeself.kv_h, self.kv_w = self.q_h // stride, self.q_w // strideself.nc = n_head_channels * n_headsself.n_groups = n_groupsself.n_group_channels = self.nc // self.n_groupsself.n_group_heads = self.n_heads // self.n_groupsself.use_pe = use_peself.fixed_pe = fixed_peself.no_off = no_offself.offset_range_factor = offset_range_factorself.ksize = ksizeself.log_cpb = log_cpbself.stride = stridekk = self.ksizepad_size = kk // 2 if kk != stride else 0self.conv_offset = nn.Sequential(nn.Conv2d(self.n_group_channels, self.n_group_channels, kk, stride, pad_size, groups=self.n_group_channels),LayerNormProxy(self.n_group_channels),nn.GELU(),nn.Conv2d(self.n_group_channels, 2, 1, 1, 0, bias=False))if self.no_off:for m in self.conv_offset.parameters():m.requires_grad_(False)self.proj_q = nn.Conv2d(self.nc, self.nc,kernel_size=1, stride=1, padding=0)self.proj_k = nn.Conv2d(self.nc, self.nc,kernel_size=1, stride=1, padding=0)self.proj_v = nn.Conv2d(self.nc, self.nc,kernel_size=1, stride=1, padding=0)self.proj_out = nn.Conv2d(self.nc, self.nc,kernel_size=1, stride=1, padding=0)self.proj_drop = nn.Dropout(proj_drop, inplace=True)self.attn_drop = nn.Dropout(attn_drop, inplace=True)if self.use_pe and not self.no_off:if self.dwc_pe:self.rpe_table = nn.Conv2d(self.nc, self.nc, kernel_size=3, stride=1, padding=1, groups=self.nc)elif self.fixed_pe:self.rpe_table = nn.Parameter(torch.zeros(self.n_heads, self.q_h * self.q_w, self.kv_h * self.kv_w))trunc_normal_(self.rpe_table, std=0.01)elif self.log_cpb:# Borrowed from Swin-V2self.rpe_table = nn.Sequential(nn.Linear(2, 32, bias=True),nn.ReLU(inplace=True),nn.Linear(32, self.n_group_heads, bias=False))else:self.rpe_table = nn.Parameter(torch.zeros(self.n_heads, self.q_h * 2 - 1, self.q_w * 2 - 1))trunc_normal_(self.rpe_table, std=0.01)else:self.rpe_table = None@torch.no_grad()def _get_ref_points(self, H_key, W_key, B, dtype, device):ref_y, ref_x = torch.meshgrid(torch.linspace(0.5, H_key - 0.5, H_key, dtype=dtype, device=device),torch.linspace(0.5, W_key - 0.5, W_key, dtype=dtype, device=device),indexing='ij')ref = torch.stack((ref_y, ref_x), -1)ref[..., 1].div_(W_key - 1.0).mul_(2.0).sub_(1.0)ref[..., 0].div_(H_key - 1.0).mul_(2.0).sub_(1.0)ref = ref[None, ...].expand(B * self.n_groups, -1, -1, -1)  # B * g H W 2return ref@torch.no_grad()def _get_q_grid(self, H, W, B, dtype, device):ref_y, ref_x = torch.meshgrid(torch.arange(0, H, dtype=dtype, device=device),torch.arange(0, W, dtype=dtype, device=device),indexing='ij')ref = torch.stack((ref_y, ref_x), -1)ref[..., 1].div_(W - 1.0).mul_(2.0).sub_(1.0)ref[..., 0].div_(H - 1.0).mul_(2.0).sub_(1.0)ref = ref[None, ...].expand(B * self.n_groups, -1, -1, -1)  # B * g H W 2return refdef forward(self, x):x = xB, C, H, W = x.size()dtype, device = x.dtype, x.deviceq = self.proj_q(x)q_off = einops.rearrange(q, 'b (g c) h w -> (b g) c h w', g=self.n_groups, c=self.n_group_channels)offset = self.conv_offset(q_off).contiguous()  # B * g 2 Hg WgHk, Wk = offset.size(2), offset.size(3)n_sample = Hk * Wkif self.offset_range_factor >= 0 and not self.no_off:offset_range = torch.tensor([1.0 / (Hk - 1.0), 1.0 / (Wk - 1.0)], device=device).reshape(1, 2, 1, 1)offset = offset.tanh().mul(offset_range).mul(self.offset_range_factor)offset = einops.rearrange(offset, 'b p h w -> b h w p')reference = self._get_ref_points(Hk, Wk, B, dtype, device)if self.no_off:offset = offset.fill_(0.0)if self.offset_range_factor >= 0:pos = offset + referenceelse:pos = (offset + reference).clamp(-1., +1.)if self.no_off:x_sampled = F.avg_pool2d(x, kernel_size=self.stride, stride=self.stride)assert x_sampled.size(2) == Hk and x_sampled.size(3) == Wk, f"Size is {x_sampled.size()}"else:x_sampled = F.grid_sample(input=x.reshape(B * self.n_groups, self.n_group_channels, H, W),grid=pos[..., (1, 0)],  # y, x -> x, ymode='bilinear', align_corners=True)  # B * g, Cg, Hg, Wgx_sampled = x_sampled.reshape(B, C, 1, n_sample)# self.proj_k.weight = torch.nn.Parameter(self.proj_k.weight.float())# self.proj_k.bias = torch.nn.Parameter(self.proj_k.bias.float())# self.proj_v.weight = torch.nn.Parameter(self.proj_v.weight.float())# self.proj_v.bias = torch.nn.Parameter(self.proj_v.bias.float())# 检查权重的数据类型q = q.reshape(B * self.n_heads, self.n_head_channels, H * W)k = self.proj_k(x_sampled).reshape(B * self.n_heads, self.n_head_channels, n_sample)v = self.proj_v(x_sampled).reshape(B * self.n_heads, self.n_head_channels, n_sample)attn = torch.einsum('b c m, b c n -> b m n', q, k)  # B * h, HW, Nsattn = attn.mul(self.scale)if self.use_pe and (not self.no_off):if self.dwc_pe:residual_lepe = self.rpe_table(q.reshape(B, C, H, W)).reshape(B * self.n_heads, self.n_head_channels,H * W)elif self.fixed_pe:rpe_table = self.rpe_tableattn_bias = rpe_table[None, ...].expand(B, -1, -1, -1)attn = attn + attn_bias.reshape(B * self.n_heads, H * W, n_sample)elif self.log_cpb:q_grid = self._get_q_grid(H, W, B, dtype, device)displacement = (q_grid.reshape(B * self.n_groups, H * W, 2).unsqueeze(2) - pos.reshape(B * self.n_groups,n_sample,2).unsqueeze(1)).mul(4.0)  # d_y, d_x [-8, +8]displacement = torch.sign(displacement) * torch.log2(torch.abs(displacement) + 1.0) / np.log2(8.0)attn_bias = self.rpe_table(displacement)  # B * g, H * W, n_sample, h_gattn = attn + einops.rearrange(attn_bias, 'b m n h -> (b h) m n', h=self.n_group_heads)else:rpe_table = self.rpe_tablerpe_bias = rpe_table[None, ...].expand(B, -1, -1, -1)q_grid = self._get_q_grid(H, W, B, dtype, device)displacement = (q_grid.reshape(B * self.n_groups, H * W, 2).unsqueeze(2) - pos.reshape(B * self.n_groups,n_sample,2).unsqueeze(1)).mul(0.5)attn_bias = F.grid_sample(input=einops.rearrange(rpe_bias, 'b (g c) h w -> (b g) c h w', c=self.n_group_heads,g=self.n_groups),grid=displacement[..., (1, 0)],mode='bilinear', align_corners=True)  # B * g, h_g, HW, Nsattn_bias = attn_bias.reshape(B * self.n_heads, H * W, n_sample)attn = attn + attn_biasattn = F.softmax(attn, dim=2)attn = self.attn_drop(attn)out = torch.einsum('b m n, b c n -> b c m', attn, v)if self.use_pe and self.dwc_pe:out = out + residual_lepeout = out.reshape(B, C, H, W)y = self.proj_drop(self.proj_out(out))h, w = pos.reshape(B, self.n_groups, Hk, Wk, 2), reference.reshape(B, self.n_groups, Hk, Wk, 2)return y

四、添加DAT到你的网络中

添加教程这里不再重复介绍、因为专栏内容有许多,添加过程又需要截特别图片会导致文章大家读者也不通顺如果你已经会添加注意力机制了,可以跳过本章节,如果你还不会,大家可以看我下面的文章,里面详细的介绍了拿到一个任意机制(C2f、Conv、Bottleneck、Loss、DetectHead)如何添加到你的网络结构中去。

添加教程->YOLOv8改进 | 如何在网络结构中添加注意力机制、C2f、卷积、Neck、检测头

五、DAT可添加的位置

5.1推荐DAT可添加的位置 

DAT可以是一种即插即用的注意力机制,其可以添加的位置有很多,添加的位置不同效果也不同,所以我下面推荐几个添加的位,置大家可以进行参考,当然不一定要按照我推荐的地方添加。

  1. 残差连接中:在残差网络的残差连接中加入注意力机制(这个位置我推荐的原因是因为DCN放在残差里面效果挺好的大家可以尝试)

  2. 特征金字塔(SPPF):在特征金字塔网络之前,可以帮助模型更好地融合不同尺度的特征。

  3. Neck部分:YOLOv8的Neck部分负责特征融合,这里添加注意力机制可以帮助模型更有效地融合不同层次的特征。

  4. 输出层前:在最终的输出层前加入注意力机制可以使模型在做出最终预测之前,更加集中注意力于最关键的特征。

大家可能看我描述不太懂,大家可以看下面的网络结构图中我进行了标注。

5.2图示DAT可添加的位置 

六、本文总结 

到此本文的正式分享内容就结束了,在这里给大家推荐我的YOLOv8改进有效涨点专栏,本专栏目前为新开的平均质量分98分,后期我会根据各种最新的前沿顶会进行论文复现,也会对一些老的改进机制进行补充,目前本专栏免费阅读(暂时,大家尽早关注不迷路~),如果大家觉得本文帮助到你了,订阅本专栏,关注后续更多的更新~

本专栏其它内容(持续更新) 

YOLOv8改进 | 如何在网络结构中添加注意力机制、C2f、卷积、Neck、检测头

YOLOv8改进 | ODConv附修改后的C2f、Bottleneck模块代码

YOLOv8改进有效涨点系列->手把手教你添加动态蛇形卷积(Dynamic Snake Convolution)

YOLOv8性能评估指标->mAP、Precision、Recall、FPS、IoU

YOLOv8改进有效涨点系列->适合多种检测场景的BiFormer注意力机制(Bi-level Routing Attention)

 YOLOv8改进有效涨点系列->多位置替换可变形卷积(DCNv1、DCNv2、DCNv3) 

详解YOLOv8网络结构/环境搭建/数据集获取/训练/推理/验证/导出/部署

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

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

相关文章

uniapp 手动调用form表单submit事件

背景&#xff1a; UI把提交的按钮弄成了图片&#xff0c;之前的button不能用了。 <button form-type"submit">搜索</button> 实现&#xff1a; html&#xff1a; 通过 this.$refs.fd 获取到form的vue对象。手动调用里面的_onSubmit()方法。 methods:…

MySQL内部组件与日志详解

MySQL的内部组件结构 MySQL 可以分为 Server 层和存储引擎层两部分。 Server 层主要包括连接器、查询缓存、分析器、优化器、执行器等&#xff0c;涵盖 MySQL 的大多数核心服务功能&#xff0c;以及所有的内置函数&#xff08;如日期、时间、数学和加密函数等&#xff09;&am…

Spring面试题:(八)Spring事务

Spring事务概述 Spring事务基于数据库&#xff0c;基于数据库的事务封装了统一的接口。 编程式事务和声明式事务。 声明式事务分为Xml声明式或者注解声明式 实现事务相关的三个类 事务管理器 事务定义 事务状态 XML声明式事务的使用方法 导入坐标配置目标类配置切面 导入…

【LeetCode刷题-双指针】--259.较小的三数之和

259.较小的三数之和 方法&#xff1a;排序双指针 class Solution {public int threeSumSmaller(int[] nums, int target) {Arrays.sort(nums);int k 0;for(int i 0;i<nums.length;i){int start i 1,end nums.length - 1;while(start < end){int sum nums[start] …

FPGA UDP RGMII 千兆以太网(4)ARP ICMP UDP

1 以太网帧 1.1 1以太网帧格式 下图为以太网的帧格式: 前导码(Preamble):8 字节,连续 7 个 8’h55 加 1 个 8’hd5,表示一个帧的开始,用于双方 设备数据的同步。 目的 MAC 地址:6 字节,存放目的设备的物理地址,即 MAC 地址 源 MAC 地址:6 字节,存放发送端设备的…

受电诱骗快充取电芯片XSP08:PD+QC+华为+三星多种协议9V12V15V20V

目前市面上很多家的快充充电器&#xff0c;都有自己的私有快充协议&#xff0c;如PD协议、QC协议、华为快充协议、三星快充协议、OPPO快充协议等待&#xff0c;为了让它们都能输出快充电压&#xff0c;就需要在受电端也增加快充协议取电芯片XSP08&#xff0c;它可以和充电器通讯…

Java学习 10.Java-类和对象

一、面向对象的初步认知 1.1 什么是面向对象 面向对象是解决问题的一种思想&#xff0c;主要依靠对象之间的交互完成一件事情&#xff0c;用面向对象的思想来设计程序&#xff0c;更符合人们对事物的认知&#xff0c;对于大型程序的设计、拓展以及维护都非常友好 1.2 面向对…

C语言--数组的长度计算【详细解释】

一.数组的长度计算公式 我们都知道字符串有特定的函数strlen,而数组没有&#xff0c;&#xff08;虽然字符串也是一种特殊的数组&#xff09; 但是&#xff0c;类似于这样的数组&#xff1a; int arr[]{12,89,1,5,31,78,45,12,12,0,45,142,21,12}&#xff1b; 我们很难一眼…

决策树,sql考题,30个经典sql题目

大数据&#xff1a; 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;oracle&#xff0c;尤其sql要学&#x…

Maven:通过相对路径向jar中添加依赖项

问&#xff1a;我有一个专有的jar&#xff0c;我想把它作为一个依赖项添加到我的pom中。 但我不想把它添加到存储库中。原因是我希望常用的maven命令(如mvn compile等)能够开箱即用。(无需要求开发人员自己将其添加到某个存储库中)。 我希望jar在源代码控制中的第三方库中&…

五分钟,Docker安装kafka 3.5,kafka-map图形化管理工具

首先确保已经安装docker&#xff0c;如果是windows安装docker&#xff0c;可参考 wsl2安装docker 1、安装zk docker run -d --restartalways -e ALLOW_ANONYMOUS_LOGINyes --log-driver json-file --log-opt max-size100m --log-opt max-file2 --name zookeeper -p 2181:218…

【Proteus仿真】【51单片机】公交车报站系统

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真51单片机控制器&#xff0c;使用LCD12864显示模块、DS18B20温度传感器、DS1302时钟模块、按键、LED蜂鸣器、ULN2003、28BYJ48步进电机模块等。 主要功能&#xff1a; 系统运行后&…

比特币上的人工智能

以感知机为例 人工智能&#xff0c;尤其是机器学习形式的人工智能&#xff0c;最近取得了巨大的进步&#xff0c;应用范围从人脸识别到自动驾驶汽车。我们建议将 AI 与比特币区块链结合起来&#xff0c;以获得许多其他方式无法实现的显着优势&#xff1a; 公开透明&#xff1a…

高校教师资格证备考

高等教育制度 关于人的全面发展和个体发展的关系&#xff0c;说法正确的是&#xff08;ABC&#xff09;。 A.个体发展是在全面发展基础上的选择性发展 B.全面发展是个体发展的前提和基础 C.个体发展又是全面发展的动力 D.个体发展是全面发展的前提和基础

从0开始学习数据结构 C语言实现 1.前篇及二分查找算法

一、前篇 1、什么是数据结构&#xff1f; 数据结构是带有结构特性的数据元素的集合&#xff0c;它研究的是数据的逻辑结构和数据的物理结构以及它们之间的相互关系 2、时间复杂度与空间复杂度 大O符号是用于描述函数渐进行为的数学符号 常用函数的增长表 阶乘O(n!) > 指数…

Lec14 File systems 笔记

文件系统中核心的数据结构就是inode和file descriptor 分层的文件系统&#xff1a; 在最底层是磁盘&#xff0c;也就是一些实际保存数据的存储设备&#xff0c;正是这些设备提供了持久化存储。在这之上是buffer cache或者说block cache&#xff0c;这些cache可以避免频繁的读…

MSYS2介绍及工具安装

0 Preface/Foreword 1 MSYS2 官网&#xff1a;MSYS2

2023年AI生成音频研究报告

第一章 行业概况 1.1 定义 AI音频生成行业&#xff0c;作为人工智能生成内容&#xff08;AIGC&#xff09;技术渗透的关键领域&#xff0c;正迅速成为技术革新的前沿阵地。这一领域专注于运用先进的人工智能技术和复杂算法来创造音频内容&#xff0c;覆盖了语音合成、音乐制作…

虚拟机配置网络ip,主打一个详细

文章目录 一、前言二、安装vim编辑器三、检查联网状态1. 使用ping命令 四、查看ip五、ens33网卡六、开机启动ens33网卡七、获取子网地址和子网掩码八、配置网关与子网掩码1. 编辑虚拟网络信息2. 配置网关3. 配置ens33网卡信息 九、动态ip配置十、静态ip配置 一、前言 本文主要…

达芬奇DaVinci Resolve Studio 18.6.3 for Mac

DaVinci Resolve Studio 18是一款专业的视频编辑和调色软件&#xff0c;适用于电影、电视节目、广告等各种视觉媒体的制作。它具有完整的后期制作功能&#xff0c;包括剪辑、调色、特效、音频处理等。 以下是DaVinci Resolve Studio 18的主要特点&#xff1a; - 提供了全面的视…