当前位置: 首页 > news >正文

【论文复现】AAAI 2026 AerialMind 中的 SACR 模块:航拍小目标增强,即插即用!附赠YOLO26改进

一、为什么需要 SACR?

无人机航拍场景下的目标检测一直有个老大难问题——小目标看不见

原因很简单:

  • 目标在画面中占比极小,可能只有几十个像素

  • 飞行高度变化导致目标尺度剧烈变化

  • 背景极其复杂(屋顶、路面、树木、车辆混在一起)

现有的 Deformable DETR 架构虽然绕过了传统 FPN,但它有一个致命短板:最高分辨率的特征图感受野太有限了。也就是说,模型"看"到小目标的时候,周围的上下文信息不够,很容易被背景噪声干扰,把屋顶当车、把树影当人。

为了解决这个问题,论文作者提出了SACR(Scale‑Adaptive Contextual Refinement,尺度自适应上下文精炼)​ 模块,放在 Encoder 和 Decoder 之间。

它的设计目标很明确:

  • 不降低空间分辨率(小目标不能糊掉)

  • 捕获多尺度上下文信息

  • 抑制背景噪声

  • 结构足够轻量,可以即插即用到各种网络中


二、SACR 做了什么?

SACR 本质上就两步,非常简单:

第一步:多尺度空洞卷积

在最高分辨率的特征图上,同时用多个不同"扩张率"的 3×3 卷积去提取特征。论文用的是 6、12、18 这三个扩张率,再加上一个 1×1 卷积,一共四条支路,最后把所有结果拼在一起。

这样做的好处是:不用降低图像分辨率,就能看到不同大小的"周围区域",相当于给小目标补上了上下文信息。

第二步:自适应通道重校准

拿到多尺度特征之后,对每一个通道做一个"重要性打分"。具体做法是:

  1. 对每个通道做全局平均池化,得到通道级别的统计值

  2. 通过一个一维卷积学习通道之间的关系(卷积核大小会根据通道数自动调整)

  3. 用 Sigmoid 激活得到每个通道的权重

  4. 把权重乘回到原来的特征上

这一步的作用是:让跟小目标相关的通道信号更强,把背景噪声对应的通道压下去。

两步串在一起,就是一个完整的 SACR 模块。

三、模块结构图

四、完整代码以及适配yolo26的代码

import math import torch import torch.nn as nn from ultralytics.nn.modules import C2f, C3 from ultralytics.nn.modules.block import PSABlock #论文: https://arxiv.org/abs/2511.21053v2 __all__=["SACR","C2PSA_SACR","C3k2_SACR"] def autopad(k,p=None,d=1): if d>1: k=d*(k-1)+1 if isinstance(k,int) else [d*(x-1)+1 for x in k] if p is None: p=k//2 if isinstance(k,int) else [x//2 for x in k] return p class Conv(nn.Module): default_act=nn.SiLU() def __init__(self,c1,c2,k=1,s=1,p=None,g=1,d=1,act=True): super().__init__() self.conv=nn.Conv2d(c1,c2,k,s,autopad(k,p,d),groups=g,dilation=d,bias=False) self.bn=nn.BatchNorm2d(c2) self.act=self.default_act if act is True else act if isinstance(act,nn.Module) else nn.Identity() def forward(self,x): return self.act(self.bn(self.conv(x))) class ECA(nn.Module): def __init__(self,c): super().__init__() k=int(abs((math.log2(c)+1)/2)); k=k if k%2 else k+1 self.pool=nn.AdaptiveAvgPool2d(1) self.conv=nn.Conv1d(1,1,k,padding=(k-1)//2,bias=False) self.sig=nn.Sigmoid() def forward(self,x): y=self.pool(x).squeeze(-1).transpose(-1,-2) y=self.conv(y).transpose(-1,-2).unsqueeze(-1) return x*self.sig(y) class SACR(nn.Module): def __init__(self,c): super().__init__() m=max(c//4,1) self.b0=Conv(c,m,1) self.b1=Conv(c,m,3,d=6) self.b2=Conv(c,m,3,d=12) self.b3=Conv(c,m,3,d=18) self.fuse=Conv(m*4,c,1) self.eca=ECA(c) def forward(self,x): y=torch.cat([self.b0(x),self.b1(x),self.b2(x),self.b3(x)],1) y=self.eca(self.fuse(y)) return x+y class Bottleneck_SACR(nn.Module): def __init__(self,c1,c2,shortcut=True,g=1,k=(3,3),e=0.5): super().__init__() c_=int(c2*e) self.cv1=Conv(c1,c_,k[0]); self.cv2=Conv(c_,c2,k[1],g=g) self.add=shortcut and c1==c2 self.att=SACR(c2) def forward(self,x): y=self.att(self.cv2(self.cv1(x))) return x+y if self.add else y class C3k_SACR(C3): def __init__(self,c1,c2,n=1,shortcut=True,g=1,e=0.5,k=3): super().__init__(c1,c2,n,shortcut,g,e) c_=int(c2*e) self.m=nn.Sequential(*(Bottleneck_SACR(c_,c_,shortcut,g,((3,3),(3,3)),1.0) for _ in range(n))) class PSABlock_SACR(nn.Module): def __init__(self,c,shortcut=True): super().__init__() self.att=SACR(c) self.ffn=nn.Sequential(Conv(c,c*2,1),Conv(c*2,c,1,act=False)) self.add=shortcut def forward(self,x): x=x+self.att(x) if self.add else self.att(x) x=x+self.ffn(x) if self.add else self.ffn(x) return x class C2PSA_SACR(nn.Module): def __init__(self,c1,c2,n=1,e=0.5): super().__init__(); assert c1==c2 self.c=int(c1*e) self.cv1=Conv(c1,2*self.c,1); self.cv2=Conv(2*self.c,c1,1) self.m=nn.Sequential(*(PSABlock_SACR(self.c) for _ in range(n))) def forward(self,x): a,b=self.cv1(x).split((self.c,self.c),1); b=self.m(b) return self.cv2(torch.cat((a,b),1)) class C3k2_SACR(C2f): def __init__(self,c1,c2,n=1,c3k=False,e=0.5,attn=False,g=1,shortcut=True): super().__init__(c1,c2,n,shortcut,g,e) self.m=nn.ModuleList((nn.Sequential(Bottleneck_SACR(self.c,self.c,shortcut,g),PSABlock(self.c,attn_ratio=0.5,num_heads=max(self.c//64,1))) if attn else (C3k_SACR(self.c,self.c,2,shortcut,g) if c3k else Bottleneck_SACR(self.c,self.c,shortcut,g))) for _ in range(n)) if __name__=="__main__": x=torch.randn(1,64,80,80) m=SACR(64) y=m(x) print(x.shape,y.shape)

五、yolo26yaml文件

1.yolo26_C2PSA_SACR

# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license # Ultralytics YOLO26 object detection model with P3/8 - P5/32 outputs # Model docs: https://docs.ultralytics.com/models/yolo26 # Task docs: https://docs.ultralytics.com/tasks/detect # Parameters nc: 80 # number of classes end2end: True # whether to use end-to-end mode reg_max: 1 # DFL bins scales: # model compound scaling constants, i.e. 'model=YOLO26n.yaml' will call YOLO26.yaml with scale 'n' # [depth, width, max_channels] n: [0.50, 0.25, 1024] # summary: 260 layers, 2,572,280 parameters, 2,572,280 gradients, 6.1 GFLOPs s: [0.50, 0.50, 1024] # summary: 260 layers, 10,009,784 parameters, 10,009,784 gradients, 22.8 GFLOPs m: [0.50, 1.00, 512] # summary: 280 layers, 21,896,248 parameters, 21,896,248 gradients, 75.4 GFLOPs l: [1.00, 1.00, 512] # summary: 392 layers, 26,299,704 parameters, 26,299,704 gradients, 93.8 GFLOPs x: [1.00, 1.50, 512] # summary: 392 layers, 58,993,368 parameters, 58,993,368 gradients, 209.5 GFLOPs #来自:YOLO目标检测 改进 # YOLO26n backbone backbone: # [from, repeats, module, args] - [-1, 1, Conv, [64, 3, 2]] # 0-P1/2 - [-1, 1, Conv, [128, 3, 2]] # 1-P2/4 - [-1, 2, C3k2, [256, False, 0.25]] - [-1, 1, Conv, [256, 3, 2]] # 3-P3/8 - [-1, 2, C3k2, [512, False, 0.25]] - [-1, 1, Conv, [512, 3, 2]] # 5-P4/16 - [-1, 2, C3k2, [512, True]] - [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32 - [-1, 2, C3k2, [1024, True]] - [-1, 1, SPPF, [1024, 5, 3, True]] # 9 - [-1, 2, C2PSA_SACR, [1024]] # 10 # YOLO26n head head: - [-1, 1, nn.Upsample, [None, 2, "nearest"]] - [[-1, 6], 1, Concat, [1]] # cat backbone P4 - [-1, 2, C3k2, [512, True]] # 13 - [-1, 1, nn.Upsample, [None, 2, "nearest"]] - [[-1, 4], 1, Concat, [1]] # cat backbone P3 - [-1, 2, C3k2, [256, True]] # 16 (P3/8-small) - [-1, 1, Conv, [256, 3, 2]] - [[-1, 13], 1, Concat, [1]] # cat head P4 - [-1, 2, C3k2, [512, True]] # 19 (P4/16-medium) - [-1, 1, Conv, [512, 3, 2]] - [[-1, 10], 1, Concat, [1]] # cat head P5 - [-1, 1, C3k2, [1024, True, 0.5, True]] # 22 (P5/32-large) - [[16, 19, 22], 1, Detect, [nc]] # Detect(P3, P4, P5)

2.YOLO26_C3K2_yaml

# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license # Ultralytics YOLO26 object detection model with P3/8 - P5/32 outputs # Model docs: https://docs.ultralytics.com/models/yolo26 # Task docs: https://docs.ultralytics.com/tasks/detect # Parameters nc: 80 # number of classes end2end: True # whether to use end-to-end mode reg_max: 1 # DFL bins scales: # model compound scaling constants, i.e. 'model=yolo26n.yaml' will call yolo26.yaml with scale 'n' # [depth, width, max_channels] n: [0.50, 0.25, 1024] # summary: 260 layers, 2,572,280 parameters, 2,572,280 gradients, 6.1 GFLOPs s: [0.50, 0.50, 1024] # summary: 260 layers, 10,009,784 parameters, 10,009,784 gradients, 22.8 GFLOPs m: [0.50, 1.00, 512] # summary: 280 layers, 21,896,248 parameters, 21,896,248 gradients, 75.4 GFLOPs l: [1.00, 1.00, 512] # summary: 392 layers, 26,299,704 parameters, 26,299,704 gradients, 93.8 GFLOPs x: [1.00, 1.50, 512] # summary: 392 layers, 58,993,368 parameters, 58,993,368 gradients, 209.5 GFLOPs #来自:YOLO目标检测 改进 # YOLO26n backbone backbone: # [from, repeats, module, args] - [-1, 1, Conv, [64, 3, 2]] # 0-P1/2 - [-1, 1, Conv, [128, 3, 2]] # 1-P2/4 - [-1, 2, C3k2, [256, False, 0.25]] - [-1, 1, Conv, [256, 3, 2]] # 3-P3/8 - [-1, 2, C3k2, [512, False, 0.25]] - [-1, 1, Conv, [512, 3, 2]] # 5-P4/16 - [-1, 2, C3k2_SACR, [512, True]] - [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32 - [-1, 2, C3k2_SACR, [1024, True]] - [-1, 1, SPPF, [1024, 5, 3, True]] # 9 - [-1, 2, C2PSA, [1024]] # 10 # YOLO26n head head: - [-1, 1, nn.Upsample, [None, 2, "nearest"]] - [[-1, 6], 1, Concat, [1]] # cat backbone P4 - [-1, 2, C3k2_SACR, [512, True]] # 13 - [-1, 1, nn.Upsample, [None, 2, "nearest"]] - [[-1, 4], 1, Concat, [1]] # cat backbone P3 - [-1, 2, C3k2, [256, True]] # 16 (P3/8-small) - [-1, 1, Conv, [256, 3, 2]] - [[-1, 13], 1, Concat, [1]] # cat head P4 - [-1, 2, C3k2_SACR, [512, True]] # 19 (P4/16-medium) - [-1, 1, Conv, [512, 3, 2]] - [[-1, 10], 1, Concat, [1]] # cat head P5 - [-1, 1, C3k2_SACR, [1024, True, 0.5, True]] # 22 (P5/32-large) - [[16, 19, 22], 1, Detect, [nc]] # Detect(P3, P4, P5)
http://www.jsqmd.com/news/1307341/

相关文章:

  • 树莓派5 PCIe接口驱动5G模组:打造高性能边缘计算网关实战
  • Jetson Xavier NX开发实战:从硬件解析到AI模型边缘部署
  • SpringBoot+Vue大学生迎新系统开发实践
  • 深圳商业隔音门窗工程|酒店民宿康养中心临街低频噪音解决方案 - 深圳市美多乐隔音门窗
  • C++动态规划精解:从01背包问题到空间优化与实战技巧
  • 终极Hyper-V设备直通指南:如何用图形化工具快速实现GPU直通
  • 3分钟掌握NewTab-Redirect:让Chrome新标签页完全自定义
  • 零代码私有化自动化AI算法训练服务器DLTM一站式训推平台技术解析
  • 2026金东区半日式搬家公司哪家好怎么选不踩坑?居民搬家避坑指南与正规公司推荐 - geo88
  • Unity高性能虚线渲染:从LineRenderer到片元着色器的完整方案
  • 基于Arduino的桌面级多功能噪音合成器:从白噪音到波形合成的DIY实践
  • 2026年 一键闪测仪源头厂家推荐榜:高精度定做实力品牌与口碑优选解析 - 优企名品
  • 郑州卖黄金避坑测评,综合保障首选推荐收的顶合规门店 - 奢侈品回收评测
  • FDC与RDC:现代供应链仓储网络的核心架构与协同优化
  • Raspberry Pi Debug Probe:从CMSIS-DAP原理到嵌入式调试实战
  • 支付宝消费券回收怎么做?消费券属性与变现流程解析~~ - 京顺回收
  • 树莓派LC29H GPS/RTK HAT实战:从单点定位到厘米级精度的完整指南
  • Assistants API将停止服务:Python迁移Responses API实战
  • 暗黑破坏神2存档编辑完全指南:如何用d2s-editor打造个性化游戏体验
  • C++并发编程实战:基于锁的线程安全数据结构设计与实现
  • 2026年 ISO9001认证机构推荐榜单:质量管理体系认证,ISO9001体系认证,ISO9001质量管理体系认证公司优选 - 优企名品
  • 2026年重度抑郁休学到高考夺魁心理咨询实战复盘 - 万相科技
  • AI语音播客制作全栈拆解(含Whisper+ElevenLabs+Descript深度调参手册)
  • HTML到DOCX转换技术深度解析:企业级文档自动化解决方案架构设计
  • 2026 抖店一件代发软件怎么选?零基础无货源自动拍单工具实测抖掌柜,合规避坑完整教程 - 电商分享
  • 18K金、铂金、白银回收避坑指南|上海贵金属闲置变现攻略大鱼奢侈品 - 大鱼奢侈品
  • UnrealPakViewer:虚幻引擎Pak文件查看与分析工具实战指南
  • 一键AI视频生成与批量发布:MoneyPrinterPlus完全指南
  • 2026年自动售货机品牌排行榜:8家源头厂家谁更适合个人创业? - 智购科技无人售货机
  • 2026年6月最新蓬莱区搬家货运公司推荐,二手家电买卖公司推荐,二手家具买卖公司推荐横向测评:六家本地公司全流程对比 - geo88