【纯干货级教程】YOLOv7如何添加注意力机制?

一、注意力机制

注意力机制(Attention Mechanism)是一种在机器学习和深度学习中广泛使用的方法,它用于模仿人类大脑如何聚焦于特定信息并忽略其他不相关信息。

例如,人眼的视网膜上有一个叫做中心凹的区域,这里的视觉细胞密度最高,能提供最清晰的视觉信息,将其视为硬件基础。

当你阅读文字时,你的眼睛会集中在每个单词上,但你的外围视野可能只能模糊地看到周围的文本或物体,同时,人脑能够根据当前的任务或目标选择性地聚焦于某些视觉刺激,而忽视其他刺激。将这种方式视为技术基础。

不多水字数进入正题。

二、注意力机制能加在哪?

实际添加注意力机制在自己的模型中时,对合适的位置进行添加才能让其发挥应有的作用,除此之外还要想办法为自己的的做法进行解释。因此,最好在添加注意力机制的时候加入一些比较明确的位置。

以YOLOv7为例,简单地说,分为骨干网、颈部网和头部网,骨干网用于特征提取,颈部网用于特征融合,而头部网用于最终的检测、分割等任务。因此,将注意力机制加在骨干网能够加强对目标特征的提取能力,加在颈部网能够加强对目标特征的融合能力加强能力。除此之外,也可以加在其他位置,当然,为了解释的需要上述位置最为常见,若你可以进行一个较好的解释也可以进一步修改。

三、以YOLOv7-tiny为例添加注意力机制
1.加入骨干层示例1

解释示例:加在主干输入首端,替换原有CBL结构,起到一个全局的处理作用,加强对某特征的提取与抑制不相关特征的影响。(替换后若GFLOPS影响不大)保持下采样的功能不变外,又有效控制了计算成本,还显著提升了xx在某方面的性能。

代码示例:

# parameters
nc: 80  # number of classes
depth_multiple: 1.0  # model depth multiple
width_multiple: 1.0  # layer channel multiple

# anchors
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

# yolov7-tiny backbone
backbone:
  # [from, number, module, args] c2, k=1, s=1, p=None, g=1, act=True
  [[-1, 1, Attention, [32, 3, 2, None, 1, nn.LeakyReLU(0.1)]],  # 0-P1/2

   [-1, 1, Attention, [64, 3, 2, None, 1, nn.LeakyReLU(0.1)]],  # 1-P2/4

   [-1, 1, Conv, [32, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-2, 1, Conv, [32, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, Conv, [32, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, Conv, [32, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [[-1, -2, -3, -4], 1, Concat, [1]],
   [-1, 1, Conv, [64, 1, 1, None, 1, nn.LeakyReLU(0.1)]],  # 7

   [-1, 1, MP, []],  # 8-P3/8
   [-1, 1, Conv, [64, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-2, 1, Conv, [64, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, Conv, [64, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, Conv, [64, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [[-1, -2, -3, -4], 1, Concat, [1]],
   [-1, 1, Conv, [128, 1, 1, None, 1, nn.LeakyReLU(0.1)]],  # 14

   [-1, 1, MP, []],  # 15-P4/16
   [-1, 1, Conv, [128, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-2, 1, Conv, [128, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, Conv, [128, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, Conv, [128, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [[-1, -2, -3, -4], 1, Concat, [1]],
   [-1, 1, Conv, [256, 1, 1, None, 1, nn.LeakyReLU(0.1)]],  # 21

   [-1, 1, MP, []],  # 22-P5/32
   [-1, 1, Conv, [256, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-2, 1, Conv, [256, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, Conv, [256, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, Conv, [256, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [[-1, -2, -3, -4], 1, Concat, [1]],
   [-1, 1, Conv, [512, 1, 1, None, 1, nn.LeakyReLU(0.1)]],  # 28
  ]
2.加入骨干层示例2

解释示例:替换主干三个特征图输出层(小中大)ELAN模块的最后一个模块,对前置模块传入的特征信息做处理,起到一个局部的注意力加强与不相关特征抑制作用,(替换后若GFLOPS影响不大)略微修改原有的ELAN结构在保持了高性能的特征提取能力外,又有效控制了计算成本,还显著提升了xx在某方面的性能。

代码示例:

   [-1, 1, MP, []],  # 8-P3/8
   [-1, 1, Conv, [64, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-2, 1, Conv, [64, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, Conv, [64, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, Conv, [64, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [[-1, -2, -3, -4], 1, Concat, [1]],
   #[-1, 1, Conv, [128, 1, 1, None, 1, nn.LeakyReLU(0.1)]],  # 14
   [-1, 1, Attention, [128, 1, 1, None, 1, nn.LeakyReLU(0.1)]],  # 14

   [-1, 1, MP, []],  # 15-P4/16
   [-1, 1, Conv, [128, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-2, 1, Conv, [128, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, Conv, [128, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, Conv, [128, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [[-1, -2, -3, -4], 1, Concat, [1]],
   #[-1, 1, Conv, [256, 1, 1, None, 1, nn.LeakyReLU(0.1)]],  # 21
   [-1, 1, Attention, [256, 1, 1, None, 1, nn.LeakyReLU(0.1)]],  # 21

   [-1, 1, MP, []],  # 22-P5/32
   [-1, 1, Conv, [256, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-2, 1, Conv, [256, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, Conv, [256, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, Conv, [256, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [[-1, -2, -3, -4], 1, Concat, [1]],
   #[-1, 1, Conv, [512, 1, 1, None, 1, nn.LeakyReLU(0.1)]],  # 28
   [-1, 1, Attention, [512, 1, 1, None, 1, nn.LeakyReLU(0.1)]],  # 28
   
  ]
3.加入骨干层示例3

解释示例:加入主干三个特征图输出层(小中大)ELAN模块的后一个模块,对前置ELAN模块传入的特征信息做处理,起到一个局部的注意力加强与不相关特征抑制作用。

代码示例:

   [-1, 1, MP, []],  # 8-P3/8
   [-1, 1, Conv, [64, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-2, 1, Conv, [64, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, Conv, [64, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, Conv, [64, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [[-1, -2, -3, -4], 1, Concat, [1]],
   [-1, 1, Conv, [128, 1, 1, None, 1, nn.LeakyReLU(0.1)]],  # 14
   [-1, 1, Attention, [128, 1, 1, None, 1, nn.LeakyReLU(0.1)]],  # 15

   [-1, 1, MP, []],  # 16-P4/16
   [-1, 1, Conv, [128, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-2, 1, Conv, [128, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, Conv, [128, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, Conv, [128, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [[-1, -2, -3, -4], 1, Concat, [1]],
   [-1, 1, Conv, [256, 1, 1, None, 1, nn.LeakyReLU(0.1)]],  # 22
   [-1, 1, Attention, [256, 1, 1, None, 1, nn.LeakyReLU(0.1)]],  # 23

   [-1, 1, MP, []],  # 24-P5/32
   [-1, 1, Conv, [256, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-2, 1, Conv, [256, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, Conv, [256, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, Conv, [256, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [[-1, -2, -3, -4], 1, Concat, [1]],
   [-1, 1, Conv, [512, 1, 1, None, 1, nn.LeakyReLU(0.1)]],  # 30
   [-1, 1, Attention, [512, 1, 1, None, 1, nn.LeakyReLU(0.1)]],  # 31
4.加入骨干层示例4

解释示例:加在主干末端,对经由整个主干提取过的特征及小目标进行一个信息的处理,(若对小目标的检测能力加强了),这样修改增强了对小目标的特征提取能力,同时仅添加在最后一个特征图通道输出层在加强了性能的同时又保持了计算成本。

代码示例:

   [-1, 1, MP, []],  # 22-P5/32
   [-1, 1, Conv, [256, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-2, 1, Conv, [256, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, Conv, [256, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, Conv, [256, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [[-1, -2, -3, -4], 1, Concat, [1]],
   [-1, 1, Conv, [512, 1, 1, None, 1, nn.LeakyReLU(0.1)]],  # 28
   [-1, 1, Attention, [512, 1, 1, None, 1, nn.LeakyReLU(0.1)]],  # 29
5.加入颈部网示例1

解释示例:替换入YOLOv7-tiny基准池化层算法的最后一层被传入层保持了用于减少输入数据的空间尺寸的功能(例如,图像的宽度和高度),同时相比基准算法能够更为有效地保持其最重要的信息(或基准算法的池化层对图像进行降维处理后特征信息丢失较多,而这么做可以有效抑制特征的丢失等)。(替换后若GFLOPS影响不大)有效控制计算成本。

代码示例:

  [[-1, 1, Conv, [256, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-2, 1, Conv, [256, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, SP, [5]],
   [-2, 1, SP, [9]],
   [-3, 1, SP, [13]],
   [[-1, -2, -3, -4], 1, Concat, [1]],
   [-1, 1, Conv, [256, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [[-1, -7], 1, Concat, [1]],
   [-1, 1, Attention, [256, 1, 1, None, 1, nn.LeakyReLU(0.1)]],  # 37
6.加入颈部网示例2

解释示例:加在池化层后上采样层前,能有效对下采样处理后的特征信息进行一个xxx的加强,加强了算法的xxx性能,也保证了特征传入上采样层后能够有效工作。

代码示例:

# yolov7-tiny head
head:
  [[-1, 1, Conv, [256, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-2, 1, Conv, [256, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, SP, [5]],
   [-2, 1, SP, [9]],
   [-3, 1, SP, [13]],
   [[-1, -2, -3, -4], 1, Concat, [1]],
   [-1, 1, Conv, [256, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [[-1, -7], 1, Concat, [1]],
   [-1, 1, Conv, [256, 1, 1, None, 1, nn.LeakyReLU(0.1)]],  # 37
   [-1, 1, Attention, [256, 1, 1, None, 1, nn.LeakyReLU(0.1)]],  # 38
7.加入颈部网示例3

解释示例:替换在颈部网的三个不同尺寸特征图输出层ELAN结构的最后一个传入CBL模块,(替换后若GFLOPS影响不大)在有效控制计算成本的同时加强(保证)了对特征的融合能力,为主干网传入的特征进行了有效的处理与学习过程等。

代码示例:

   [-1, 1, Conv, [32, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-2, 1, Conv, [32, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, Conv, [32, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, Conv, [32, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [[-1, -2, -3, -4], 1, Concat, [1]],
   [-1, 1, Attention, [64, 1, 1, None, 1, nn.LeakyReLU(0.1)]],  # 29

   [-1, 1, Conv, [128, 3, 2, None, 1, nn.LeakyReLU(0.1)]],
   [[-1, 47], 1, Concat, [1]],

   [-1, 1, Conv, [64, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-2, 1, Conv, [64, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, Conv, [64, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, Conv, [64, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [[-1, -2, -3, -4], 1, Concat, [1]],
   [-1, 1, Attention, [128, 1, 1, None, 1, nn.LeakyReLU(0.1)]],  # 29

   [-1, 1, Conv, [256, 3, 2, None, 1, nn.LeakyReLU(0.1)]],
   [[-1, 37], 1, Concat, [1]],

   [-1, 1, Conv, [128, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-2, 1, Conv, [128, 1, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, Conv, [128, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [-1, 1, Conv, [128, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [[-1, -2, -3, -4], 1, Concat, [1]],
   [-1, 1, Attention, [256, 1, 1, None, 1, nn.LeakyReLU(0.1)]],  # 29
8.加入颈部网示例4

解释示例:加在颈部网的最后一个传出层,对不同尺度的特征进行一个最终的处理以有利于传入头部网进行进一步的检测、分类、分割等任务。(注意加在这里一般计算成本会加大许多)

代码示例:

   [57, 1, Attention, [128, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [65, 1, Attention, [256, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
   [73, 1, Attention, [512, 3, 1, None, 1, nn.LeakyReLU(0.1)]],
五、注意点

本文改进只是提供一个简单的示例解释,教大家如何添加注意力机制。

yolov7和yolov7-tiny整体结构差不多,若能理解本文的改进方法,则yolov7亦可采用同样的改进方式。

实际情况还需要各位同学按照添加的注意力机制类型、位置等进行一个合理的解释。

在实际的实验情况中,往往会出现添加注意力机制导致降点,这是很正常的。比如若你添加在骨干层,相较于基准算法,提取到的特征因为添加注意力机制后发生改变,则很容易对融合操作等造成影响;同样的,若你添加在颈部层,相较于基准算法,提取到的特征不变,但因为添加注意力机制后融合特征的操作发生改变,对网络的学习也会造成影响......

六、因此,什么时候添加注意力机制?

1.这里提供一个节约思路的小妙招,多看看文献再多实验,看看别人在你的环境采用的注意力机制实验情况,再思考自己的实验情况,此处只可意会不可言传了~

2.除此之外,若对自己的改进没有思路,也可以思考一下再引入不同的注意力机制进行测试,比如你的任务和位置有很大关系,那么采用基于位置的注意力机制是否有作用呢?多添加几个位置多实验试试吧。

3.若你的数据集很庞大,建议抽样检测,比如从一个拥有十万张图片的10类别数据集中抽取一千张10类别的图片,跑一次测试后,再添加注意力机制测试,这只是一个思路,实际仍需实验,因为抽样出的小数据集并不能完全代表整体。同时,它的特征也没有大数据集有代表性。

4.多实验几个改进后,分析数据——比如你的各个指标,相比其它改进,哪个高?低?为什么?问就是多测,实践出真知。

若你对本文有更多意见与建议,欢迎在评论区批评指正!本文将会持续更新。

更多文章产出中,主打简洁和准确,欢迎关注我,共同探讨!

  • 13
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值