YOLOv5改进系列(1)——添加CBAM注意力机制

news2024/11/19 16:49:54

一、如何理解注意力机制

假设你正在阅读一本书,同时有人在你旁边说话。当你听到某些关键字时,比如“你的名字”或者“你感兴趣的话题”,你会自动把注意力从书上转移到他们的谈话上,尽管你并没有完全忽略书本的内容。这就是注意力机制的核心思想:动态地根据重要性来分配注意力,而不是对输入的信息一视同仁地处理。

在计算机视觉或自然语言处理(NLP)中,注意力机制让模型能够灵活地聚焦于输入数据的某些重要部分。例如,在图像分类任务中,模型不需要对整张图像的每一个像素都一视同仁,而是可以专注于那些关键区域,如目标物体的边缘或特征。在句子处理时,模型可以根据句子的上下文,专注于那些对语义理解最为关键的单词或短语,而忽略不太重要的部分。

注意力机制就像在复杂信息处理中自动筛选和重点关注的过程,帮助模型更智能地选择和处理最有用的信息。


作用

  1. 提升准确性:注意力机制聚焦关键信息,提升预测精度。
  2. 增强可解释性:能更清晰展示模型决策过程。
  3. 处理变长数据:适用于文本、语音等不定长序列数据。

不足

  1. 计算开销大:需要计算每个位置的权重,耗时长。
  2. 易过拟合:复杂权重可能导致模型在训练集上表现过好,泛化能力弱。
  3. 数据需求高:需要大量数据训练,否则效果不佳。

二、CBAM注意力机制

论文名称:《CBAM: Convolutional Block Attention Module》

论文地址:https://arxiv.org/pdf/1807.06521

论文代码:GitHub - Jongchan/attention-module: Official PyTorch code for "BAM: Bottleneck Attention Module (BMVC2018)" and "CBAM: Convolutional Block Attention Module (ECCV2018)"

CBAM从通道channel和空间spatial两个作用域出发,实现从通道到空间的顺序注意力结构。空间注意力可使神经网络更加关注在图像分类中决定性作用的像素区域而忽略无关紧要的区域,通道注意力则用于处理特征图通道的分配关系,同时对两个维度进行注意力分配加强了注意力机制对模型性能的提升效果。

2.1 CAM通道注意力模块

shared MLP

  1. 输入特征图(H×W×C)

    • 先将输入的特征图(H×W×C)分别经过基于宽度和高度的最大池化和平均池化,对特征图按两个维度压缩,得到两个1×1×C的特征图。
  2. 池化后的特征图进行处理

    • 将最大池化和平均池化的结果利用共享的全连接层(Shared MLP)进行处理:
      • 先通过一个全连接层下降通道数(C -> C/4)。
      • 然后再通过另一个全连接层恢复通道数(C/4 -> C)。
  3. 生成权重

    • 将共享的全连接层所得到的结果相加后再使用Sigmoid激活函数,生成最终的channel attention feature,得到每个通道的权重值(0~1之间)。
  4. 特征调整

    • 将权重通过逐通道相加到输入特征图上,生成最终调整后的特征图。

代码如下所示:

import torch
import torch.nn as nn

class ChannelAttentionModule(nn.Module):
    def __init__(self, in_channels, reduction=4):
        super(ChannelAttentionModule, self).__init__()
        # 使用最大池化和平均池化
        self.avg_pool = nn.AdaptiveAvgPool2d(1)  # 输出形状 1x1
        self.max_pool = nn.AdaptiveMaxPool2d(1)  # 输出形状 1x1
        
        # 全连接层用于减少通道数再增加回来
        self.fc1 = nn.Conv2d(in_channels, in_channels // reduction, 1, bias=False)  # 1x1卷积,降维
        self.relu = nn.ReLU()  # 激活函数ReLU
        self.fc2 = nn.Conv2d(in_channels // reduction, in_channels, 1, bias=False)  # 1x1卷积,升维
        
        # 使用 Sigmoid 激活函数
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        # 输入特征图的形状为 (B, C, H, W)
        avg_out = self.avg_pool(x)  # 平均池化
        max_out = self.max_pool(x)  # 最大池化
        
        # 使用共享的全连接层(MLP)进行处理
        avg_out = self.fc2(self.relu(self.fc1(avg_out)))
        max_out = self.fc2(self.relu(self.fc1(max_out)))
        
        # 将池化结果加起来
        out = avg_out + max_out
        
        # 使用Sigmoid激活
        out = self.sigmoid(out)
        
        # 通过逐通道相乘的方式调整输入特征图
        return x * out

# 测试模块
if __name__ == "__main__":
    # 输入张量 (batch_size, channels, height, width)
    input_tensor = torch.randn(1, 64, 32, 32)  # 示例输入 (B=1, C=64, H=32, W=32)
    
    # 实例化通道注意力模块
    cam = ChannelAttentionModule(in_channels=64)
    
    # 前向传播
    output = cam(input_tensor)
    
    print(output.shape)  # 输出特征图的形状

2.2 SAM空间注意力模块

具体流程如下:

将上面CAM模块输出的特征图F'作为本模块的输入特征图

首先,对输入特征图在通道维度下做最大池化和平均池化,将池化后的两张特征图在通道维度堆叠(concat)。

然后,经过一个7×7卷积(7×7比3×3效果更好)操作,降维为1个channel,即叠积核融合通道信息,特征图的shape从b,2,h,w - >b,1,h,w。

最后,将卷积后的结果经过sigmoid函数对特征图的空间权重归一化,再将输入特征图和权重相乘。

class spatial_attention(nn.Module):
    def __init__(self, kernel_size=7):
        super(spatial_attention, self).__init__()

        # 为了保持卷积前后的特征图shape相同,卷积时需要padding
        padding = kernel_size // 2  # 确保7x7卷积后输出形状与输入一致

        # 7×7卷积融合通道信息,输入[b, 2, h, w],输出[b, 1, h, w]
        self.conv = nn.Conv2d(in_channels=2, out_channels=1, kernel_size=kernel_size, padding=padding, bias=False)

        # sigmoid激活函数
        self.sigmoid = nn.Sigmoid()

    def forward(self, inputs):
        # 在通道维度上最大池化 [b, 1, h, w],keepdim保留原有深度
        x_maxpool, _ = torch.max(inputs, dim=1, keepdim=True)

        # 在通道维度上平均池化 [b, 1, h, w]
        x_avgpool = torch.mean(inputs, dim=1, keepdim=True)

        # 池化后的结果在通道维度上堆叠 [b, 2, h, w]
        x = torch.cat([x_maxpool, x_avgpool], dim=1)

        # 卷积融合通道信息, [b, 2, h, w] ==> [b, 1, h, w]
        x = self.conv(x)

        # 空间权重归一化
        x = self.sigmoid(x)

        # 输入特征图和空间权重相乘
        outputs = inputs * x

        return outputs
  1. 输入特征图:假设输入一个中间特征图 F∈RC×H×W,其中 C、H、W 分别表示通道数、高度和宽度。

  2. 通道注意力模块

    • 对输入特征图 F 进行全局最大池化(Max Pooling)和全局平均池化(Average Pooling)操作,生成两个不同的空间信息描述。即通过最大池化和平均池化将特征图 F 沿空间维度压缩为1维向量。
    • 将池化后的结果通过一个共享的多层感知机(MLP),以融合不同空间信息并输出通道维度的权重 Mc​(F)。
    • 最后通过公式 (3) 计算通道注意力:

  1. 空间注意力模块
    • 将通道加权后的特征图 F′ 进一步经过空间注意力机制。首先,再次对特征图 F′ 进行全局最大池化和全局平均池化操作,但这次是在通道维度进行操作。
    • 将池化结果拼接后,经过一个卷积操作生成空间注意力图 Ms​(F′),公式 (4) 表示为:

  • 最终将空间注意力图 Ms​(F′) 与特征图 F′ 按元素相乘,得到最终的输出特征图 F′′。
  1. CBAM模块的完整过程:
    • 通道注意力模块和空间注意力模块可以串联使用,先通过通道注意力调整特征,再通过空间注意力调整,完成特征图的两次加权。

三、CBAM注意力机制添加过程

1.在common.py中添加网络结构


将下面代码复制到common.py文件最下面

class ChannelAttentionModule(nn.Module):
    def __init__(self, in_channels, reduction=4):
        super(ChannelAttentionModule, self).__init__()
        # 使用最大池化和平均池化
        self.avg_pool = nn.AdaptiveAvgPool2d(1)  # 输出形状 1x1
        self.max_pool = nn.AdaptiveMaxPool2d(1)  # 输出形状 1x1

        # 全连接层用于减少通道数再增加回来
        self.fc1 = nn.Conv2d(in_channels, in_channels // reduction, 1, bias=False)  # 1x1卷积,降维
        self.relu = nn.ReLU()  # 激活函数ReLU
        self.fc2 = nn.Conv2d(in_channels // reduction, in_channels, 1, bias=False)  # 1x1卷积,升维

        # 使用 Sigmoid 激活函数
        self.sigmoid = nn.Sigmoid()
    def forward(self, x):
        b, c, h, w = x.size()  # 获取输入的形状 (batch_size, channels, height, width)

        # 最大池化和平均池化,输出维度 [b, c, 1, 1]
        max_pool = self.max_pool(x).view(b, c)  # 调整池化结果维度为 [b, c]
        avg_pool = self.avg_pool(x).view(b, c)  # 调整池化结果维度为 [b, c]

        # 第一个全连接层降通道数 [b, c] => [b, c/4]
        x_maxpool = self.fc1(max_pool)
        x_avgpool = self.fc1(avg_pool)

        # 激活函数
        x_maxpool = self.relu(x_maxpool)
        x_avgpool = self.relu(x_avgpool)

        # 第二个全连接层恢复通道数 [b, c/4] => [b, c]
        x_maxpool = self.fc2(x_maxpool)
        x_avgpool = self.fc2(x_avgpool)

        # 将最大池化和平均池化的结果相加 [b, c]
        x = x_maxpool + x_avgpool

        # Sigmoid函数权重归一化
        x = self.sigmoid(x)

        # 调整维度 [b, c] => [b, c, 1, 1]
        x = x.view(b, c, 1, 1)

        # 输入特征图和通道权重相乘 [b, c, h, w]
        outputs = x * x

        return outputs
    # def forward(self, x):
    #     # 输入特征图的形状为 (B, C, H, W)
    #     b,c,h,w = x.shape
    #     max_out = self.max_pool(x)  # 最大池化
    #     avg_out = self.avg_pool(x)  # 平均池化
    #
    #     # 使用共享的全连接层(MLP)进行处理
    #     avg_out = self.fc2(self.relu(self.fc1(avg_out)))
    #     max_out = self.fc2(self.relu(self.fc1(max_out)))
    #
    #     # 将池化结果加起来
    #     out = avg_out + max_out
    #
    #     # 使用Sigmoid激活
    #     out = self.sigmoid(out)
    #
    #     # 通过逐通道相乘的方式调整输入特征图
    #     return x * out


class spatial_attention(nn.Module):
    def __init__(self, kernel_size=7):
        super(spatial_attention, self).__init__()

        # 为了保持卷积前后的特征图shape相同,卷积时需要padding
        padding = kernel_size // 2  # 确保7x7卷积后输出形状与输入一致

        # 7×7卷积融合通道信息,输入[b, 2, h, w],输出[b, 1, h, w]
        self.conv = nn.Conv2d(in_channels=2, out_channels=1, kernel_size=kernel_size, padding=padding, bias=False)

        # sigmoid激活函数
        self.sigmoid = nn.Sigmoid()

    def forward(self, inputs):
        # 在通道维度上最大池化 [b, 1, h, w],keepdim保留原有深度
        x_maxpool, _ = torch.max(inputs, dim=1, keepdim=True)

        # 在通道维度上平均池化 [b, 1, h, w]
        x_avgpool = torch.mean(inputs, dim=1, keepdim=True)

        # 池化后的结果在通道维度上堆叠 [b, 2, h, w]
        x = torch.cat([x_maxpool, x_avgpool], dim=1)

        # 卷积融合通道信息, [b, 2, h, w] ==> [b, 1, h, w]
        x = self.conv(x)

        # 空间权重归一化
        x = self.sigmoid(x)

        # 输入特征图和空间权重相乘
        outputs = inputs * x

        return outputs


class CBAM(nn.Module):
    def __init__(self, c1, c2, ratio=16, kernel_size=7):  # ch_in
        super(CBAM, self).__init__()
        self.channel_attention = ChannelAttentionModule(c1, ratio)
        self.spatial_attention = spatial_attention(kernel_size)

    def forward(self, x):
        out = self.channel_attention(x) * x  # 通道注意力加权
        # c*h*w (通道数、高度、宽度)
        out = self.spatial_attention(out) * out  # 空间注意力加权
        # c*h*w * 1*h*w(空间维度权重)
        return out

2.在yolo.py中添加CBAM结构

在下面找到 def parse_model(d, ch): 函数,往下找到if m in 这一行 添加CBAM

3.在yolov5s_CBAM.yaml中添加CBAM结构

yolov5s_CBAM.yaml 文件中添加 CBAM 模块。具体来说,有两种常见的添加方式:

  1. 在主干(backbone)的 SPPF(Spatial Pyramid Pooling-Fast)层之前添加一层 CBAM 模块。
  2. 将 Backbone 中所有的 C3 层全部替换为 CBAM 模块。

4.在yolo.py中修改yolov5s.yaml为yolov5s_CBAM.yaml

5.添加成功结果展示

6.修改train下的parse_opt开始训练

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2183740.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

docker零基础入门教程

注意 本系列文章已升级、转移至我的自建站点中,本章原文为:Docker入门 目录 注意1.前言2.docker安装3.docker基本使用4.打包docker镜像5.docker进阶 1.前言 如果你长期写C/C代码,那你应该很容易发现C/C开源项目存在的一个严重问题&#xff…

Xshell-8下载安装教程

下载地址 https://www.xshell.com/zh/free-for-home-school/ 新建Xshell文件夹 点击安装程序 选择新建Xshell文件夹 默认即可 点击安装 注册 提交后点击邮箱收到的链接 点击确认 安装完成

【C/C++】错题记录(三)

题目一 题目二 题目三 题目四 题目五 题目六 题目七??? 题目八 这道题主要考查对数据类型和位运算的理解与运用。 分析选项 A: *((unsigned char *)(&number) 1)0xcd; 这里将 number 的地址强制转换为 unsigned char* 类型&a…

Qt界面优化——QSS

文章目录 QSS基本语法使用示例样式和代码分离选择器用法子控件选择器伪类选择器盒子模型控件样式示例按钮复选框输入框列表框菜单 登录界面 QSS基本语法 Qt对界面进行优化,主要采用的是QSS。 选择器 {属性名: 属性值; }选择器:先选择某个/类控件&#…

【JVM】双亲委派机制打破双亲委派机制

双亲委派机制 类加载器的双亲委派机制 由于Java虚拟机中有多个类加载器,双亲委派机制的核心是解决一个类到底由谁加载的问题。 双亲委派的作用: 保证类加载的安全性:通过双亲委派机制避免恶意代码替换 JDK中的核心类库。避免重复加载&…

算法题总结(五)——普通数组

普通数组 #238、除自身以外数组的乘积 给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要…

JDBC相关内容

第1章:JDBC概述 1.1 数据的持久化 持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用。大多数情况下,特别是企业级应用,数据持久化意味着将内存中的数据保存到硬盘上加以”固化”,而持久化的实现过程大…

进程--消息队列和共享内存

目录 消息队列 创建消息队列 删除消息队列 发送消息和接收 消息队列 消息队列就是一个消息的列表,进程可以在消息队列中添加消息和的读取消息 消息队列具有FIFO的特性,具有无名管道与有名管道各自的优势,可以支持任意两个进程的进程间通讯…

【python实操】python小程序之随机抽签以及for循环计算0-x的和

引言 python小程序之随机抽签以及for循环计算0-x的和 文章目录 引言一、随机抽签1.1 题目1.2 代码1.3 代码解释 二、for循环计算0-x的和2.1 题目2.2 代码2.3 代码解释 三、思考3.1 随机抽签3.2 for循环计算0-x的和 一、随机抽签 1.1 题目 使用input输入五个同学的名字随机抽取…

对于基础汇编的趣味认识

汇编语言 机器指令 机器语言是机器指令的集合 机器指令展开来讲就是一台机器可以正确执行的命令 电子计算机的机器指令是一列二进制数字 (计算机将其转变为一列高低电平,使得计算机的电子器件受到驱动,进行运算 寄存器:微处理器…

zabbix7.0创建自定义模板的案例详解(以监控httpd服务为例)

前言 服务端配置 链接: rocky9.2部署zabbix服务端的详细过程 环境 主机ip应用zabbix-server192.168.10.11zabbix本体zabbix-client192.168.10.12zabbix-agent zabbix-server(服务端已配置) 创建模板 模板组直接写一个新的,不用选择 通过名称查找模板&#xf…

动手学深度学习(李沐)PyTorch 第 6 章 卷积神经网络

李宏毅-卷积神经网络CNN 如果使用全连接层:第一层的weight就有3*10^7个 观察 1:检测模式不需要整张图像 很多重要的pattern只要看小范围即可 简化1:感受野 根据观察1 可以做第1个简化,卷积神经网络会设定一个区域&#xff0c…

wenyan:markdown 一键转换文章排版

介绍 今天给大家介绍一个markdown排版成自媒体文章的工具。 markdown 的重要性和便捷性不用再多说,但是从markdown 转换到文章排版,我换了很多个也都很不满意,尤其在不支持markdown的平台,更是一言难尽。 本次介绍的wenyan的核心…

JDBC进阶

目录 JDBC进阶 实体类和ORM 主键回显 批量操作 连接池 介绍 常见的连接池 Druid连接池 Hikari连接池 连接池与软编码 其他配置 Druid配置 Hikari配置 JDBC进阶 实体类和ORM 在使用JDBC操作数据库时会发现数据都是零散的,明明在数据库中是一行完整的数…

【四】Spring Cloud OpenFeign原理分析

Spring Cloud OpenFeign原理分析 概述 Spring Cloud 微服务实践也有挺多年了,一直想着总结一下这系列的知识点,最近终于下定决心来出一个Spring Cloud 系列文章了。本文主要围绕fegin组件来进行讲解,文中将会给出基础使用的示例,还…

0x10 用友 畅捷通T+ RecoverPassword.aspx 管理员密码修改漏洞

参考: 用友 畅捷通T RecoverPassword.aspx 管理员密码修改漏洞 | PeiQi文库 (wgpsec.org) 免责声明 欢迎访问我的博客。以下内容仅供教育和信息用途: 合法性:我不支持或鼓励非法活动。请确保遵守法律法规。信息准确性:尽管我尽…

全站最详细的Python环境配置步骤

1、官网下载IDE JetBrains下载 2、IDE下载、安装步骤 这里展示的是如何在Windows上下载、安装Pycharm工具,Linux的步骤类似。 2.1、选择开发者工具 选择开发者工具 2.2、选择Pycharm 选择Pycharm 2.3、选择下载 选择下载 2.4、选择社区版 一般而言&#xff…

【STM32-HAL库】AHT10温湿度传感器使用(STM32F407ZGT6配置i2c)(附带工程下载连接)

一、温湿度传感器: 温湿度传感器是一种能够检测环境中的温度和湿度,并将其转化为电信号输出的装置。它在智能家居、工业自动化、气象监测、农业等领域有着广泛的应用。 原理: 温湿度传感器通常基于不同的物理原理,以下是一些常见…

补:vs调试技巧!

目录 1>>闲话 2>>bug 3>>debug调式 4>>debug和release 5>>监视和内存观察 6>>总结 1>>闲话 数据结构章节在国庆后再给大家更喔,现在先把c基础打好,所以我又重返回去学习c语言,并且&#xff0…

17 vue3之tsx手写vite tsx插件

我们之前呢是使用Template去写我们模板。现在可以扩展另一种风格TSX风格 vue2 的时候就已经支持jsx写法,只不过不是很友好,随着vue3对typescript的支持度,tsx写法越来越被接受,减少我们学习react的成本 Ant Design组件库就是使用…