1. 文章主要内容
本篇博客主要涉及多尺度高效注意力机制,融合到YOLOv5s模型中,增加模型提取多尺度特征的能力,助力模型涨点。(通读本篇博客需要7分钟左右的时间)。
2. 简要概括
论文地址:EMA论文地址
如下图所示。EMA注意力提取特征主要分三个特征分支,分别是X、Y和一个3 x 3 的卷积块。另外,原论文中提出了一种跨分支学习,将其中的三个分支融合在一起,丰富多尺度感受野的特征,最后调整维度输出。更详细的细节,请阅读原论文。
亮点在于:比起CA注意力机制多了一个3x3的卷积分支,另外新增了跨分支的特征交互,丰富了提取模型的特征信息,有助于模型涨点。
分析:建议将EMA放置于YOLOv5 BackBone部分,因为BackBone是提取特征的关键,另外数据集如果有大量多尺度的目标分布,EMA很有可能起到明显的作用。另外,在EMA基础上,可以做一些微弱的创新,比如新增一个5x5的卷积分支并加入到跨分支学习中等等,这里只是提供一个思路。
3. 详细代码改进流程
3.1新建一个EMA的文件,放置源代码
特别注意:博主这里将EMA与YOLOv5中的C3模块结构融合在一起并命名为C3EMA。所以源代码中会含有C3EMA的代码,另外此代码提高了main函数的测试案例,启动测试控制台打印输入输出通道对得上就木有问题。源代码如下:
import torch
from torch import nn
class EMA(nn.Module):
def __init__(self, channels, c2=None, factor=32):
super(EMA, self).__init__()
self.groups = factor
assert channels // self.groups > 0
self.softmax = nn.Softmax(-1)
self.agp = nn.AdaptiveAvgPool2d((1, 1))
self.pool_h = nn.AdaptiveAvgPool2d((None, 1))
self.pool_w = nn.AdaptiveAvgPool2d((1, None))
self.gn = nn.GroupNorm(channels // self.groups, channels // self.groups)
self.conv1x1 = nn.Conv2d(channels // self.groups, channels // self.groups, kernel_size=1, stride=1, padding=0)
self.conv3x3 = nn.Conv2d(channels // self.groups, channels // self.groups, kernel_size=3, stride=1, padding=1)
def forward(self, x):
b, c, h, w = x.size()
group_x = x.reshape(b * self.groups, -1, h, w) # b*g,c//g,h,w
x_h = self.pool_h(group_x)
x_w = self.pool_w(group_x).permute(0, 1, 3, 2)
hw = self.conv1x1(torch.cat([x_h, x_w], dim=2))
x_h, x_w = torch.split(hw, [h, w], dim=2)
x1 = self.gn(group_x * x_h.sigmoid() * x_w.permute(0, 1, 3, 2).sigmoid())
x2 = self.conv3x3(group_x)
x11 = self.softmax(self.agp(x1).reshape(b * self.groups, -1, 1).permute(0, 2, 1))
x12 = x2.reshape(b * self.groups, c // self.groups, -1) # b*g, c//g, hw
x21 = self.softmax(self.agp(x2).reshape(b * self.groups, -1, 1).permute(0, 2, 1))
x22 = x1.reshape(b * self.groups, c // self.groups, -1) # b*g, c//g, hw
weights = (torch.matmul(x11, x12) + torch.matmul(x21, x22)).reshape(b * self.groups, 1, h, w)
return (group_x * weights.sigmoid()).reshape(b, c, h, w)
class C3EMA(nn.Module):
def __init__(self, c1, c2, n=1, shortcut=True, g=1,
e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion #iscyy
super(C3EMA, self).__init__()
c_ = int(c2 * e) # hidden channels
self.ema = EMA(c1)
self.cv1 = Conv(c1, c_, 1, 1)
self.cv2 = Conv(c1, c_, 1, 1)
self.cv3 = Conv(2 * c_, c2, 1) # act=FReLU(c2)
# self.m = nn.Sequential(*[CB2d(c_) for _ in range(n)])
self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)])
def forward(self, x):
out = torch.cat((self.m(self.cv1(self.ema(x))), self.cv2(x)), dim=1)
out = self.cv3(out)
return out
if __name__ == '__main__':
model = C3EMA(64,64)
inputs = torch.randn((1, 64, 64, 64))
print(model(inputs).size())
3.2新建一个yolov5-EMA.yaml文件
注意到,这里博主直接使用C3EMA代替Backbone部分后面两个C3模块(事实上可以替换结构中的任意C3模块), 另外注意nc改为自己数据集的类别数。
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
# Parameters
nc: 4 # 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 output_channel, kernel_size, stride, padding
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
[-1, 3, C3, [128]],
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
[-1, 6, C3, [256]],
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
[-1, 9, C3EMA, [512]],
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
[-1, 3, C3EMA, [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)
]
3.3 将C3EMA引入到yolo.py文件中
在下图的红色圈内位置处,引入C3EMA(一共是两处地方),并手动导入相应的包即可。
3.4 修改train.py启动文件
修改配置文件为yolov5-EMA.yaml即可,如下图所示:
4. 总结
本篇博客主要介绍了高效多尺度注意力机制EMA,融合多尺度特征信息,助力YOLOv5模型涨点。另外,在修改过程中,要是有任何问题,评论区交流;如果博客对您有帮助,请帮忙点个赞,收藏一下;后续会持续更新本人实验当中觉得有用的点子,如果很感兴趣的话,可以关注一下,谢谢大家啦!