尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

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

【论文复现】AAAI 2026 AerialMind 中的 SACR 模块:航拍小目标增强,即插即用!附赠YOLO26改进
📅 发布时间:2026/8/1 14:18:03

一、为什么需要 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)

相关新闻

  • 终极Hyper-V设备直通指南:如何用图形化工具快速实现GPU直通
  • C++动态规划精解:从01背包问题到空间优化与实战技巧
  • Raspberry Pi Debug Probe:从CMSIS-DAP原理到嵌入式调试实战

最新新闻

  • 2026无锡人力资源外包怎么选 7项标准帮你避坑选靠谱机构 - 产品评测官
  • 2026遂宁家电维修师傅上门电话空调冰箱洗衣机热水器燃气灶同城急修推荐 - 全国家电维修上门服务
  • 2026 北京商圈探店易奢福,各类大牌名包回收出价公开透明 - 奢侈品回收实体店
  • 5分钟掌握Power BI主题模板:打造专业级数据可视化报表的终极秘籍
  • 资阳房屋漏水怎么办?全城靠谱房屋修缮团队汇总,解决季节性渗漏难题 - 吉林同城获客
  • 2026.8月 章贡区专业防水公司推荐:卫生间防水 楼顶漏水 外墙防水 - 超人防水

日新闻

  • ClickHouse版本管理深度实战:4步构建零风险升级与回滚体系
  • Java 23 种设计模式:从踩坑到精通 | 番外:责任链模式 —— 物流审批流程实战
  • 华硕笔记本性能解放指南:G-Helper轻量级控制工具全面解析

周新闻

  • 大连理工大学与东京大学联手打造的“主动型AI助手“
  • 170.2026年国家级科研瓶颈:超精密单点金刚石切削(SPDT)光学表面生成
  • SongBloom:革命性歌曲生成框架深度解析——如何通过交织自回归与扩散模型创作完整音乐

月新闻

  • ClickHouse版本管理深度实战:4步构建零风险升级与回滚体系
  • Java 23 种设计模式:从踩坑到精通 | 番外:责任链模式 —— 物流审批流程实战
  • 华硕笔记本性能解放指南:G-Helper轻量级控制工具全面解析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号