本文章纯属记录学习使用,我也不太明白是否为小目标检测层,不对的地方还希望一块交流
yolov5初始模型在特征融合时只对P3、P4、P5、三个特征层进行了融合,添加小目标检测层的目的是把P2(也就是yaml文件中第二个conv层得到的特征图)也加入到特征融合中。
P2位于低特征层,具有较强的位置信息,语义特征信息较弱,常用来进行小目标检测,这篇博客我觉得写的很好高低特征层。
但并不是你觉得你所要检测的是小目标,就需要加入小目标检测层,当添加小目标检测层有时会适得其反,并不会有所改进。看论文对于小目标的定义为:小于32*32像素的目标为小目标。
(yaml能跑通但是有些不合理,等有时间会修改)
添加BiFPN
第一步: 在common.py 文件下添加下列代码
# BiFPN# 两个特征图add操作class BiFPN_Add2(nn.Module):def __init__(self, c1, c2):super(BiFPN_Add2, self).__init__()# 设置可学习参数 nn.Parameter的作用是:将一个不可训练的类型Tensor转换成可以训练的类型parameter# 并且会向宿主模型注册该参数 成为其一部分 即model.parameters()会包含这个parameter# 从而在参数优化的时候可以自动一起优化self.w = nn.Parameter(torch.ones(2, dtype=torch.float32), requires_grad=True)self.epsilon = 0.0001self.conv = nn.Conv2d(c1, c2, kernel_size=1, stride=1, padding=0)self.silu = nn.SiLU()def forward(self, x):w = self.wweight = w / (torch.sum(w, dim=0) + self.epsilon)return self.conv(self.silu(weight[0] * x[0] + weight[1] * x[1]))# 三个特征图add操作class BiFPN_Add3(nn.Module):def __init__(self, c1, c2):super(BiFPN_Add3, self).__init__()self.w = nn.Parameter(torch.ones(3, dtype=torch.float32), requires_grad=True)self.epsilon = 0.0001self.conv = nn.Conv2d(c1, c2, kernel_size=1, stride=1, padding=0)self.silu = nn.SiLU()def forward(self, x):w = self.wweight = w / (torch.sum(w, dim=0) + self.epsilon)# Fast normalized fusionreturn self.conv(self.silu(weight[0] * x[0] + weight[1] * x[1] + weight[2] * x[2]))
第二步: 修改 yolo.py
elif m in [BiFPN_Add2, BiFPN_Add3]:c2 = max([ch[x] for x in f])
第三步: 修改 train.py
看下边
该部分代码还请去看迪导博客,他提供很多改进方法,值得学习,强烈推荐!
Yolov5如何更换BiFPN?
6.2版本的train.py代码有些变化,需要进入smart_optomizer这个函数中加入train.py这部分代码
6.2版本会出现报错,后续会解决
2023.3.6 最近在忙并没进行解决,报错的原因大概就是执行顺序的问题
解决思路:把smart_optimizer复制到train.py里,然后加上面红框的代码就可以(如果还报错请留言我再解决)
2023.3.13 解决报错
只需要把smart_optimizer这个函数放在train.py就行,位置随便
# BiFPN_Concatelif isinstance(v, BiFPN_Add2) and hasattr(v, 'w') and isinstance(v.w, nn.Parameter):g[1].append(v.w)elif isinstance(v, BiFPN_Add3) and hasattr(v, 'w') and isinstance(v.w, nn.Parameter):g[1].append(v.w)
导入BiFPN_Add2,BiFPN_Add3
from models.common import BiFPN_Add3, BiFPN_Add2
记得要把这个删除,不然会报错
运行成功
接下来便是修改后的yaml文件
# Parametersnc: 1# number of classesdepth_multiple: 0.33# model depth multiplewidth_multiple: 0.50# layer channel multipleanchors: 3# AutoAnchor evolves 3 anchors per P output layer# YOLOv5 v6.0 backbone# Adding connection in architecture between backbone and multi-stage headbackbone:# [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, [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, C3, [512]], [-1, 1, Conv, [1024, 3, 2]],# 7-P5/32 [-1, 3, C3, [1024]], [-1, 1, SPPF, [1024, 5]],# 9]# YOLOv5 v6.0 headhead:[[-1, 1, Conv, [512, 1, 1]], [-1, 1, nn.Upsample, [None, 2, 'nearest']], [[-1, 6], 1, BiFPN_Add2, [256,256]],# 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, BiFPN_Add2, [128,128]],# cat backbone P3 [-1, 3, C3, [256, False]],# 17 (P3/8-small) [-1, 1, Conv, [128, 1, 1]], [-1, 1, nn.Upsample, [None, 2, 'nearest']], [[-1, 2], 1, BiFPN_Add2, [64,64]],# cat backbone P2 [-1, 3, C3, [128, False]],# 21 (P2/4-tiny) [-1, 1, Conv, [256, 3, 2]], [[-1, 17, 4], 1, BiFPN_Add3, [128,128]], [-1, 3, C3, [256, False]],# 24 (P4/16-medium) [-1, 1, Conv, [512, 3, 2]], [[-1, 13, 6], 1, BiFPN_Add3, [256,256]], [-1, 3, C3, [512, False]],# 27 (P4/16-medium) [-1, 1, Conv, [512, 3, 2]], [[-1, 10], 1,BiFPN_Add2, [256,256]], [-1, 3, C3, [1024, False]],# 30 (P5/32-large) [[21, 24, 27, 30], 1, Detect, [nc, anchors]],# Detect(P2, P3, P4, P5)]
经过修改后的四检测头结合BiFPN(若有错误还请大佬指点一下)
# YOLOv5by Ultralytics, GPL-3.0 license# Parameters(P2, P3, P4, P5)都输出,宽深与large版本相同,相当于比large版本能检测更小物体nc: 1# number of classesdepth_multiple: 0.33# model depth multiplewidth_multiple: 0.50# layer channel multipleanchors: 4# AutoAnchor evolves 3 anchors per P output layer# YOLOv5 v6.0 backbonebackbone:# [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, [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, C3, [512]], [-1, 1, Conv, [1024, 3, 2]],# 7-P5/32 [-1, 3, C3, [1024]], [-1, 1, SPPF, [1024, 5]],# 9]# YOLOv5 v6.0 head with (P2, P3, P4, P5) outputshead:[[-1, 1, Conv, [512, 1, 1]], [-1, 1, nn.Upsample, [None, 2, 'nearest']], [[-1, 6], 1, BiFPN_Add2, [256,256]],# 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, BiFPN_Add2, [128,128]],# cat backbone P3 [-1, 3, C3, [256, False]],# 17 (P3/8-small) [-1, 1, Conv, [128, 1, 1]], [-1, 1, nn.Upsample, [None, 2, 'nearest']], [[-1, 2], 1, BiFPN_Add2, [64,64]],# cat backbone P2 [-1, 1, C3, [128, False]],# 21 (P2/4-xsmall) [-1, 1, Conv, [128, 3, 2]], [-1, 1, Conv, [256, 1, 1]], [[-1, 17, 4], 1, BiFPN_Add3, [128,128]],# cat head P3 [-1, 3, C3, [256, False]],# 25 (P3/8-small) [-1, 1, Conv, [256, 3, 2]], [-1, 1, Conv, [512, 1, 1]], [[-1, 13, 6], 1, BiFPN_Add3, [256,256]],# cat head P4 [-1, 3, C3, [512, False]],# 29 (P4/16-medium) [-1, 1, Conv, [512, 3, 2]], [[-1, 10], 1, BiFPN_Add2, [256,256]],# cat head P5 [-1, 3, C3, [1024, False]],# 32 (P5/32-large) [[21, 25, 29, 32], 1, Detect, [nc, anchors]],# Detect(P2, P3, P4, P5)]
题外话
这个博客中的BiFPN代码有两个版本,个人感觉第二个版本更合理些,因为不会出现我用1*1conv更换通道使得BiFPN_Add3结合成功,可以尝试下