学习资源站

05-添加ECA注意力机制_yolov5 添加eca注意力

YOLOv5改进系列(4)——添加ECA注意力机制


🚀一、ECA注意力机制原理 

论文题目:《ECA-Net: Efficient Channel Attention for Deep Convolutional Neural Networks

原文地址:ECA-Net

代码实现:ECA-Net: Efficient Channel Attention for Deep Convolutional Neural Networks 开源代码GitHub - BangguWu/ECANet: Code for ECA-Net: Efficient Channel Attention for Deep Convolutional Neural NetworksECA-Net: Efficient Channel Attention for Deep Convolutional Neural Networks 开源代码

1.1 ECA方法介绍 

ECA是通道注意力机制的一种实现形式,是基于SE的扩展。

作者认为SE block的两个FC层之间的降维是不利于channel attention的权重学习的,并且捕获所有通道之间的依存关系是效率不高且是不必要的。权重学习的过程应该直接一一对应。

ECA 注意力机制模块直接在全局平均池化层之后使用1x1卷积层,去除了全连接层。该模块避免了维度缩减,并有效捕获了跨通道交互。并且ECA只涉及少数参数就能达到很好的效果

ECA通过一维卷积 layers.Conv1D 来完成跨通道间的信息交互,卷积核的大小通过一个函数来自适应变化,使得通道数较大的层可以更多地进行跨通道交互。

自适应函数为: ,其中 \gamma =2,\,\,b=1


1.2 SE和ECA网络结构的对比

SEblock网络结构ECA模块网络结构
(1)global avg pooling产生1 ∗ 1 ∗ C 大小的feature maps(1)global avg pooling产生1 ∗ 1 ∗ C 大小的feature maps
(2)两个fc层(中间有维度缩减)来产生每个channel的weight(2)计算得到自适应的kernel_size
(3)应用kernel_size于一维卷积中,得到每个channel的weight

1.3 ECA实现过程

(1)将输入特征图经过全局平均池化,特征图从 [h,w,c] 的矩阵变成 [1,1,c] 的向量

(2)根据特征图的通道数计算得到自适应的一维卷积核大小 kernel_size

(3)将 kernel_size 用于一维卷积中,得到对于特征图的每个通道的权重

(4)将归一化权重原输入特征图逐通道相乘,生成加权后的特征图


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

2.1 添加顺序 

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

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


2.2 具体添加步骤 

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

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

  1. class ECA(nn.Module):
  2. """Constructs a ECA module.
  3. Args:
  4. channel: Number of channels of the input feature map
  5. k_size: Adaptive selection of kernel size
  6. """
  7. def __init__(self, c1,c2, k_size=3):
  8. super(ECA, self).__init__()
  9. self.avg_pool = nn.AdaptiveAvgPool2d(1)
  10. self.conv = nn.Conv1d(1, 1, kernel_size=k_size, padding=(k_size - 1) // 2, bias=False)
  11. self.sigmoid = nn.Sigmoid()
  12. def forward(self, x):
  13. # feature descriptor on the global spatial information
  14. y = self.avg_pool(x)
  15. y = self.conv(y.squeeze(-1).transpose(-1, -2)).transpose(-1, -2).unsqueeze(-1)
  16. # Multi-scale information fusion
  17. y = self.sigmoid(y)
  18. return x * y.expand_as(x)

如下图所示:


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

首先找到yolo.pyparse_model函数的这一行

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


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

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

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

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

在这里我是用第一种:将 [-1,1,ECA,[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_ECA.yaml 

 然后运行yolo.py 

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

可以看到params参数这里只有3,说明参数量真的很少呀。


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

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


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

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

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

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

第①步:在common.py中添加ECABottleneck和C3_ECA模块

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

  1. class ECABottleneck(nn.Module):
  2. # Standard bottleneck
  3. def __init__(self, c1, c2, shortcut=True, g=1, e=0.5, ratio=16, k_size=3): # ch_in, ch_out, shortcut, groups, expansion
  4. super().__init__()
  5. c_ = int(c2 * e) # hidden channels
  6. self.cv1 = Conv(c1, c_, 1, 1)
  7. self.cv2 = Conv(c_, c2, 3, 1, g=g)
  8. self.add = shortcut and c1 == c2
  9. # self.eca=ECA(c1,c2)
  10. self.avg_pool = nn.AdaptiveAvgPool2d(1)
  11. self.conv = nn.Conv1d(1, 1, kernel_size=k_size, padding=(k_size - 1) // 2, bias=False)
  12. self.sigmoid = nn.Sigmoid()
  13. def forward(self, x):
  14. x1 = self.cv2(self.cv1(x))
  15. # out=self.eca(x1)*x1
  16. y = self.avg_pool(x1)
  17. y = self.conv(y.squeeze(-1).transpose(-1, -2)).transpose(-1, -2).unsqueeze(-1)
  18. y = self.sigmoid(y)
  19. out = x1 * y.expand_as(x1)
  20. return x + out if self.add else out
  21. class C3_ECA(C3):
  22. # C3 module with ECABottleneck()
  23. def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
  24. super().__init__(c1, c2, n, shortcut, g, e)
  25. c_ = int(c2 * e) # hidden channels
  26. self.m = nn.Sequential(*(ECABottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)))

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

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


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

按照上面的步骤创建yolov5s_C3_ECA.yaml文件

 替换4个C3模块,如下图所示

代码如下:

  1. # YOLOv5 🚀 by Ultralytics, GPL-3.0 license
  2. # Parameters
  3. nc: 80 # number of classes
  4. depth_multiple: 0.33 # model depth multiple
  5. width_multiple: 0.50 # layer channel multiple
  6. anchors:
  7. - [10,13, 16,30, 33,23] # P3/8
  8. - [30,61, 62,45, 59,119] # P4/16
  9. - [116,90, 156,198, 373,326] # P5/32
  10. # YOLOv5 v6.0 backbone
  11. backbone:
  12. # [from, number, module, args]
  13. [[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2
  14. [-1, 1, Conv, [128, 3, 2]], # 1-P2/4
  15. [-1, 3, C3_ECA, [128]],
  16. [-1, 1, Conv, [256, 3, 2]], # 3-P3/8
  17. [-1, 6, C3_ECA, [256]],
  18. [-1, 1, Conv, [512, 3, 2]], # 5-P4/16
  19. [-1, 3, C3_ECA, [512]],
  20. [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
  21. [-1, 3, C3_ECA, [1024]],
  22. [-1, 1, SPPF, [1024, 5]], # 9
  23. ]
  24. # YOLOv5 v6.0 head
  25. head:
  26. [[-1, 1, Conv, [512, 1, 1]],
  27. [-1, 1, nn.Upsample, [None, 2, 'nearest']],
  28. [[-1, 6], 1, Concat, [1]], # cat backbone P4
  29. [-1, 3, C3, [512, False]], # 13
  30. [-1, 1, Conv, [256, 1, 1]],
  31. [-1, 1, nn.Upsample, [None, 2, 'nearest']],
  32. [[-1, 4], 1, Concat, [1]], # cat backbone P3
  33. [-1, 3, C3, [256, False]], # 17 (P3/8-small)
  34. [-1, 1, Conv, [256, 3, 2]],
  35. [[-1, 14], 1, Concat, [1]], # cat head P4
  36. [-1, 3, C3, [512, False]], # 20 (P4/16-medium)
  37. [-1, 1, Conv, [512, 3, 2]],
  38. [[-1, 10], 1, Concat, [1]], # cat head P5
  39. [-1, 3, C3, [1024, False]], # 23 (P5/32-large)
  40. [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
  41. ]

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

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

这样就OK啦~


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

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

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


PS:今天训练了一下,我的评价是,ECA不如昨天的CA,mAP降了0.3