🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀
你好,我是@努力的小巴掌
之前用baseline跑了yolov8。
为了提升性能,我们需要对yolov8进行优化。
本次的优化,我们从增加注意力机制开始。
🚀CA:注意力机制
增强yolov8对于小目标的识别,加入CA机制。
论文是这样说的:
CA(Hou et al., 2021)机制:“该机制可以同时捕获远处的空间和通道信息,显着提高模型针对复杂情况准确定位和识别目标物体的能力”
“CA机制纳入YOLOv8的主干网络中,并与C2f模块相结合,将C2f模块重新设计为C2f_CA,可用于替换网络中原有的C2f模块。”
🚀优化步骤
一、增加CA注意力机制模块到conv.py里面
CA机制的代码
#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, 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, inp, kernel_size=1, stride=1, padding=0)
self.conv_w = nn.Conv2d(mip, inp, 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
这里有几个点要注意,网上的有的代码是不对的,这份是对的,可以跑起来。
class CoordAtt(nn.Module):
def __init__(self, inp, reduction=32): # 这里是输入通道的inp
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, inp, kernel_size=1, stride=1, padding=0) #这里也是输入通道
self.conv_w = nn.Conv2d(mip, inp, kernel_size=1, stride=1, padding=0)
在conv.py中增加这个CA的类:
直接在conv.py下拉到最下面,添加。
conv.py在ultralytics-main/ultralytics/nn/modules/conv.py
二、并且在conv.py的all中添加CoorAtt:
![](https://i-blog.csdnimg.cn/direct/c9c8e23966194719b2e69b501b07626b.png)
三、在__init__.py中的all进行引用:
ultralytics-main/ultralytics/nn/modules/__init__.py
![](https://i-blog.csdnimg.cn/direct/22debad371e544a3b3d7db21b352057d.png)
四、在tasks.py中导入
ultralytics-main/ultralytics/nn/tasks.py
打开nn =》 tasks.py
1、首先新增导入:import
nn.module中导入
2、然后在parse_model增加:
elif m in {CoordAtt}: args=[ch[f],*args]
直接crtl+F 搜索,找到定义算子层那里:
五、修改配置文件
先复制一份yolov8-seg.yaml,然后重命名为yolov8CA-seg.yaml
注意,之前在做语义分割的时候,用的是yolov8n-seg.yaml ,除了最后的任务是
- [[15, 18, 21], 1, Segment, [nc, 32, 256]] # Segment(P3, P4, P5)
其实,n的意思是,在yaml中定义了一个scale,是模型的缩放比例,如果是model = yolov8n-seg.yaml,那么就会调用yolov8-seg.yaml的带有“n”的缩放的yolov8-seg.yaml
要使用哪个,就把其他的注释掉。
修改yolov8CA-seg.yaml
好了,这里有个问题,我们看到,yolov8-seg.yaml一共有26层,我们应该把注意力机制加到什么地方?
以VGG16神经网络来看,随着网络的深入增加,特征张量的w和h在不断缩小,通道数不断增加。
越往后的图片,像素越小,分辨率越低,通道数越来越丰富;随着卷积的不断加深,特征图上的空间信息越来越少,而通道的信息会越来越丰富。
如果把空间注意力机制放在前面,通道注意力机制放在后面,往往会有比较好的效果。
加在head中,关于加到哪里,是实验经验得知的。
优化如下:
注意inp这个输入通道数是需要写的,输入通道数就是上一层的输出
具体可对照yolov8网络图看
然后我做的是小目标的分割,所以,把s保留,其他的注释掉。
完整的yaml配置文件是:
# Ultralytics YOLO 🚀, AGPL-3.0 license
# YOLOv8-seg instance segmentation model. For Usage examples see https://docs.ultralytics.com/tasks/segment
# Parameters
nc: 80 # number of classes
scales: # model compound scaling constants, i.e. 'model=yolov8n-seg.yaml' will call yolov8-seg.yaml with scale 'n'
# [depth, width, max_channels]
#n: [0.33, 0.25, 1024]
s: [0.33, 0.50, 1024]
# m: [0.67, 0.75, 768]
#l: [1.00, 1.00, 512]
#x: [1.00, 1.25, 512]
# YOLOv8.0n backbone
backbone:
# [from, repeats, module, args]
- [-1, 1, Conv, [64, 3, 2]] # 0-P1/2
- [-1, 1, Conv, [128, 3, 2]] # 1-P2/4
- [-1, 3, C2f, [128, True]]
- [-1, 1, Conv, [256, 3, 2]] # 3-P3/8
- [-1, 6, C2f, [256, True]]
- [-1, 1, Conv, [512, 3, 2]] # 5-P4/16
- [-1, 6, C2f, [512, True]]
- [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32
- [-1, 3, C2f, [1024, True]]
- [-1, 1, SPPF, [1024, 5]] # 9
# YOLOv8.0n head
head:
- [-1, 1, nn.Upsample, [None, 2, "nearest"]]
- [[-1, 6], 1, Concat, [1]] # cat backbone P4
- [-1, 3, C2f, [512]] # 12
- [-1, 1, nn.Upsample, [None, 2, "nearest"]]
- [[-1, 4], 1, Concat, [1]] # cat backbone P3
- [-1, 3, C2f, [256]] # 15 (P3/8-small)
- [-1, 1, CoordAtt,[256]]
- [-1, 1, Conv, [256, 3, 2]]
- [[-1, 12], 1, Concat, [1]]# cat head P4
- [-1, 3, C2f, [512]] # 18 (P4/16-medium)
- [-1, 1, CoordAtt,[512]]
- [-1, 1, Conv, [512, 3, 2]]
- [[-1, 9], 1, Concat, [1]]# cat head P5
- [-1, 3, C2f, [1024]] # 21 (P5/32-large)
- [-1, 1, CoordAtt,[1024]]
- [[15, 18, 21], 1, Segment, [nc, 32, 256]] # Segment(P3, P4, P5)
到这里就优化好了
六、测试有没有改成功
接下来,试下能不能运行成功。
新建一个脚本用来训练:mytrain-segCA.py
import multiprocessing
if __name__ == '__main__':
multiprocessing.freeze_support()
from ultralytics import YOLO
# Load a model
#model = YOLO("yolov8n-seg.yaml") # build a new model from YAML
model = YOLO('E:\\yolov8\\ultralytics-main\\ultralytics\\cfg\\models\\v8\\yolov8n-seg-CA.yaml')
#model = YOLO("yolov8n-seg.yaml").load("yolov8n.pt") # build from YAML and transfer weights
# Train the model
results = model.train(**{'data': 'E:\\yolov8\\ultralytics-main\\mydata.yaml',
'epochs':1})
首先,是定义了训练的模型,是我们刚刚优化的那个配置文件,第二个就是,定义了data在哪里。
从网络结构这,看到CoordAtt 机制加进去了。
所以,就修改成功了。
而且她提示,WARNING ⚠️ no model scale passed. Assuming scale='s'.
那就是用s了。
参考资料:
YOLOv5改进系列(3)——添加CA注意力机制-阿里云开发者社区