YOLO11 改进 – 注意力机制 _ CAFM (Convolutional Block Attention Module) 卷积块注意力模块:轻量级设计优化特征提取流程,提升小目标感知

前言

本文介绍了用于高光谱图像去噪的HCANet模型及其在YOLOv11中的结合应用。HCANet结合了卷积神经网络和Transformer的优势,通过设计卷积和注意力融合模块(CAFM)与多尺度前馈网络(MSFN),实现全局和局部特征的综合建模与多尺度信息聚合。CAFM模块中,局部分支提取局部特征,全局分支捕获长距离依赖关系,二者融合提升去噪性能;MSFN模块利用不同步长的扩张卷积在多尺度提取特征。我们将CAFMAttention集成进YOLOv11,实验表明能有效改善模型噪效果。

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

专栏链接: YOLOv11改进专栏

介绍

摘要

高光谱图像(HSI)去噪技术对于高光谱数据的精确分析与有效解释具有至关重要的意义,然而当前研究在同时建模全局与局部特征以增强HSI去噪性能方面仍存在明显不足。针对此问题,本文提出了一种混合卷积与注意力网络(HCANet),该网络创新性地融合了卷积神经网络(CNN)与Transformers架构的双重优势。为强化全局与局部特征的协同建模,我们设计了一个卷积与注意力融合模块,专门用于捕获长距离依赖关系及邻域光谱相关性特征。此外,为进一步提升多尺度信息聚合能力,我们构建了一个多尺度前馈网络,通过在多个尺度层次上提取特征来显著增强去噪性能。在多个主流HSI数据集上进行的系统性实验验证表明,所提出的HCANet模型在架构设计上具有充分的合理性与实际有效性。实验结果显示,该模型在处理各类复杂噪声模式时均展现出卓越的去噪性能。相关实现代码已公开于https://github.com/summitgao/HCANet平台,以供学术研究参考与进一步验证。

文章链接

论文地址: 论文地址

代码地址: 代码地址

基本原理

这篇文章介绍了一种名为Hybrid Convolutional and Attention Network (HCANet)的模型,用于高光谱图像去噪。该模型结合了卷积神经网络和Transformer的优势,以有效地去除高光谱图像中的噪声。文章提出了注意力机制,用于捕获远程依赖性和邻域光谱相关性,以增强全局和局部特征建模。通过设计卷积和注意力融合模块以及多尺度前馈网络,该模型能够在不同尺度提取特征,从而提高去噪性能。

  1. 结构概述 :HCANet采用了U型网络结构,其中包含多个Convolution Attention Mixing(CAMixing)块。每个CAMixing块由两部分组成:卷积和注意力融合模块(CAFM)以及多尺度前馈网络(MSFN)。

  2. CAFM模块 :在CAFM模块中,局部分支利用卷积和通道重排来提取局部特征,全局分支则利用注意力机制来捕获长距离依赖关系。这种结合了卷积和注意力的设计使得模型能够综合建模全局和局部特征,从而提高去噪性能。

  3. MSFN模块 :MSFN模块用于多尺度信息聚合,通过三个并行的具有不同步长的扩张卷积来实现。这有助于在不同尺度提取特征,有效地抑制多尺度的噪声。

  4. 训练过程 :HCANet首先使用3x3x3卷积提取低级特征,然后通过U型网络结构和跳跃连接来生成噪声残差图。最终,通过重建损失和全局梯度正则化器来训练模型,以实现高光谱图像的去噪。

通过结合CAFM模块和MSFN模块,HCANet能够有效地利用卷积和注意力机制,同时在不同尺度提取特征,从而提高高光谱图像去噪的性能和效果。

CAFM

CAFM是指卷积和注意力融合模块(Convolution and Attention Fusion Module),在HCANet模型中起着关键作用。该模块包括局部分支和全局分支,用于融合卷积和注意力机制以捕获全局和局部特征。以下是关于CAFM的详细介绍:

  1. 局部分支 :局部分支旨在提取局部特征,通过卷积和通道重排来实现。这一部分专注于在高光谱图像中提取局部信息,以帮助全局和局部特征的综合建模。

  2. 全局分支 :全局分支利用注意力机制来建模长距离特征依赖关系。通过注意力机制,模型能够捕获更广泛的高光谱数据信息,从而更好地理解全局特征。

  3. 融合操作 :在CAFM模块中,局部分支和全局分支的特征经过融合操作,通常是通过加法操作来融合两者的特征表示。这种融合操作能够有效地结合局部和全局信息,提高模型对高光谱图像的理解能力和去噪效果。

核心代码

import sys
import torch
import torch.nn as nn
import torch.nn.functional as F
from pdb import set_trace as stx
import numbers
from einops import rearrange
import os
sys.path.append(os.getcwd())

# 将四维张量转换为三维张量
def to_3d(x):
    return rearrange(x, 'b c h w -> b (h w) c')

# 将三维张量转换为四维张量
def to_4d(x, h, w):
    return rearrange(x, 'b (h w) c -> b c h w', h=h, w=w)

# 无偏置的层归一化
class BiasFree_LayerNorm(nn.Module):
    def __init__(self, normalized_shape):
        super(BiasFree_LayerNorm, self).__init__()
        if isinstance(normalized_shape, numbers.Integral):
            normalized_shape = (normalized_shape,)
        normalized_shape = torch.Size(normalized_shape)

        assert len(normalized_shape) == 1

        self.weight = nn.Parameter(torch.ones(normalized_shape))
        self.normalized_shape = normalized_shape

    def forward(self, x):
        sigma = x.var(-1, keepdim=True, unbiased=False)
        return x / torch.sqrt(sigma + 1e-5) * self.weight

# 有偏置的层归一化
class WithBias_LayerNorm(nn.Module):
    def __init__(self, normalized_shape):
        super(WithBias_LayerNorm, self).__init__()
        if isinstance(normalized_shape, numbers.Integral):
            normalized_shape = (normalized_shape,)
        normalized_shape = torch.Size(normalized_shape)

        assert len(normalized_shape) == 1

        self.weight = nn.Parameter(torch.ones(normalized_shape))
        self.bias = nn.Parameter(torch.zeros(normalized_shape))
        self.normalized_shape = normalized_shape

    def forward(self, x):
        mu = x.mean(-1, keepdim=True)
        sigma = x.var(-1, keepdim=True, unbiased=False)
        return (x - mu) / torch.sqrt(sigma + 1e-5) * self.weight + self.bias

# 层归一化选择
class LayerNorm(nn.Module):
    def __init__(self, dim, LayerNorm_type):
        super(LayerNorm, self).__init__()
        if LayerNorm_type == 'BiasFree':
            self.body = BiasFree_LayerNorm(dim)
        else:
            self.body = WithBias_LayerNorm(dim)

    def forward(self, x):
        h, w = x.shape[-2:]
        return to_4d(self.body(to_3d(x)), h, w)

## 卷积和注意力融合模块 (CAFM)
class CAFMAttention(nn.Module):
    def __init__(self, dim, num_heads, bias):
        super(Attention, self).__init__()
        self.num_heads = num_heads
        self.temperature = nn.Parameter(torch.ones(num_heads, 1, 1))

        self.qkv = nn.Conv3d(dim, dim * 3, kernel_size=(1, 1, 1), bias=bias)
        self.qkv_dwconv = nn.Conv3d(dim * 3, dim * 3, kernel_size=(3, 3, 3), stride=1, padding=1, groups=dim * 3, bias=bias)
        self.project_out = nn.Conv3d(dim, dim, kernel_size=(1, 1, 1), bias=bias)
        self.fc = nn.Conv3d(3 * self.num_heads, 9, kernel_size=(1, 1, 1), bias=True)

        self.dep_conv = nn.Conv3d(9 * dim // self.num_heads, dim, kernel_size=(3, 3, 3), bias=True, groups=dim // self.num_heads, padding=1)

    def forward(self, x):
        b, c, h, w = x.shape
        x = x.unsqueeze(2)
        qkv = self.qkv_dwconv(self.qkv(x))
        qkv = qkv.squeeze(2)
        f_conv = qkv.permute(0, 2, 3, 1)
        f_all = qkv.reshape(f_conv.shape[0], h * w, 3 * self.num_heads, -1).permute(0, 2, 1, 3)
        f_all = self.fc(f_all.unsqueeze(2))
        f_all = f_all.squeeze(2)

        # 局部卷积
        f_conv = f_all.permute(0, 3, 1, 2).reshape(x.shape[0], 9 * x.shape[1] // self.num_heads, h, w)
        f_conv = f_conv.unsqueeze(2)
        out_conv = self.dep_conv(f_conv)
        out_conv = out_conv.squeeze(2)

        # 全局自注意力
        q, k, v = qkv.chunk(3, dim=1)

        q = rearrange(q, 'b (head c) h w -> b head c (h w)', head=self.num_heads)
        k = rearrange(k, 'b (head c) h w -> b head c (h w)', head=self.num_heads)
        v = rearrange(v, 'b (head c) h w -> b head c (h w)', head=self.num_heads)

        q = torch.nn.functional.normalize(q, dim=-1)
        k = torch.nn.functional.normalize(k, dim=-1)

        attn = (q @ k.transpose(-2, -1)) * self.temperature
        attn = attn.softmax(dim=-1)

        out = (attn @ v)

        out = rearrange(out, 'b head c (h w) -> b (head c) h w', head=self.num_heads, h=h, w=w)
        out = out.unsqueeze(2)
        out = self.project_out(out)
        out = out.squeeze(2)
        output = out + out_conv

        return output

实验

脚本

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

if __name__ == '__main__':
#     修改为自己的配置文件地址
    model = YOLO('/root/ultralytics-main/ultralytics/cfg/models/11/yolov11-CAFMAttention.yaml')
#     修改为自己的数据集地址
    model.train(data='/root/ultralytics-main/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='CAFMAttention',
                )
THE END