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.rhkb.cn/news/201091.html

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

相关文章

数字IC基础:有符号数和无符号数加、减法的Verilog设计

相关阅读 数字IC基础https://blog.csdn.net/weixin_45791458/category_12365795.html?spm1001.2014.3001.5482 本文是对数字IC基础:有符号数和无符号数的加减运算一文中的谈到的有符号数加减法的算法进行Verilog实现,有关算法细节请阅读原文&#xff0…

git merge 和 git rebase

一、是什么 在使用 git 进行版本管理的项目中,当完成一个特性的开发并将其合并到 master 分支时,会有两种方式: git merge git rebasegit rebase 与 git merge都有相同的作用,都是将一个分支的提交合并到另一分支上,…

大数据平台实践之CDH6.2.1+spark3.3.0+kyuubi-1.6.0

前言:关于kyuubi的原理和功能这里不做详细的介绍,感兴趣的同学可以直通官网:https://kyuubi.readthedocs.io/en/v1.7.1-rc0/index.html 下载软件版本 wget http://distfiles.macports.org/scala2.12/scala-2.12.16.tgz wget https://archi…

Java 开源重试类 guava-retrying 使用案例

使用背景 需要重复尝试执行某些动作&#xff0c;guava-retrying 提供了成型的重试框架 依赖 <dependency><groupId>com.github.rholder</groupId><artifactId>guava-retrying</artifactId><version>${retrying.version}</version>…

NLog配置文件详解

一、属性详解 1. 属性一览 <target xsi:type"File"name"String"layout"Layout"header"Layout"footer"Layout"encoding"Encoding"lineEnding"Enum"archiveAboveSize"Long"maxArchiveFile…

浅谈低压绝缘监测及定位系统在海上石油平台的研究与应用

安科瑞 华楠 摘要&#xff1a;海上石油平台低压系统与陆地电力系统有很大区别&#xff0c;其属于中性点绝缘系统&#xff0c;在出现单相接地故障时&#xff0c;系统允许带故障正常运行2 h&#xff0c;保证海上重要电气设备不会立即关停。现以渤海某海上平台为例&#xff0c;其…

多篇论文介绍-可变形卷积

01 具有双层路由注意力的 YOLOv8 道路场景目标检测方法 01 摘要: 随着机动车的数量不断增加&#xff0c;道路交通环境变得更复杂&#xff0c;尤其是光照变化以及复杂背景都会干扰目标检测算法的准确性和精度&#xff0c;同时道路场景下多变形态的目标也会给检测任务造成干扰&am…

redis的一些操作

文章目录 清空当前缓存和所有缓存配置内存大小&#xff0c;防止内存饱满设置内存淘汰策略键过期机制 清空当前缓存和所有缓存 Windows环境下使用命令行进行redis缓存清理 redis安装目录下输入cmdredis-cli -p 端口号flushdb 清除当前数据库缓存flushall 清除整个redis所有缓存…

window文件夹下python脚本实现批量删除无法预览的图片

你是否遇到过下载的图片会发现有些图片会无法预览情况&#xff1f; 有几种原因可能导致一些图片在预览时无法正常显示&#xff1a; 损坏的图片文件&#xff1a; 图片文件可能损坏或者部分损坏&#xff0c;导致无法被正常解析和预览。这种情况可能是因为文件在传输过程中损坏、…

模块化Common JS 和 ES Module

目录 历程 1.几个函数&#xff1a;全局变量的污染&#xff0c;模块间没有联系 2.对象&#xff1a;暴露成员&#xff0c;外部可修改 3.立即执行函数&#xff1a;闭包实现模块私有作用域 common JS module和Module 过程 模块依赖&#xff1a;深度优先遍历、父 -> 子 -…

我在Vscode学OpenCV 几何变换(缩放、翻转、仿射变换、透视、重映射)

几何变换指的是将一幅图像映射到另一幅图像内的操作。 cv2.warpAffine&#xff1a;使用仿射变换矩阵对图像进行变换&#xff0c;可以实现平移、缩放和旋转等操作。cv2.warpPerspective&#xff1a;使用透视变换矩阵对图像进行透视变换&#xff0c;可以实现镜头校正、图像纠偏等…

基于 Flink CDC 打造企业级实时数据集成方案

本文整理自Flink数据通道的Flink负责人、Flink CDC开源社区的负责人、Apache Flink社区的PMC成员徐榜江在云栖大会开源大数据专场的分享。本篇内容主要分为四部分&#xff1a; CDC 数据实时集成的挑战Flink CDC 核心技术解读基于 Flink CDC 的企业级实时数据集成方案实时数据集…

视频转码方法:多种格式视频批量转FLV视频的技巧

随着互联网的发展&#xff0c;视频已成为日常生活中不可或缺的一部分。然而&#xff0c;不同的视频格式可能适用于不同的设备和平台&#xff0c;因此需要进行转码。在转码之前&#xff0c;要了解各种视频格式的特点和适用场景。常见的视频格式包括MP4、AVI、MKV、FLV等。其中&a…

left join查询耗时太慢,添加索引解决问题

背景 因为最近自己用的小app越用感觉加载越慢&#xff0c;以为是自己app开发逻辑出现问题了&#xff0c;结果才发现是自己很早以前的代码用到的是left join多表联查&#xff0c;以前因为数据少&#xff0c;所以没有感觉&#xff0c;现在数据量稍微一大&#xff0c;耗时就非常严…

Vatee万腾外汇市场新力量:vatee科技决策力

在当今数字化时代&#xff0c;Vatee万腾崭露头角&#xff0c;以其强大的科技决策力进军外汇市场&#xff0c;成为该领域的新力量。这一新动向将不仅塑造外汇市场的未来&#xff0c;也展现Vatee科技决策力在金融领域的引领作用。 Vatee万腾带着先进的科技决策力进入外汇市场&…

ON1 Photo RAW 2024 for Mac——专业照片编辑的终极利器

ON1 Photo RAW 2024 for Mac是一款专为Mac用户打造的照片编辑器&#xff0c;以其强大的功能和易用的操作&#xff0c;让你的照片编辑工作变得轻松愉快。 一、强大的RAW处理能力 ON1 Photo RAW 2024支持大量的RAW格式照片&#xff0c;能够让你在编辑过程中获得更多的自由度和更…

STM32F4系列单片机GPIO概述和寄存器分析

第2章 STM32-GPIO口 2.1 GPIO口概述 通用输入/输出口 2.1.1 GPIO口作用 GPIO是单片机与外界进行数据交流的窗口。 2.1.2 STM32的GPIO口 在51单片机中&#xff0c;IO口&#xff0c;以数字进行分组&#xff08;P0~P3&#xff09;&#xff0c;每一组里面又有8个IO口。 在ST…

Nacos和Eureka的区别

目录 配置&#xff1a; 区别&#xff1a; ephemeral设置为true时 ephemeral设置为false时&#xff08;这里我使用的服务是order-service&#xff09; 1. Nacos与eureka的共同点 都支持服务注册和服务拉取 都支持服务提供者心跳方式做健康检测 2. Nacos与Eu…

验证码 | 可视化一键管控各场景下的风险数据

目录 查看今日验证数据 查看未来趋势数据 验证码作为人机交互界面经常出现的关键要素&#xff0c;是身份核验、防范风险、数据反爬的重要组成部分&#xff0c;广泛应用网站、App上&#xff0c;在注册、登录、交易、交互等各类场景中发挥着巨大作用&#xff0c;具有真人识别、身…

新手做抖店,这6点建议一定要收好,能让你不亏钱!

我是电商珠珠 我呢&#xff0c;目前身居郑州。 电商这个行业也做了5年多了&#xff0c;抖店从20年开始做&#xff0c;到现在也已经快3年了。 其实&#xff0c;我做抖店期间呢&#xff0c;踩过很多坑&#xff0c;所以今天就把我所踩过的坑&#xff0c;给做抖店的新手总结了6点…