云计算百科
云计算领域专业知识百科平台

YOLO11改进 - C3k2融合 | IRA倒残差注意力模块,轻量融合卷积表征与通道空间注意,助力移动端多光谱颜色校正误差大幅降低 | CVPR 2026

前言

本文介绍了用于轻量端到端颜色校正的倒残差注意力模块 IRA,融合了 MobileNet 风格倒残差结构与通道、空间注意力机制。该模块通过深度可分离卷积高效提取局部特征,并利用注意力自适应强化与颜色、纹理、边缘相关的关键信息,在较低计算成本下提升特征选择能力与表达稳定性。我们将 IRA 成功集成进 YOLO11 的 C3k2 模块,构建 C3k2_IRA,用于增强主干与颈部网络的局部细节建模和有效特征筛选能力,从而提升目标检测中的特征表达效果。

文章目录: YOLO11改进大全:卷积层、轻量化、注意力机制、损失函数、Backbone、SPPF、Neck、检测头全方位优化汇总

专栏链接: YOLO11改进专栏

介绍

image-20260812215334303

近年来,快照式多光谱(MS)成像的发展使紧凑、低成本的光谱传感器能够应用于消费级和移动设备。相比传统 RGB 传感器,这类系统能够捕获更丰富的光谱信息,从而增强包括颜色校正在内的关键成像任务。然而,大多数现有方法将颜色校正流程拆分为多个独立阶段,并且往往在流程早期就丢弃 MS 数据。本文提出一种统一的、基于学习的框架,用于执行端到端颜色校正,并联合利用来自高分辨率 RGB 传感器和辅助低分辨率 MS 传感器的数据。该方法将完整颜色校正流程整合到单一模型中,生成一致且颜色准确的输出。作者通过重构两种不同的先进图像到图像架构,展示了该框架的灵活性和通用性。为支持训练和评估,本文聚合并重新利用公开光谱数据集,构建了专用数据集,并在多种 RGB 相机光谱灵敏度下进行渲染。大量实验表明,该方法提升了颜色准确性和稳定性,相比仅 RGB 和 MS 驱动的基线,误差最高降低 50%。代码、模型和数据集均已公开。

文章链接

论文地址:论文地址

代码地址:代码地址

基本原理

1. 解决的关键问题

IRA 所在的这篇论文主要解决移动端相机颜色校正中 RGB 信息不足、传统管线误差逐级传播以及多光谱信息利用不充分的问题。传统相机颜色校正通常被拆成自动白平衡(AWB)和颜色空间变换(CST)两个阶段:前者估计并抵消场景光照颜色,后者将白平衡后的相机 RGB 映射到 CIE XYZ 或 ProPhoto RGB 等设备无关颜色空间。这种模块化流程虽然高效,但每个阶段相互独立,前一步的照明估计误差会影响后续颜色变换,复杂光照下容易出现偏色和不稳定。

更根本的问题在于,普通 RGB 传感器只有三个宽光谱通道,难以充分区分物体反射率和场景光照。当不同光照、不同材质或不同相机光谱响应组合在一起时,RGB 数值可能高度相似,但真实颜色却不同,这会造成颜色恒常性和颜色空间映射的歧义。快照式低分辨率多光谱传感器可以提供额外光谱线索,但现有方法常常只把 MS 信息用于照明估计,之后仍然回到传统 RGB 管线,导致多光谱信息没有贯穿整个颜色校正过程。IRA 模块所在的 LPIENet 改造方案,就是为了在轻量图像到图像网络中更有效地处理 RGB 与 MS 特征,让网络端到端地联合完成照明估计、照明抵消和颜色空间变换。

2. 整体架构

论文提出的是一个双输入端到端颜色校正框架:输入包括高分辨率 RGB 图像和辅助低分辨率多光谱图像,输出为颜色校正后的 CIE XYZ 图像。作者没有从零设计全新网络,而是重构了两类已有轻量图像到图像架构,其中 LPIENet 分支的核心组件就是 IRA block。LPIENet 原本面向移动端实时图像增强,采用类似 U-Net 的编码器-解码器结构,适合需要空间一致输出的颜色校正任务。

在改造后的 LPIENet 中,RGB 图像首先经过 RGB encoder 提取多尺度特征,网络包含三个编码块、两个解码块、跳跃连接以及最终 refinement block。为了引入多光谱模态,论文额外加入一个 spectral encoder,该分支镜像 RGB encoder 的结构,由三个 IRA block 组成,但不执行下采样,以适配低分辨率 MS 输入并提取光谱辅助特征。每一层多光谱编码器提取的特征,会通过 skip connection 与 RGB 编码器对应层的特征融合,具体采用逐元素相加,并在每个解码阶段之前注入。这样,RGB 分支负责保留高分辨率空间结构,MS 分支负责提供更丰富的光谱先验,IRA 则作为两个分支中的轻量特征提取与注意力增强单元,为后续颜色校正输出提供稳定表征。

3. 技术原理

IRA 的全称是 Inverted Residual Attention,即倒残差注意力模块。它结合了 MobileNet 风格的轻量卷积结构和 CBAM 类似的通道/空间注意力思想。倒残差结构的价值在于以较低参数量和计算量完成有效特征变换:通常先通过 1×1 卷积进行通道变换,再使用深度可分离卷积提取局部空间信息,最后再通过 1×1 卷积投影回目标通道维度,并通过残差连接保持信息流动。这种设计非常适合移动端颜色校正,因为颜色校正需要对整幅图像产生空间连续、细节一致的输出,同时又不能引入过重计算。

在 IRA 中,注意力机制进一步增强了倒残差卷积的选择能力。通道注意力用于判断哪些特征通道对当前颜色校正更重要,例如与光照颜色、材质反射、局部色偏相关的响应;空间注意力则用于定位图像中更需要校正或更具参考价值的区域,例如高亮区域、阴影区域、色彩边界和材质变化位置。论文图示中,IRA block 包含 IRB 卷积分支,并并行结合 channel attention 与 spatial attention,最终通过逐元素乘法和残差/融合操作强化有效特征。相比纯卷积模块,IRA 能更自适应地选择颜色相关信息;相比重型 Transformer 注意力,它又保持了移动端友好的轻量特性。

在多光谱颜色校正场景中,IRA 的作用不仅是增强 RGB 图像特征,还承担了光谱特征编码的基础单元。对于 spectral encoder 来说,三个 IRA block 从低分辨率 MS 图像中提取与照明、反射率和相机光谱响应相关的辅助特征;这些特征通过跳跃连接注入 RGB 解码流程,使网络不再只依赖 RGB 三通道猜测真实颜色,而是在端到端映射中持续利用 MS 线索。实验结果也支持这一设计:论文在消融中移除 spectral encoder 后,性能明显下降;加入 MS 信息后,LPIENet、LPIENet-small 和 cmKAN-light 的平均 ΔE00 与 Reproduction Error 均显著改善,整体误差最高可比 RGB-only 设置降低约 50%。因此,IRA 可以被理解为该轻量多光谱颜色校正框架中的关键积木:它用低成本卷积保证移动端可部署性,用通道与空间注意力增强颜色相关表征,再通过 RGB-MS 双分支融合提升复杂光照下的颜色准确性和稳定性。

核心代码

YOLO11引入代码

在根目录下的ultralytics/nn/目录,新建一个 C3k2目录,然后新建一个以 C3k2_IRA为文件名的py文件, 把代码拷贝进去。

import torch
import torch.nn as nn
import torch.nn.functional as F
from ultralytics.nn.modules.conv import RepConv, Conv
from ultralytics.nn.modules.block import C3k, C3k2

class AttentionBlock(nn.Module):
def __init__(self, dim: int):
super(AttentionBlock, self).__init__()
self._spatial_attention_conv = nn.Conv2d(2, dim, kernel_size=3, padding=1)

# Channel attention MLP
self._channel_attention_conv0 = nn.Conv2d(1, dim, kernel_size=1, padding=0)
self._channel_attention_conv1 = nn.Conv2d(dim, dim, kernel_size=1, padding=0)

self._out_conv = nn.Conv2d(2 * dim, dim, kernel_size=1, padding=0)

def forward(self, x: torch.Tensor):
if len(x.shape) != 4:
raise ValueError(f"Expected [B, C, H, W] input, got {x.shape}.")

# Spatial attention
mean = torch.mean(x, dim=1, keepdim=True) # Mean/Max on C axis
max, _ = torch.max(x, dim=1, keepdim=True)
spatial_attention = torch.cat([mean, max], dim=1) # [B, 2, H, W]
spatial_attention = self._spatial_attention_conv(spatial_attention)
spatial_attention = torch.sigmoid(spatial_attention) * x

# Channel attention. TODO: Correct that it only uses average pool contrary to CBAM?
# NOTE/TODO: This differs from CBAM as it uses Channel pooling, not spatial pooling!
# In a way, this is 2x spatial attention
channel_attention = torch.relu(self._channel_attention_conv0(mean))
channel_attention = self._channel_attention_conv1(channel_attention)
channel_attention = torch.sigmoid(channel_attention) * x

attention = torch.cat([spatial_attention, channel_attention], dim=1) # [B, 2*dim, H, W]
attention = self._out_conv(attention)
return x + attention

class BaseBlock(nn.Module):
def __init__(self, channels: int):
super(BaseBlock, self).__init__()

self._conv0 = nn.Conv2d(channels, channels, kernel_size=1)
self._dw_conv = nn.Conv2d(channels, channels, kernel_size=3, padding=1, groups=channels)
self._conv1 = nn.Conv2d(channels, channels, kernel_size=1)

self._conv2 = nn.Conv2d(channels, channels, kernel_size=1)
self._conv3 = nn.Conv2d(channels, channels, kernel_size=1)

def forward(self, x: torch.Tensor):
features = self._conv0(x)
features = F.elu(self._dw_conv(features)) # TODO: ELU or ReLU?
features = self._conv1(features)
x = x + features

features = F.elu(self._conv2(x))
features = self._conv3(features)
return x + features

class IRA(nn.Module):
def __init__(self, inc, ouc):
super().__init__()

self.proj = nn.Conv2d(inc, ouc, kernel_size=3, padding=1)

self.irb = nn.Sequential(
nn.Conv2d(ouc, ouc, kernel_size=1),
nn.Conv2d(ouc, ouc, kernel_size=3, padding=1, groups=ouc),
nn.ELU(inplace=True),
nn.Conv2d(ouc, ouc, kernel_size=1),
)

self.ffn = nn.Sequential(
nn.Conv2d(ouc, ouc, kernel_size=1),
nn.ELU(inplace=True),
nn.Conv2d(ouc, ouc, kernel_size=1),
)

self.attn = AttentionBlock(ouc)

def forward(self, x):
x = self.proj(x)

identity = x
x = identity + self.irb(x)

identity = x
x = identity + self.ffn(x)

x = self.attn(x)
return x

class C3k_IRA(C3k):
def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5, k=3):
super().__init__(c1, c2, n, shortcut, g, e, k)
c_ = int(c2 * e) # hidden channels
self.m = nn.Sequential(*(IRA(c_, c_) for _ in range(n)))

class C3k2_IRA(C3k2):
def __init__(self, c1, c2, n=1, c3k=False, e=0.5, g=1, shortcut=True):
super().__init__(c1, c2, n, c3k, e, g, shortcut)
self.m = nn.ModuleList(C3k_IRA(self.c, self.c, 2, shortcut, g) if c3k else IRA(self.c, self.c) for _ in range(n))

注册

在ultralytics/nn/tasks.py中进行如下操作:

步骤1:

from ultralytics.nn.C3k2.C3k2_IRA import C3k2_IRA

步骤2

修改def parse_model(d, ch, verbose=True):

C3k2_IRA

image-20260812220414836

配置yolo11-C3k2_IRA.yaml

# Ultralytics YOLO 🚀, AGPL-3.0 license
# YOLO11 object detection model with P3-P5 outputs. For Usage examples see https://docs.ultralytics.com/tasks/detect

# 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] # summary: 319 layers, 2624080 parameters, 2624064 gradients, 6.6 GFLOPs
s: [0.50, 0.50, 1024] # summary: 319 layers, 9458752 parameters, 9458736 gradients, 21.7 GFLOPs
m: [0.50, 1.00, 512] # summary: 409 layers, 20114688 parameters, 20114672 gradients, 68.5 GFLOPs
l: [1.00, 1.00, 512] # summary: 631 layers, 25372160 parameters, 25372144 gradients, 87.6 GFLOPs
x: [1.00, 1.50, 512] # summary: 631 layers, 56966176 parameters, 56966160 gradients, 196.0 GFLOPs

# YOLO11n 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_IRA, [256, False, 0.25]]
[-1, 1, Conv, [256, 3, 2]] # 3-P3/8
[-1, 2, C3k2_IRA, [512, False, 0.25]]
[-1, 1, Conv, [512, 3, 2]] # 5-P4/16
[-1, 2, C3k2_IRA, [512, True]]
[-1, 1, Conv, [1024, 3, 2]] # 7-P5/32
[-1, 2, C3k2_IRA, [1024, True]]
[-1, 1, SPPF, [1024, 5]] # 9
[-1, 2, C2PSA, [1024]] # 10

# YOLO11n head
head:
[-1, 1, nn.Upsample, [None, 2, "nearest"]]
[[-1, 6], 1, Concat, [1]] # cat backbone P4
[-1, 2, C3k2_IRA, [512, False]] # 13

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

[-1, 1, Conv, [256, 3, 2]]
[[-1, 13], 1, Concat, [1]] # cat head P4
[-1, 2, C3k2_IRA, [512, False]] # 19 (P4/16-medium)

[-1, 1, Conv, [512, 3, 2]]
[[-1, 10], 1, Concat, [1]] # cat head P5
[-1, 2, C3k2_IRA, [1024, True]] # 22 (P5/32-large)

[[16, 19, 22], 1, Detect, [nc]] # Detect(P3, P4, P5)

实验

脚本

import warnings
warnings.filterwarnings('ignore')
from ultralytics import YOLO

if __name__ == '__main__':
# 修改为自己的配置文件地址
model = YOLO('./ultralytics/cfg/models/11/yolo11-C3k2_IRA.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='SGD',
amp=True,
project='runs/train',
name='C3k2_IRA',
)

结果

image-20260812220348321

赞(0)
未经允许不得转载:网硕互联帮助中心 » YOLO11改进 - C3k2融合 | IRA倒残差注意力模块,轻量融合卷积表征与通道空间注意,助力移动端多光谱颜色校正误差大幅降低 | CVPR 2026
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!