摘要

本文提出了一种基于YOLO11-MM框架的多模态目标检测改进方法,重点针对红外与可见光融合场景下的检测性能优化。通过引入高频感知模块(HFP),在频域对特征进行高通滤波处理,突出小目标的纹理与边缘信息,并采用通道与空间注意力机制增强网络对高频细节的感知能力。实验在FLIR数据集上验证了HFP模块的有效性,结果表明该方法能够显著提升模型在夜间、低对比度等复杂环境下的检测性能,同时对计算资源需求保持可控。文章详细介绍了模块实现、网络结构设计以及训练配置方案,为多模态目标检测研究提供了实用参考。

摘要

一、引言

二、高频感知模块(HighFrequencyPerception)模块

2.1、核心思想(HFP × YOLO11-MM )

2.2、突出贡献(HFP 在多模态 FLIR 检测中的价值)

2.3、优势特点(面向 FLIR 数据集的工程与性能优势)

三、逐步手把手添加高频感知模块(HFP)

3.1 第一步

3.2 第二步

3.3 第三步

3.4 第四步 

四 完整yaml

4.1 YOLO11-MM中期(early)加入高频感知模块 (HFP)

4.2 YOLO11-MM后期(late)加入高频感知模块 (HFP)

 五 训练代码和结果

5.1 模型训练代码

5.2 模型训练结果

六 总结


一、引言

本文围绕 YOLO11-MM 多模态目标检测框架 的结构改进与性能优化展开研究,重点探讨在红外(Infrared)与可见光(Visible)多模态场景下,如何通过更精细的特征建模与跨模态交互机制,提升复杂环境中的目标检测性能。

针对传统多模态特征融合方法在小目标、弱纹理及复杂背景场景下表达能力不足的问题,本文引入 HFP(High Frequency Perception)模块,在频域对特征进行高通滤波,有效突出小目标的纹理与边缘信息,并进一步将其映射为 通道注意力与空间注意力,增强网络对高频细节的感知能力。在此基础上,结合 SDP(Spatial Dependency Perception)模块,在相邻特征金字塔层之间引入像素级 cross-attention 机制,使上采样后的高层语义特征与底层细节特征实现细粒度对齐,从而完成红外与可见光特征的高效融合。

在具体实现层面,本文系统分析了 HFP 模块在网络不同阶段的插入策略,围绕多模态特征融合过程,设计并对比了 中期融合(Middle Fusion)后期融合(Late Fusion) 两种实验方案,深入探讨 HFP 介入时机对特征表达能力及最终检测性能的影响,为多模态检测网络的结构设计提供了可参考的实践经验。

需要特别说明的是,本文实验所采用的数据集为 FLIR 数据集的子集,而非完整 FLIR 数据集。在进行实验复现或进一步扩展研究时,读者需注意数据划分与配置上的差异,以避免由数据规模或分布不一致带来的结果偏差。希望本文的研究与实践经验,能够为多模态目标检测领域的研究者与工程实践者提供具有参考价值的技术思路与实现范式。

二、高频感知模块(HighFrequencyPerception)模块

论文:https://arxiv.org/pdf/2412.10116

2.1、核心思想(HFP × YOLO11-MM )

在 YOLO11-MM 多模态目标检测框架中,HFP(High Frequency Perception)模块的核心思想是:在红外与可见光特征完成初步融合之后,引入频域高频感知机制,显式建模对小目标和边缘结构最敏感的高频信息,从而弥补传统 FPN 与 Concat 融合在细粒度特征表达上的不足

具体而言,HFP 首先对融合后的特征映射执行频域变换(FFT),通过高通滤波抑制红外与可见光中高度相关但判别性较弱的低频冗余信息,保留对小目标检测至关重要的纹理、轮廓与边缘响应;随后,分别构建 空间注意力(Spatial Mask)通道注意力(Channel Weight),对高频响应进行自适应门控,引导网络在空间位置与语义通道两个维度上聚焦有效区域。
在 FLIR 数据集中常见的夜间、低对比度、热噪声干扰场景下,这种“先频域筛选、再注意力增强”的设计,使 YOLO11-MM 能够更稳定地感知红外与可见光互补信息,为后续检测头提供更加判别性的融合特征。

2.2、突出贡献(HFP 在多模态 FLIR 检测中的价值)

HFP 模块在 YOLO11-MM 多模态目标检测框架中的贡献,体现在 对传统特征融合范式的结构性补强。不同于简单的通道拼接(Concat)或逐元素相加(Add),HFP 从频域层面对融合特征进行重新解构,使模型首次在 YOLO 系列多模态检测中显式引入 “高频感知 + 空间/通道双门控” 的融合后增强策略。

从工程与实验角度看,HFP 的引入有效解决了 FLIR 数据集中普遍存在的三类问题:
1)红外模态中目标边界模糊、细节缺失;
2)可见光模态在夜间或强噪声环境下语义不稳定;
3)多模态融合后低频信息冗余掩盖小目标响应。

2.3、优势特点(面向 FLIR 数据集的工程与性能优势)

在 FLIR 红外–可见光多模态数据集上,HFP 采用 频域高通滤波 + 轻量级注意力门控 的组合设计,避免了复杂 Transformer 结构带来的计算负担,在实际部署中对 FLOPs 与显存占用影响可控,适合实时或近实时检测需求。

其次,相比仅在空间域建模注意力的模块,HFP 能够直接作用于高频成分,对小尺寸目标、远距离行人、弱热源目标表现出更强的鲁棒性,尤其适用于 FLIR 场景中常见的夜间行人、车辆轮廓与低分辨率目标检测任务。
此外,HFP 作为一种 融合后特征增强模块,具有良好的结构解耦性,可灵活插入 YOLO11-MM 的中期融合或后期融合阶段,与 SDP 等跨层模块形成互补关系,整体提升模型在复杂多模态场景下的稳定性与泛化能力。

代码说明

# 论文: HS-FPN: High Frequency and Spatial Perception FPN for Tiny Object Detection (AAAI 2025)
# 链接: https://arxiv.org/abs/2412.10116
# 模块作用: 对融合后特征进行频域高频感知与空间/通道门控,突出细粒度目标与边缘信息,抑制跨模态低频冗余。

import torch
import torch.nn as nn


class HighFrequencyPerception(nn.Module):
    def __init__(self, ratio: tuple[float, float] = (0.25, 0.25), patch: tuple[int, int] = (8, 8), groups: int = 32) -> None:
        super().__init__()
        self.ratio = ratio
        self.ph, self.pw = int(patch[0]), int(patch[1])
        self.groups = int(groups)
        self._built = False
        self._c = None
        self.spatial_conv: nn.Module | None = None
        self.channel_conv1: nn.Module | None = None
        self.channel_conv2: nn.Module | None = None
        self.out_conv: nn.Module | None = None

    def _build_if_needed(self, c: int) -> None:
        if self._built and self._c == c:
            return
        g = max(1, min(self.groups, c))
        self.spatial_conv = nn.Sequential(
            nn.Conv2d(c, 1, kernel_size=1, bias=True),
            nn.BatchNorm2d(1),
            nn.Sigmoid(),
        )
        self.channel_conv1 = nn.Conv2d(c, c, kernel_size=1, groups=g)
        self.channel_conv2 = nn.Conv2d(c, c, kernel_size=1, groups=g)
        self.out_conv = nn.Sequential(
            nn.Conv2d(c, c, kernel_size=3, padding=1, bias=False),
            nn.GroupNorm(num_groups=min(32, c), num_channels=c),
        )
        self._built = True
        self._c = c

    def _mask_fft(self, x: torch.Tensor) -> torch.Tensor:
        B, C, H, W = x.shape
        xf = torch.fft.rfft2(x, dim=(-2, -1))
        h0 = int(H * self.ratio[0])
        w0 = int((W // 2 + 1) * self.ratio[1])
        mask = torch.ones_like(xf, dtype=xf.dtype)
        mask[:, :, :h0, :w0] = 0
        xf = xf * mask
        xh = torch.fft.irfft2(xf, s=(H, W))
        return xh

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        if not isinstance(x, torch.Tensor) or x.dim() != 4:
            raise TypeError("HighFrequencyPerception 期望输入 [B, C, H, W]")
        B, C, H, W = x.shape
        self._build_if_needed(C)
        hf = self._mask_fft(x)
        spa = self.spatial_conv(hf) * x
        amax = torch.nn.functional.adaptive_max_pool2d(hf, output_size=(self.ph, self.pw))
        aavg = torch.nn.functional.adaptive_avg_pool2d(hf, output_size=(self.ph, self.pw))
        amax = torch.sum(torch.relu(amax), dim=(2, 3), keepdim=True)
        aavg = torch.sum(torch.relu(aavg), dim=(2, 3), keepdim=True)
        ch = self.channel_conv1(amax) + self.channel_conv1(aavg)
        ch = torch.sigmoid(self.channel_conv2(ch))
        cha = ch * x
        out = self.out_conv(spa + cha)
        return out

三、逐步手把手添加高频感知模块(HFP)

3.1 第一步

在 ultralytics/nn 目录下面,新建一个叫 fusion的文件夹,然后在里面分别新建一个.py 文件,把注意力模块的“核心代码”粘进去。

注意🔸 如果你使用我完整的项目代码,这个 fusion文件夹已经有了、里面的模块也是有的,直接使用进行训练和测试,如果没有你只需要在里面新建一个 py 文件或直接修改已有的即可,如下图所示。

3.2 第二步

第二步:在该目录下新建一个名为 __init__.py 的 Python 文件(如果使用的是我项目提供的工程,该文件一般已经存在,无需重复创建),然后在该文件中导入我们自定义的注意力EMA,具体写法如下图所示。

3.3 第三步

第三步:找到 ultralytics/nn/tasks.py 文件,在其中完成我们模块的导入和注册(如果使用的是我提供的项目工程,该文件已自带,无需新建)。具体书写方式如下图所示

3.4 第四步 

第四步:找到 ultralytics/nn/tasks.py 文件,在 parse_model 方法中加入对应配置即可,具体书写方式如下图所示。

        elif m in frozenset({CAM,HighFrequencyPerception}):
            # Expect exactly two inputs; output channels follow the left branch
            if isinstance(f, int) or len(f) != 2:
                raise ValueError(f"{m.__name__} expects 2 inputs, got {f} at layer {i}")
            c_left, c_right = ch[f[0]], ch[f[1]]
            # Auto infer dim if not provided (None or missing)
            if len(args) == 0:
                args.insert(0, c_left)
            elif args[0] is None:
                args[0] = c_left
            c2 = c_left

四 完整yaml

4.1 YOLO11-MM中期(early)加入高频感知模块 (HFP)

训练信息:summary: 336 layers, 6,169,980 parameters, 6,169,964 gradients, 13.6 GFLOPs


# Parameters
nc: 80 # number of classes
scales: # model compound scaling constants
  # [depth, width, max_channels]
  n: [0.50, 0.25, 1024]
  s: [0.50, 0.50, 1024]
  m: [0.50, 1.00, 512]
  l: [1.00, 1.00, 512]
  x: [1.00, 1.50, 512]

backbone:
  # ========== RGB路径 (层0-10) ==========
  - [-1, 1, Conv, [64, 3, 2], 'RGB']      # 0-P1/2 RGB路径起始
  - [-1, 1, Conv, [128, 3, 2]]            # 1-P2/4
  - [-1, 2, C3k2, [256, False, 0.25]]     # 2
  - [-1, 1, Conv, [256, 3, 2]]            # 3-P3/8 (RGB_P3)
  - [-1, 2, C3k2, [512, False, 0.25]]     # 4
  - [-1, 1, Conv, [512, 3, 2]]            # 5-P4/16
  - [-1, 2, C3k2, [512, True]]            # 6 (RGB_P4)
  - [-1, 1, Conv, [1024, 3, 2]]           # 7-P5/32
  - [-1, 2, C3k2, [1024, True]]           # 8
  - [-1, 1, SPPF, [1024, 5]]              # 9
  - [-1, 2, C2PSA, [1024]]                # 10 (RGB_P5)

  # ========== X路径 (层11-21) ==========
  - [-1, 1, Conv, [64, 3, 2], 'X']        # 11-P1/2 X路径起始
  - [-1, 1, Conv, [128, 3, 2]]            # 12-P2/4
  - [-1, 2, C3k2, [256, False, 0.25]]     # 13
  - [-1, 1, Conv, [256, 3, 2]]            # 14-P3/8 (X_P3)
  - [-1, 2, C3k2, [512, False, 0.25]]     # 15
  - [-1, 1, Conv, [512, 3, 2]]            # 16-P4/16
  - [-1, 2, C3k2, [512, True]]            # 17 (X_P4)
  - [-1, 1, Conv, [1024, 3, 2]]           # 18-P5/32
  - [-1, 2, C3k2, [1024, True]]           # 19
  - [-1, 1, SPPF, [1024, 5]]              # 20
  - [-1, 2, C2PSA, [1024]]                # 21 (X_P5)

  # ========== P4 / P5 融合 + HFP ==========
  - [[6, 17], 1, Concat, [1]]            # 22: P4 融合
  - [-1, 2, C3k2, [1024, True]]          # 23: Fused_P4
  - [-1, 1, HighFrequencyPerception, []] # 24: Fused_P4_HFP

  - [[10, 21], 1, Concat, [1]]           # 25: P5 融合
  - [-1, 2, C3k2, [1024, True]]          # 26
  - [-1, 1, C2PSA, [1024]]               # 27: Fused_P5
  - [-1, 1, HighFrequencyPerception, []] # 28: Fused_P5_HFP

head:
  # 自顶向下路径 (FPN)
  - [28, 1, nn.Upsample, [None, 2, "nearest"]]  # 29 Fused_P5_HFP 上采样
  - [[-1, 24], 1, Concat, [1]]                  # 30 + Fused_P4_HFP
  - [-1, 2, C3k2, [512, False]]                 # 31

  - [-1, 1, nn.Upsample, [None, 2, "nearest"]]  # 32
  - [[-1, 4], 1, Concat, [1]]                   # 33 + RGB_P3(4)
  - [-1, 2, C3k2, [256, False]]                 # 34 (P3/8-small)

  # 自底向上路径 (PAN)
  - [-1, 1, Conv, [256, 3, 2]]                  # 35
  - [[-1, 31], 1, Concat, [1]]                  # 36
  - [-1, 2, C3k2, [512, False]]                 # 37 (P4/16-medium)

  - [-1, 1, Conv, [512, 3, 2]]                  # 38
  - [[-1, 27], 1, Concat, [1]]                  # 39
  - [-1, 2, C3k2, [1024, True]]                 # 40 (P5/32-large)

  - [[34, 37, 40], 1, Detect, [nc]]             # 41 Detect(P3, P4, P5)

4.2 YOLO11-MM后期(late)加入高频感知模块 (HFP)

训练信息:summary: 342 layers, 8,261,087 parameters, 8,261,071 gradients, 18.2 GFLOPs

# Ultralytics YOLO11 🚀 MultiModal Late Fusion Reference
# 说明:该“late-ref”版本遵循 YOLOMM 设计范式与分支书写规范,使用第5字段进行模态路由标注。

# Parameters
nc: 80 # number of classes
scales: # model compound scaling constants, i.e. 'model=yolo11n.yaml' will call yolo11.yaml with scale 'n'
  # [depth, width, max_channels]
  n: [0.50, 0.25, 1024] # YOLO11n summary: 319 layers, 2624080 parameters, 2624064 gradients, 6.6 GFLOPs
  s: [0.50, 0.50, 1024] # YOLO11s summary: 319 layers, 9458752 parameters, 9458736 gradients, 21.7 GFLOPs
  m: [0.50, 1.00, 512]  # YOLO11m summary: 409 layers, 20114688 parameters, 20114672 gradients, 68.5 GFLOPs
  l: [1.00, 1.00, 512]  # YOLO11l summary: 631 layers, 25372160 parameters, 25372144 gradients, 87.6 GFLOPs
  x: [1.00, 1.50, 512]  # YOLO11x summary: 631 layers, 56966176 parameters, 56966160 gradients, 196.4 GFLOPs


backbone:
  # RGB路径 (层0-10) - 完整的RGB特征提取
  - [-1, 1, Conv, [64, 3, 2], 'RGB']      # 0-P1/2 RGB路径起始
  - [-1, 1, Conv, [128, 3, 2]]            # 1-P2/4
  - [-1, 2, C3k2, [256, False, 0.25]]     # 2
  - [-1, 1, Conv, [256, 3, 2]]            # 3-P3/8
  - [-1, 2, C3k2, [512, False, 0.25]]     # 4
  - [-1, 1, Conv, [512, 3, 2]]            # 5-P4/16
  - [-1, 2, C3k2, [512, True]]            # 6
  - [-1, 1, Conv, [1024, 3, 2]]           # 7-P5/32
  - [-1, 2, C3k2, [1024, True]]           # 8
  - [-1, 1, SPPF, [1024, 5]]              # 9
  - [-1, 2, C2PSA, [1024]]                # 10 RGB路径结束

  # X路径 (层11-21) - 完整的X模态特征提取
  - [-1, 1, Conv, [64, 3, 2], 'X']        # 11-P1/2 X路径起始(from=-1但实际从X模态输入)
  - [-1, 1, Conv, [128, 3, 2]]            # 12-P2/4
  - [-1, 2, C3k2, [256, False, 0.25]]     # 13
  - [-1, 1, Conv, [256, 3, 2]]            # 14-P3/8
  - [-1, 2, C3k2, [512, False, 0.25]]     # 15
  - [-1, 1, Conv, [512, 3, 2]]            # 16-P4/16
  - [-1, 2, C3k2, [512, True]]            # 17
  - [-1, 1, Conv, [1024, 3, 2]]           # 18-P5/32
  - [-1, 2, C3k2, [1024, True]]           # 19
  - [-1, 1, SPPF, [1024, 5]]              # 20
  - [-1, 2, C2PSA, [1024]]                # 21 X路径结束

# 独立检测头 - RGB和X模态各自独立检测,最后融合决策
head:
  # RGB检测分支 (层22-33)
  - [10, 1, nn.Upsample, [None, 2, "nearest"]]   # 22 从RGB backbone
  - [[-1, 6], 1, Concat, [1]]                    # 23 cat RGB backbone P4
  - [-1, 2, C3k2, [512, False]]                  # 24

  - [-1, 1, nn.Upsample, [None, 2, "nearest"]]   # 25
  - [[-1, 4], 1, Concat, [1]]                    # 26 cat RGB backbone P3
  - [-1, 2, C3k2, [256, False]]                  # 27 (RGB P3/8-small)

  - [-1, 1, Conv, [256, 3, 2]]                   # 28
  - [[-1, 24], 1, Concat, [1]]                   # 29 cat RGB head P4
  - [-1, 2, C3k2, [512, False]]                  # 30 (RGB P4/16-medium)

  - [-1, 1, Conv, [512, 3, 2]]                   # 31
  - [[-1, 10], 1, Concat, [1]]                   # 32 cat RGB backbone P5
  - [-1, 2, C3k2, [1024, True]]                  # 33 (RGB P5/32-large)

  # X模态检测分支 (层34-45)
  - [21, 1, nn.Upsample, [None, 2, "nearest"]]   # 34 从X backbone
  - [[-1, 17], 1, Concat, [1]]                   # 35 cat X backbone P4
  - [-1, 2, C3k2, [512, False]]                  # 36

  - [-1, 1, nn.Upsample, [None, 2, "nearest"]]   # 37
  - [[-1, 15], 1, Concat, [1]]                   # 38 cat X backbone P3
  - [-1, 2, C3k2, [256, False]]                  # 39 (X P3/8-small)

  - [-1, 1, Conv, [256, 3, 2]]                   # 40
  - [[-1, 36], 1, Concat, [1]]                   # 41 cat X head P4
  - [-1, 2, C3k2, [512, False]]                  # 42 (X P4/16-medium)

  - [-1, 1, Conv, [512, 3, 2]]                   # 43
  - [[-1, 21], 1, Concat, [1]]                   # 44 cat X backbone P5
  - [-1, 2, C3k2, [1024, True]]                  # 45 (X P5/32-large)

  - [[27, 39], 1, Concat, [1]]                   # 46 融合P3
  - [-1, 1, HighFrequencyPerception, []] # 28: Fused_P5_HFP
  - [[30, 42], 1, Concat, [1]]                   # 47 融合P4
  - [-1, 1, HighFrequencyPerception, []] # 28: Fused_P5_HFP
  - [[33, 45], 1, Concat, [1]]                   # 48 融合P5
  - [-1, 1, HighFrequencyPerception, []] # 28: Fused_P5_HFP



  - [[47, 49, 51], 1, Detect, [nc]]              # 49 Detect(P3, P4, P5)

 五 训练代码和结果

5.1 模型训练代码

import warnings
from ultralytics import YOLOMM

# 1. 可选:屏蔽 timm 的未来弃用警告(不影响训练,仅减少控制台噪音)
warnings.filterwarnings(
    "ignore",
    category=FutureWarning,
    message="Importing from timm.models.layers is deprecated, please import via timm.layers"
)

if __name__ == "__main__":
    # 2. 加载多模态模型配置(RGB + IR)
    #    这里使用官方提供的 yolo11n-mm-mid 配置,你也可以换成自己的 yaml
    model = YOLOMM("ultralytics/cfg/models/FFT/yolo11n-mm-late-hfp.yaml")

    # 3. 启动训练
    model.train(
        data="FLIR3C/data.yaml",   # 多模态数据集配置(上一节已经编写)
        epochs=10,                 # 训练轮数,实际实验中建议 100+ 起步
        batch=4,                   # batch size,可根据显存大小调整
        imgsz=640,                 # 输入分辨率(默认 640),可与数据集分辨率统一
        device=0,                  # 指定 GPU id,CPU 训练可写 "cpu"
        workers=4,                 # dataloader 线程数(Windows 一般 0~4 比较稳)
        project="runs/mm_exp",     # 训练结果保存根目录
        name="rtdetrmm_flir3c",    # 当前实验名,对应子目录名
        # resume=True,             # 如需从中断的训练继续,可打开此项
        # patience=30,             # 早停策略,连降若干轮 mAP 不提升则停止
        # modality="X",            # 模态消融参数(默认由 data.yaml 中的 modality_used 决定)
        # cache=True,              # 启用图片缓存,加快 IO(内存足够时可打开)
    )

5.2 模型训练结果

六 总结

到这里,本文的正式内容就告一段落啦。

最后也想郑重向大家推荐我的专栏 「YOLO11-MM 多模态目标检测」。目前专栏整体以实战为主,每一篇都是我亲自上手验证后的经验沉淀。后续我也会持续跟进最新顶会的前沿工作进行论文复现,并对一些经典方法及改进机制做系统梳理和补充。

✨如果这篇文章对你哪怕只有一丝帮助,欢迎订阅本专栏、关注我,并私信联系,我会拉你进入 「YOLO11-MM 多模态目标检测」技术交流群 QQ 群~
你的支持,就是我持续输出的最大动力!✨


Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐