目录

一、引言

二、CBAM注意力优势、结构图、代码

1. 更好地利用红外与可见光的互补信息

2. 提升复杂环境下的检测鲁棒性

3.代码说明

三、逐步手把手添加CBAM注意力

3.1 第一步

3.2 第二步

3.3 第三步

3.4 第四步 

四 完整yaml

4.1 YOLO11-MM前期(early)加入CBAM

4.2 YOLO11-MM中期(middle)加入CBAM

4.3 YOLO11-MM后期(middle)加入CBAM

五 训练代码和结果

5.1 模型训练代码

​编辑5.2 模型训练结果

六 总结

一、引言

本文主要围绕 YOLO11-MM 多模态目标检测 展开,重点探讨在网络中引入 CBAM 等注意力机制的核心思路与具体实践。我们将 CBAM 分别置于模态融合路径的前期、中期和后期,对不同插入位置进行改进与性能对比,系统分析在不同阶段引入注意力,对特征表达能力和最终检测效果带来的影响。

需要强调的是,YOLO11-MM 多模态目标检测与 RTDETR-MM 多模态目标检测在设计取向上存在明显差异
YOLO11-MM 更偏向轻量化与工程友好,结构更易于改进和融合各类增强模块,在推理速度、部署体验,以及训练稳定性与收敛性方面具有更明显的优势。

在实验过程中,我们一方面通过合理选择注意力模块的插入位置,尽可能增强模型的特征表示能力;另一方面也尽量控制额外参数量,在较低计算与存储开销的前提下,获得尽可能高的检测精度。近期我们已完成大量实验并对结果进行了系统整理,希望能在多模态目标检测方向的研究与工程实践中,为大家节省一定的调参与反复验证成本。

需要特别说明的是:本文所使用的数据集为 FLIR 数据集的一个子集,而非完整 FLIR 数据集,后续在复现或扩展实验时请务必留意这一点。也希望这篇文章能为正在攻关多模态目标检测的同学提供一些有价值的参考。

二、CBAM注意力优势、结构图、代码

1. 更好地利用红外与可见光的互补信息

FLIR 数据集的典型特点是:

  • 红外:对 人体、车辆等热目标 非常敏感,但纹理细节较弱;

  • 可见光:在白天有较丰富的 纹理与颜色信息,但在夜晚、逆光、雾天时容易退化。

CBAM 同时包含 通道注意力 + 空间注意力

  • 通道注意力可以自适应地放大“对当前场景更有用的模态/特征通道”,例如在夜间更依赖红外特征,在白天更平衡两种模态;

  • 空间注意力则侧重突出图像中真正可能是目标的区域,弱化大面积无用背景。

👉 这使得模型在模态融合阶段能够更智能地「按需取用」红外和可见光的信息,而不是简单相加或拼接。

2. 提升复杂环境下的检测鲁棒性

FLIR 中有大量 弱光、逆光、遮挡、远距离小目标 的场景,这些场景里:

  • 可见光图像噪声多、对比度低;

  • 红外图像虽然有热点,但边界模糊、容易和背景热源混淆。

CBAM 在空间维度上的注意力,可以:

  • 压制掉噪声较重或纹理不清晰的区域;

  • 更集中关注 人、车等高响应的真实目标区域

  • 对小目标周围做局部增强,让检测头更容易分类与回归。

👉 结果就是:在夜间、恶劣天气、远距离目标等难例上,召回率和精度都更稳定。

3.代码说明

import torch
import torch.nn as nn


class ChannelAttention(nn.Module):
    """Channel-attention module https://github.com/open-mmlab/mmdetection/tree/v3.0.0rc1/configs/rtmdet."""

    def __init__(self, channels: int) -> None:
        """Initializes the class and sets the basic configurations and instance variables required."""
        super().__init__()
        self.pool = nn.AdaptiveAvgPool2d(1)
        self.fc = nn.Conv2d(channels, channels, 1, 1, 0, bias=True)
        self.act = nn.Sigmoid()

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        """Applies forward pass using activation on convolutions of the input, optionally using batch normalization."""
        return x * self.act(self.fc(self.pool(x)))


class SpatialAttention(nn.Module):
    """Spatial-attention module."""

    def __init__(self, kernel_size=7):
        """Initialize Spatial-attention module with kernel size argument."""
        super().__init__()
        assert kernel_size in (3, 7), "kernel size must be 3 or 7"
        padding = 3 if kernel_size == 7 else 1
        self.cv1 = nn.Conv2d(2, 1, kernel_size, padding=padding, bias=False)
        self.act = nn.Sigmoid()

    def forward(self, x):
        """Apply channel and spatial attention on input for feature recalibration."""
        return x * self.act(self.cv1(torch.cat([torch.mean(x, 1, keepdim=True), torch.max(x, 1, keepdim=True)[0]], 1)))


class CBAM(nn.Module):
    """Convolutional Block Attention Module."""

    def __init__(self, c1, kernel_size=7):
        """Initialize CBAM with given input channel (c1) and kernel size."""
        super().__init__()
        self.channel_attention = ChannelAttention(c1)
        self.spatial_attention = SpatialAttention(kernel_size)

    def forward(self, x):
        """Applies the forward pass through C1 module."""
        return self.spatial_attention(self.channel_attention(x))

三、逐步手把手添加CBAM注意力

3.1 第一步

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

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

3.2 第二步

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

3.3 第三步

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

3.4 第四步 

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

        elif m in {CBAM}:  # 所有注意力机制模块
            c2 = ch[f]
            args = [c2, *args]

四 完整yaml

4.1 YOLO11-MM前期(early)加入CBAM

YOLO11-mm-early_cbam summary: 186 layers, 2,591,032 parameters, 2,591,016 gradients, 6.6 GFLOPs

# Ultralytics YOLO11 🚀 MultiModal Early Fusion Configuration
# 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:
  # P1/2 - 早期融合层:直接处理6通道输入(RGB+X)
  - [-1, 1, Conv, [64, 3, 2], 'Dual']     # 0-P1/2 (6ch->64ch early fusion)

  - [-1, 1, CBAM, []] # 0-P1/2 6-channel input                                   #########   加入注意力 ,同时也可以替换为其他的注意力

  - [-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

# 检测头 - 标准YOLO11检测头结构
head:
  - [-1, 1, nn.Upsample, [None, 2, "nearest"]]   # 11
  - [[-1, 7], 1, Concat, [1]]                    # 12 cat backbone P4
  - [-1, 2, C3k2, [512, False]]                  # 13

  - [-1, 1, nn.Upsample, [None, 2, "nearest"]]   # 14
  - [[-1, 5], 1, Concat, [1]]                    # 15 cat backbone P3
  - [-1, 2, C3k2, [256, False]]                  # 16 (P3/8-small)

  - [-1, 1, Conv, [256, 3, 2]]                   # 17
  - [[-1, 14], 1, Concat, [1]]                   # 18 cat head P4
  - [-1, 2, C3k2, [512, False]]                  # 19 (P4/16-medium)

  - [-1, 1, Conv, [512, 3, 2]]                   # 20
  - [[-1, 11], 1, Concat, [1]]                   # 21 cat backbone P5
  - [-1, 2, C3k2, [1024, True]]                  # 22 (P5/32-large)

  - [[17, 20, 23], 1, Detect, [nc]]              # 23 Detect(P3, P4, P5)

4.2 YOLO11-MM中期(middle)加入CBAM

YOLO11-mm-midddle_cbam summary: 332 layers, 5,111,354 parameters, 5,111,338 gradients, 11.4 GFLOPs

# Ultralytics YOLOMM 🚀 Standard Mid-Fusion Paradigm
# YOLOMM RGB+X 标准中期融合范式
# 核心策略: 双路独立P5骨干网 + P4/P5双层高阶融合 + 标准检测头
# 设计原则: 最大化利用各模态的深层语义信息,并通过标准检测头实现高效的特征聚合。
# 适用场景: 作为多模态检测的通用基准,适用于大多数需要中高层特征互补的任务。

# 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]

# P4/P5双层融合架构
backbone:
  # ========== RGB路径 (层0-10) - 完整骨干网到P5 ==========
  - [-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) - 完整骨干网到P5 ==========
  - [-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层级融合 ==========
  # P4级融合
  - [[6, 17], 1, Concat, [1]]             # 22 P4融合: RGB_P4(6) + X_P4(17)
  - [-1, 2, C3k2, [1024, True]]           # 23 (Fused_P4)
  - [-1, 1, CBAM, []] # 0-P1/24   6-channel input                                   #########   加入注意力 ,同时也可以替换为其他的注意力


  # P5级融合
  - [[10, 21], 1, Concat, [1]]            # 24 P5融合: RGB_P5(10) + X_P5(21)
  - [-1, 2, C3k2, [1024, True]]           # 25
  - [-1, 1, C2PSA, [1024]]                # 26 (Fused_P5)
  - [-1, 1, CBAM, []] # 0-P1/28 6-channel input                                   #########   加入注意力 ,同时也可以替换为其他的注意力


# 标准YOLOv11检测头 (Neck + Head)
head:
  # 自顶向下路径 (FPN)
  - [28, 1, nn.Upsample, [None, 2, "nearest"]]  # 27 Fused_P5上采样
  - [[-1, 24], 1, Concat, [1]]                  # 28 连接Fused_P4
  - [-1, 2, C3k2, [512, False]]                 # 29

  - [-1, 1, nn.Upsample, [None, 2, "nearest"]]  # 30
  - [[-1, 4], 1, Concat, [1]]                   # 31 连接RGB_P3(4)
  - [-1, 2, C3k2, [256, False]]                 # 32 (P3/8-small)

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

  - [-1, 1, Conv, [512, 3, 2]]                  # 36
  - [[-1, 28], 1, Concat, [1]]                  # 37
  - [-1, 2, C3k2, [1024, True]]                 # 38 (P5/32-large)

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

4.3 YOLO11-MM后期(middle)加入CBAM

YOLO11-mm-late_cbam summary: 336 layers, 5,483,772 parameters, 5,483,756 gradients, 12.8 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, CBAM, []] # 0-P1/2 6-channel input                                   #########   加入注意力 ,同时也可以替换为其他的注意力


  - [[30, 42], 1, Concat, [1]]                   # 47 融合P4
  - [-1, 1, CBAM, []] # 0-P1/2 6-channel input                                   #########   加入注意力 ,同时也可以替换为其他的注意力


  - [[33, 45], 1, Concat, [1]]                   # 48 融合P5
  - [-1, 1, CBAM, []] # 0-P1/2 6-channel input                                   #########   加入注意力 ,同时也可以替换为其他的注意力



  - [[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/attention/yolo11-mm-late_cbam.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

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐