前言
本文介绍了维度感知选择性融合(DASI)模块在YOLO26中的结合应用。DASI模块是HCF - Net用于红外小目标检测的关键组件,可实现自适应的通道选择和融合。它通过对高维、低维和当前层特征进行对齐、分区,依据sigmoid激活值自适应选择细粒度或上下文特征进行融合。我们将DASI集成到YOLO26的检测头中,并进行相关注册和配置。实验表明,改进后的模型在SIRST红外单帧图像数据集上表现优异,超越了其他传统和深度学习模型。
文章目录: YOLO26改进大全:卷积层、轻量化、注意力机制、损失函数、Backbone、SPPF、Neck、检测头全方位优化汇总
专栏链接: YOLO26改进专栏
@[TOC]
介绍

摘要
红外小目标检测作为计算机视觉领域的一项关键任务,旨在识别并定位红外图像中的微小目标,这些目标往往仅有几个像素大小。然而,由于目标体积微小且红外图像背景通常较为复杂,该任务面临着诸多挑战。本文提出了一种深度学习方法HCF - Net,借助多个实用模块显著提高了红外小目标检测的性能。具体来讲,该方法包含并行化的感知补丁注意力(PPA)模块、维度感知选择性融合(DASI)模块和多膨胀通道优化(MDCR)模块。PPA模块采用多分支特征提取策略来捕获不同尺度和层次的特征信息;DASI模块实现了自适应的通道选择与融合;MDCR模块通过多层深度可分离卷积捕捉不同感受野范围的空间特征。大量实验结果显示,在SIRST红外单帧图像数据集上,所提出的HCF - Net表现卓越,超越了其他传统模型和深度学习模型。代码可从github.com/zhengshuche…
文章链接
论文地址:论文地址
代码地址:代码地址
基本原理
HCF-Net(Hierarchical Context Fusion Network)是一种用于红外小目标检测的深度学习模型,旨在提高对红外图像中微小目标的识别和定位能力。
-
网络架构:HCF-Net采用了一种升级版的U-Net架构,主要由三个关键模块组成:Parallelized Patch-Aware Attention(PPA)模块、Dimension-Aware Selective Integration(DASI)模块和Multi-Dilated Channel Refiner(MDCR)模块。这些模块在不同层级上解决了红外小目标检测中的挑战 。
-
PPA模块:
- Hierarchical Feature Fusion:PPA模块利用分层特征融合和注意力机制,以在多次下采样过程中保持和增强小目标的表示,确保关键信息在整个网络中得以保留[T1]。
- Multi-Branch Feature Extraction:PPA采用多分支特征提取策略,以捕获不同尺度和级别的特征信息,从而提高小目标检测的准确性 。
-
DASI模块:
- Adaptive Feature Fusion:DASI模块增强了U-Net中的跳跃连接,专注于高低维特征的自适应选择和精细融合,以增强小目标的显著性 。
-
MDCR模块:
-
Spatial Feature Refinement:MDCR模块通过多个深度可分离卷积层捕获不同感受野范围的空间特征,更细致地建模目标和背景之间的差异,提高了定位小目标的能力 。

DASI
-
在红外小物体检测的多个降采样阶段中,高维特征可能会丢失小物体的信息,而低维特征可能无法提供足够的背景信息。为解决这一问题,提出了一种新颖的信道分区选择机制(如图 3 所示),使 DASI 能够根据物体的大小和特征自适应地选择合适的特征进行融合。
具体来说,DASI 首先通过卷积和插值等操作,将高维特征 和低维特征 ,以及当前层的特征 对齐。随后,它将这些特征在通道维度上分成四个相等的部分,从而得到 , , ,其中 分别表示高维、低维和当前层特征的第 个分区特征。
这些分区的计算公式如下:
其中,( \alpha \in \mathbb{R}^{H \times W \times \frac{C}{4}} \) 表示应用于 \( \mathbf{u}_i ) 的 sigmoid 激活函数后得到的值, 表示每个分区的选择性汇总结果。在通道维度上合并 后,得到 。操作 、 和 分别表示卷积、批量归一化(BN)和整流线性单元(ReLU),最终得到输出 。
如果 ,则模型优先考虑细粒度特征;如果 ,则强调上下文特征。

核心代码
class DASI(nn.Module):
def __init__(self, in_features, out_features) -> None:
super().__init__()
self.bag = Bag()
# 尾部卷积层
self.tail_conv = nn.Sequential(
conv_block(in_features=out_features,
out_features=out_features,
kernel_size=(1, 1),
padding=(0, 0),
norm_type=None,
activation=False)
)
# 主要卷积操作
self.conv = nn.Sequential(
conv_block(in_features=out_features // 2,
out_features=out_features // 4,
kernel_size=(1, 1),
padding=(0, 0),
norm_type=None,
activation=False)
)
# 批量归一化层
self.bns = nn.BatchNorm2d(out_features)
# 跳跃连接处理
self.skips = conv_block(in_features=in_features,
out_features=out_features,
kernel_size=(1, 1),
padding=(0, 0),
norm_type=None,
activation=False)
self.skips_2 = conv_block(in_features=in_features * 2,
out_features=out_features,
kernel_size=(1, 1),
padding=(0, 0),
norm_type=None,
activation=False)
self.skips_3 = nn.Conv2d(in_features // 2, out_features,
kernel_size=3, stride=2, dilation=2, padding=2)
# self.skips_3 = nn.Conv2d(in_features // 2, out_features,
# kernel_size=3, stride=2, dilation=1, padding=1)
# 激活函数
self.relu = nn.ReLU()
self.gelu = nn.GELU()
def forward(self, x, x_low, x_high):
if x_high is not None:
x_high = self.skips_3(x_high)
x_high = torch.chunk(x_high, 4, dim=1)
if x_low is not None:
x_low = self.skips_2(x_low)
x_low = F.interpolate(x_low, size=[x.size(2), x.size(3)], mode='bilinear', align_corners=True)
x_low = torch.chunk(x_low, 4, dim=1)
x_skip = self.skips(x)
x = self.skips(x)
x = torch.chunk(x, 4, dim=1)
if x_high is None:
x0 = self.conv(torch.cat((x[0], x_low[0]), dim=1))
x1 = self.conv(torch.cat((x[1], x_low[1]), dim=1))
x2 = self.conv(torch.cat((x[2], x_low[2]), dim=1))
x3 = self.conv(torch.cat((x[3], x_low[3]), dim=1))
elif x_low is None:
x0 = self.conv(torch.cat((x[0], x_high[0]), dim=1))
x1 = self.conv(torch.cat((x[0], x_high[1]), dim=1))
x2 = self.conv(torch.cat((x[0], x_high[2]), dim=1))
x3 = self.conv(torch.cat((x[0], x_high[3]), dim=1))
else:
x0 = self.bag(x_low[0], x_high[0], x[0])
x1 = self.bag(x_low[1], x_high[1], x[1])
x2 = self.bag(x_low[2], x_high[2], x[2])
x3 = self.bag(x_low[3], x_high[3], x[3])
# 合并处理后的特征
x = torch.cat((x0, x1, x2, x3), dim=1)
# 尾部卷积和跳跃连接
x = self.tail_conv(x)
x += x_skip
# 批量归一化和激活函数
x = self.bns(x)
x = self.relu(x)
return x
实验
脚本
import warnings
warnings.filterwarnings('ignore')
from ultralytics import YOLO
if __name__ == '__main__':
# 修改为自己的配置文件地址
model = YOLO('./ultralytics/cfg/models/26/yolo26-DASI.yaml')
# 修改为自己的数据集地址
model.train(data='./ultralytics/cfg/datasets/coco8.yaml',
cache=False,
imgsz=640,
epochs=10,
single_cls=False, # 是否是单类别检测
batch=8,
close_mosaic=10,
workers=0,
optimizer='MuSGD',
# optimizer='SGD',
amp=False,
project='runs/train',
name='yolo26-DASI',
)
结果
