YOLOv5改进系列(3)——添加CA注意力机制

962f7cb1b48f44e29d9beb1d499d0530.gifYOLOv5改进系列】前期回顾:

YOLOv5改进系列(0)——重要性能指标与训练结果评价及分析

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

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

 


目录

 🚀一、CA注意力机制原理

1.1 SE和CBAM方法的不足

1.2 Coordinate Attention Block

1.2.1 Coordinate Information Embedding:Coordinate信息嵌入

1.2.2 Coordinate Attention Generation :Coordinate Attention生成

1.3 CA的优势

🚀二、添加CA注意力机制方法(单独加) 

2.1 添加顺序 

2.2 具体添加步骤 

第①步:在common.py中添加CA模块

第②步:在yolo.py文件里的parse_model函数加入类名

第③步:创建自定义的yaml文件 

第④步:验证是否加入成功

第⑤步:修改train.py中 ‘--cfg’默认参数

🚀三、添加C3_CA注意力机制方法(在C3模块中添加)

第①步:在common.py中添加CABottleneck和C3_CA模块

第②步:在yolo.py文件里的parse_model函数加入类名

第③步:创建自定义的yaml文件 

第④步:验证是否加入成功

第⑤步:修改train.py中 ‘--cfg’默认参数

YOLOv5%E7%B3%BB%E5%88%97%E5%AF%BC%E8%88%AA-toc" style="margin-left:0px;">🌟本人YOLOv5系列导航

 🚀一、CA注意力机制原理

论文题目:《Coordinate Attention for Efficient Mobile Network Design》

论文地址:https://arxiv.org/abs/2103.02907

代码实现: https://github.com/Andrew- Qibin/CoordAttention 

1.1 SE和CBAM方法的不足

  • SE(Squeeze-and-Excitation):SE注意力机制在编码过程中只考虑编码通道间信息而忽略了位置信息的重要性
  • CBAM(CBAM: Convolutional Block Attention Module):使用卷积计算空间注意力来利用位置信息,但卷积只能捕获局部关系,而无法捕捉的长距离依赖关系。

1.2 Coordinate Attention Block

Coordinate Attention通过精确的位置信息对通道关系和长期依赖性进行编码,具体操作分为 Coordinate信息嵌入 和 Coordinate Attention生成 2个步骤。


1.2.1 Coordinate Information Embedding:Coordinate信息嵌入

CxHxW 形状的输入特征图逐通道进行平均池化,使用(H,1)(1,W)的池化核分别按XY轴方向进行池化,产生 CxHx1 Cx1xW 形状的特征图。

通过这种方式所产生的一对方法感知特征图,可以使 CA 注意力能够在一个通道内捕获长距离的依赖关系,并且还有助于保留精确的位置信息,使网络能够更加准确的定位对象。


1.2.2 Coordinate Attention Generation :Coordinate Attention生成

(1)将上述所提取到的特征图按空间维度进行拼接,拼接成 \frac{C}{r}x1x(W+H) 形状的特征图,其中 r 用于控制块的减小率和SE中的作用相同。

(2)再将特征图经过F1卷积变换函数(1x1卷积)和非线性激活函数产生 f 中间特征图。然后将 f 按空间维度拆分成两个张量 fhfw ,形状分别为\frac{C}{r}xH 和 \frac{C}{r}xW,接着分别进行 FhFw 卷积变换函数(1x1卷积)和 Sigmoid 激活函数,得到 gh 和 gw 坐标注意力

(3)最后将gh gw 与原输入进行相乘,得到与输入相同形状的输出


1.3 CA的优势

  • 不仅考虑了通道信息,还考虑了方向相关的位置信息。
  • 足够的灵活和轻量,能够简单的插入到轻量级网络的核心模块中。 
  • 可以作为预训练模型用于多种任务中,如检测和分割,均有不错的性能提升。

🚀二、添加CA注意力机制方法(单独加) 

2.1 添加顺序 

(1)models/common.py    -->  加入新增的网络结构

(2)     models/yolo.py       -->  设定网络结构的传参细节,将CA(CoordAtt

)类名加入其中。(当新的自定义模块中存在输入输出维度时,要使用qw调整输出维度)
(3) models/yolov5*.yaml  -->  新建一个文件夹,如yolov5s_CA.yaml,修改现有模型结构配置文件。(当引入新的层时,要修改后续的结构中的from参数)
(4)         train.py                -->  修改‘--cfg’默认参数,训练时指定模型结构配置文件 


2.2 具体添加步骤 

第①步:在common.py中添加CA模块

将下面的CA代码复制粘贴到common.py文件的末尾

# CA
class h_sigmoid(nn.Module):
    def __init__(self, inplace=True):
        super(h_sigmoid, self).__init__()
        self.relu = nn.ReLU6(inplace=inplace)
    def forward(self, x):
        return self.relu(x + 3) / 6
class h_swish(nn.Module):
    def __init__(self, inplace=True):
        super(h_swish, self).__init__()
        self.sigmoid = h_sigmoid(inplace=inplace)
    def forward(self, x):
        return x * self.sigmoid(x)

class CoordAtt(nn.Module):
    def __init__(self, inp, oup, reduction=32):
        super(CoordAtt, self).__init__()
        self.pool_h = nn.AdaptiveAvgPool2d((None, 1))
        self.pool_w = nn.AdaptiveAvgPool2d((1, None))
        mip = max(8, inp // reduction)
        self.conv1 = nn.Conv2d(inp, mip, kernel_size=1, stride=1, padding=0)
        self.bn1 = nn.BatchNorm2d(mip)
        self.act = h_swish()
        self.conv_h = nn.Conv2d(mip, oup, kernel_size=1, stride=1, padding=0)
        self.conv_w = nn.Conv2d(mip, oup, kernel_size=1, stride=1, padding=0)
    def forward(self, x):
        identity = x
        n, c, h, w = x.size()
        #c*1*W
        x_h = self.pool_h(x)
        #c*H*1
        #C*1*h
        x_w = self.pool_w(x).permute(0, 1, 3, 2)
        y = torch.cat([x_h, x_w], dim=2)
        #C*1*(h+w)
        y = self.conv1(y)
        y = self.bn1(y)
        y = self.act(y)
        x_h, x_w = torch.split(y, [h, w], dim=2)
        x_w = x_w.permute(0, 1, 3, 2)
        a_h = self.conv_h(x_h).sigmoid()
        a_w = self.conv_w(x_w).sigmoid()
        out = identity * a_w * a_h
        return out

 如下图所示:


第②步:在yolo.py文件里的parse_model函数加入类名

首先找到yolo.py里面parse_model函数的这一行

然后把刚才加入的类CoordAtt添加到这个注册表里面 


第③步:创建自定义的yaml文件 

首先在models文件夹下复制yolov5s.yaml 文件,粘贴并重命名为 yolov5s_CA.yaml 

接着修改  yolov5s_CA.yaml  ,将CA模块加到我们想添加的位置。

注意力机制可以添加在backbone,Neck,Head等部分, 常见的有两种:一是在主干的 SPPF 前添加一层二是将Backbone中的C3全部替换

在这里我是用第一种:将 [-1,1,CA,[1024]]添加到 SPPF 的上一层,下一节使用第二种。即下图中所示位置:

同样的下面的head也得修改,p4,p5以及最后detect的总层数都得+1

  这里我们要把后面两个Concat的from系数分别由[ − 1 , 14 ] , [ − 1 , 10 ]改为[ − 1 , 15 ] , [ − 1 , 11 ]。然后将Detect原始的from系数[ 17 , 20 , 23 ]要改为[ 18 , 21 , 24 ] 。


 第④步:验证是否加入成功

yolo.py 文件里面配置改为我们刚才自定义的yolov5s_CA.yaml 

  然后运行yolo.py 

   找到CA这一层,就说明我们添加成功啦!


第⑤步:修改train.py中 ‘--cfg’默认参数

我们先找到 train.py 文件的parse_opt函数,然后将第二行‘--cfg’的 default改为'models/yolov5s_CA.yaml',然后就可以开始训练啦~


🚀三、添加C3_CA注意力机制方法(在C3模块中添加)

上面是单独加注意力层,接下来的方法是在C3模块中加入注意力层

刚才也提到了,这个策略是将CA注意力机制添加到Bottleneck,替换Backbone中的所有C3模块

(因为步骤和上面相同,所以接下来只放重要步骤噢~) 

第①步:在common.py中添加CABottleneck和C3_CA模块

将下面的代码复制粘贴到common.py文件的末尾

# CA
class h_sigmoid(nn.Module):
    def __init__(self, inplace=True):
        super(h_sigmoid, self).__init__()
        self.relu = nn.ReLU6(inplace=inplace)

    def forward(self, x):
        return self.relu(x + 3) / 6


class h_swish(nn.Module):
    def __init__(self, inplace=True):
        super(h_swish, self).__init__()
        self.sigmoid = h_sigmoid(inplace=inplace)

    def forward(self, x):
        return x * self.sigmoid(x)


class CABottleneck(nn.Module):
    # Standard bottleneck
    def __init__(self, c1, c2, shortcut=True, g=1, e=0.5, ratio=32):  # ch_in, ch_out, shortcut, groups, expansion
        super().__init__()
        c_ = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c_, c2, 3, 1, g=g)
        self.add = shortcut and c1 == c2
        # self.ca=CoordAtt(c1,c2,ratio)
        self.pool_h = nn.AdaptiveAvgPool2d((None, 1))
        self.pool_w = nn.AdaptiveAvgPool2d((1, None))
        mip = max(8, c1 // ratio)
        self.conv1 = nn.Conv2d(c1, mip, kernel_size=1, stride=1, padding=0)
        self.bn1 = nn.BatchNorm2d(mip)
        self.act = h_swish()
        self.conv_h = nn.Conv2d(mip, c2, kernel_size=1, stride=1, padding=0)
        self.conv_w = nn.Conv2d(mip, c2, kernel_size=1, stride=1, padding=0)

    def forward(self, x):
        x1 = self.cv2(self.cv1(x))
        n, c, h, w = x.size()
        # c*1*W
        x_h = self.pool_h(x1)
        # c*H*1
        # C*1*h
        x_w = self.pool_w(x1).permute(0, 1, 3, 2)
        y = torch.cat([x_h, x_w], dim=2)
        # C*1*(h+w)
        y = self.conv1(y)
        y = self.bn1(y)
        y = self.act(y)
        x_h, x_w = torch.split(y, [h, w], dim=2)
        x_w = x_w.permute(0, 1, 3, 2)
        a_h = self.conv_h(x_h).sigmoid()
        a_w = self.conv_w(x_w).sigmoid()
        out = x1 * a_w * a_h

        # out=self.ca(x1)*x1
        return x + out if self.add else out


class C3_CA(C3):
    # C3 module with CABottleneck()
    def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
        super().__init__(c1, c2, n, shortcut, g, e)
        c_ = int(c2 * e)  # hidden channels
        self.m = nn.Sequential(*(CABottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)))

第②步:在yolo.py文件里的parse_model函数加入类名

yolo.pyparse_model函数中,加入CABottleneckC3_CA两个模块


第③步:创建自定义的yaml文件 

按照上面的步骤创建yolov5s_C3_CA.yaml文件,替换4个C3模块

 代码如下:

# YOLOv5 🚀 by Ultralytics, GPL-3.0 license

# Parameters
nc: 80  # number of classes
depth_multiple: 0.33  # model depth multiple
width_multiple: 0.50  # layer channel multiple
anchors:
  - [10,13, 16,30, 33,23]  # P3/8
  - [30,61, 62,45, 59,119]  # P4/16
  - [116,90, 156,198, 373,326]  # P5/32

# YOLOv5 v6.0 backbone
backbone:
  # [from, number, module, args]
  [[-1, 1, Conv, [64, 6, 2, 2]],  # 0-P1/2
   [-1, 1, Conv, [128, 3, 2]],  # 1-P2/4
   [-1, 3, C3_CA, [128]],
   [-1, 1, Conv, [256, 3, 2]],  # 3-P3/8
   [-1, 6, C3_CA, [256]],
   [-1, 1, Conv, [512, 3, 2]],  # 5-P4/16
   [-1, 3, C3_CA, [512]],
   [-1, 1, Conv, [1024, 3, 2]],  # 7-P5/32
   [-1, 3, C3_CA, [1024]],
   [-1, 1, SPPF, [1024, 5]],  # 9
  ]

# YOLOv5 v6.0 head
head:
  [[-1, 1, Conv, [512, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 6], 1, Concat, [1]],  # cat backbone P4
   [-1, 3, C3, [512, False]],  # 13

   [-1, 1, Conv, [256, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 4], 1, Concat, [1]],  # cat backbone P3
   [-1, 3, C3, [256, False]],  # 17 (P3/8-small)

   [-1, 1, Conv, [256, 3, 2]],
   [[-1, 14], 1, Concat, [1]],  # cat head P4
   [-1, 3, C3, [512, False]],  # 20 (P4/16-medium)

   [-1, 1, Conv, [512, 3, 2]],
   [[-1, 10], 1, Concat, [1]],  # cat head P5
   [-1, 3, C3, [1024, False]],  # 23 (P5/32-large)

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

第④步:验证是否加入成功

yolo.py 文件里面配置改为我们刚才自定义的yolov5s_C3_CA.yaml,然后运行

这样就OK啦~


第⑤步:修改train.py中 ‘--cfg’默认参数

接下来的训练就和上面一样,不再叙述啦~

完结~撒花✿✿ヽ(°▽°)ノ✿


PS:今天训练的结果和比昨天CBAM的mAP增长了1.1,说明CA效果还是很不错的~


YOLOv5%E7%B3%BB%E5%88%97%E5%AF%BC%E8%88%AA">🌟本人YOLOv5系列导航

962f7cb1b48f44e29d9beb1d499d0530.gif​   🍀YOLOv5源码详解系列:  

YOLOv5源码逐行超详细注释与解读(1)——项目目录结构解析

​​​​​​YOLOv5源码逐行超详细注释与解读(2)——推理部分detect.py

YOLOv5源码逐行超详细注释与解读(3)——训练部分train.py

YOLOv5源码逐行超详细注释与解读(4)——验证部分val(test).py

YOLOv5源码逐行超详细注释与解读(5)——配置文件yolov5s.yaml

YOLOv5源码逐行超详细注释与解读(6)——网络结构(1)yolo.py

YOLOv5源码逐行超详细注释与解读(7)——网络结构(2)common.py

962f7cb1b48f44e29d9beb1d499d0530.gif​​   🍀YOLOv5入门实践系列:  

YOLOv5入门实践(1)——手把手带你环境配置搭建

YOLOv5入门实践(2)——手把手教你利用labelimg标注数据集

YOLOv5入门实践(3)——手把手教你划分自己的数据集

YOLOv5入门实践(4)——手把手教你训练自己的数据集

YOLOv5入门实践(5)——从零开始,手把手教你训练自己的目标检测模型(包含pyqt5界面) 


本文参考(感谢大佬们):

b站:【YOLOv5 v6.1添加SE,CA,CBAM,ECA注意力机制教学,即插即用】

CSDN: CVPR 2021 | 即插即用! CA:新注意力机制,助力分类/检测/分割涨点!_Amusi(CVer)的博客-CSDN博客

手把手带你YOLOv5 (v6.1)添加注意力机制(二)(在C3模块中加入注意力机制)_迪菲赫尔曼的博客-CSDN博客 


http://www.niftyadmin.cn/n/311924.html

相关文章

大数据 | (六)Hadoop集群启停脚本

知识目录 一、前言二、Hadoop集群启停脚本2.1 启停脚本及其背景2.2 使用方法 三、jps脚本四、关机脚本五、结语 一、前言 hello,大家好!这篇文章是我在使用Hadoop集群时使用到的启停脚本的详细内容与感想,希望能帮助到大家! 本篇…

BRC20懂Web3?探寻宗教式社区建设- Yuga Labs「猿」宇宙案例解析

前言 早前,NFT 领域最强IP 缔造者Yuga Labs官宣:将基于Ordinal 协议在比特币区块链上推出NFT 系列「TwelveFold 」,为比特币NFT 生态添加了催化剂。所以以太坊失宠了吗?事实上,据欧科云链OKLink多链浏览器数据显示&am…

OpenCV 直方图统计函数 cv::calcHist算是彻底弄明白了

参数说明 void calcHist( const Mat* images, int nimages,const int* channels, InputArray mask,OutputArray hist, int dims, const int* histSize,const float** ranges, bool uniform true, bool accumulate false );images 图像数组。每个图像的大小要一致&#xff0c…

【深度学习】第一门课 神经网络和深度学习 Week 2 神经网络基础

2.1 二元分类 前言 第二周的主题是学习神经网络的基础知识。 实现神经网络需要用到一些重要的技术和技巧,比如怎样处理包含大量样本的训练集。 在神经网络的计算中,还会有前向暂停、前向传播、反向暂停和反向传播等步骤,本周会对它们进行…

dom操作

<script>var htmlEldocument.documentElementvar bodyEldocument.bodyvar headEldocument.headvar doctypedocument.doctypeconsole.log(htmlEl,bodyEl,headEl,doctype)</script> ul>li{$}*5 节点之间的关系(极少用) 空行注释没标签包括的文字也算是一个节点 子…

lua实战(2)

目录 值和类型子类型类型字符串type (v) 值和类型 Lua是一种动态类型语言。这意味着变量没有类型;只有价值观才有意义。该语言中没有类型定义。所有值都有自己的类型。 Lua中的所有值都是一等值。这意味着所有的值都可以存储在变量中&#xff0c;作为参数传递给其他函数&…

Day02剑指offer-链表

视频地址&#xff1a;帅地玩编程 剑指 Offer 06. 从尾到头打印链表 输入一个链表的头节点&#xff0c;从尾到头反过来返回每个节点的值&#xff08;用数组返回&#xff09;。 概念 .val()方法 .val()无参数&#xff0c;获取匹配的元素集合中第一个元素的当前值.val( value…

IS200TREGH1BEC直流并励电动机的启动

​ IS200TREGH1BEC​​​​​​直流并励电动机的启动 要启动机器&#xff0c;电机在零速时产生的扭矩必须超过负载所需的扭矩。然后 TM − TL 将为正&#xff0c;d!/dt 也为正&#xff0c;机器加速。 通常&#xff0c;直流电机的电枢电阻在满载电流时会导致 1% 到 5% 的下降。…