慢慢学,慢慢干。
大神博客:https://yolov5.blog.csdn.net/article/details/125148552
我老老实实的按照大神博主的方案进行修改。
第一步:common.py中添加BiFPN模型
# 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.0001
self.conv = nn.Conv2d(c1, c2, kernel_size=1, stride=1, padding=0)
self.silu = nn.SiLU()
def forward(self, x):
w = self.w
weight = 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.0001
self.conv = nn.Conv2d(c1, c2, kernel_size=1, stride=1, padding=0)
self.silu = nn.SiLU()
def forward(self, x):
w = self.w
weight = w / (torch.sum(w, dim=0) + self.epsilon)
# Fast normalized fusion
return self.conv(self.silu(weight[0] * x[0] + weight[1] * x[1] + weight[2] * x[2]))
第二步:修改yolo.py
使用Ctrl+F查询在elif m is Concat:
语句,在其后面加上BiFPN_Add
选项,确保yaml的BiFPN参数能够被识别到。
elif m is Concat:
c2 = sum(ch[x] for x in f)
# 添加bifpn_add结构
elif m in [BiFPN_Add2, BiFPN_Add3]:
c2 = max([ch[x] for x in f])
第三步:修改train.py,探讨重点篇。
- 将
BiFPN_Add2
和BiFPN_Add3
函数中定义的w
参数,加入g1
-
g0, g1, g2 = [], [], [] # optimizer parameter groups for v in model.modules(): if hasattr(v, 'bias') and isinstance(v.bias, nn.Parameter): # bias g2.append(v.bias) if isinstance(v, nn.BatchNorm2d): # weight (no decay) g0.append(v.weight) elif hasattr(v, 'weight') and isinstance(v.weight, nn.Parameter): # weight (with decay) g1.append(v.weight) # BiFPN_Concat elif isinstance(v, BiFPN_Add2) and hasattr(v, 'w') and isinstance(v.w, nn.Parameter): g1.append(v.w) elif isinstance(v, BiFPN_Add3) and hasattr(v, 'w') and isinstance(v.w, nn.Parameter): g1.append(v.w)
按照之前的版本是存在如上代码的,但是yolov5-v7.0版本中,这个部分优化成智能的optimizer。
-
通过观察可以发现,在大神博主的博客中修改的地方时160行左右附近,我按图索骥,来到这个地方。
-
与之前相比,这个地方变成了一个智能优化器函数。
-
smart_optimizer(model, opt.optimizer, hyp['lr0'], hyp['momentum'], hyp['weight_decay'])
震惊,我点了进去看看这个函数的实现,发现了惊天大咪咪。
没错,凭着多年直觉,我认为这个地方被重构了。之前的一重for变成了二重for,我来对比一下区别。
近似度非常高,作者偷懒没有修改注释,我们通过注释可以看出,虽然g[0] g[1] g[2 ]被g = [] [] []代替,但是我们还是可以读出来,这就是我们要的地方。
# optimizer parameter groups
我们要注意到博主老版本的地方g[1]代表:
#g[1] weight (with decay)
通过注释我们可以看出,v7.0的g[0]代表
# g【0 】 weight (with decay)
- 新版将这个地方关于weight的顺序翻转了一下,这样就导致一个问题,只要不是bias或者weight no decay,那么就全都归结于weight with decay上.
- 与之前需要elif 进行判断Bi_FPN进行模型的添加相比,这里不在需要添加判断条件了,因为最后的else会把 剩余非bias 和非weight nodecay 部分全部加到weight with decay上。
- 也就是说,添加其他Neck时,不需要额外对optimizer进行添加elif判断,也就实现了一个所谓智能的优化。
所以,我的结论就是第三步对参数g的修改不在需要,直接略过即可,智能优化器会帮我们对多余的部分进行自动增加权重。
第四步:修改yolov5.yaml
将Concat
全部换成BiFPN_Add,可以看到原来的Concat,全都变成新的BiFPN结构了。
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
# Parameters
nc: 80 # 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
[-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.1 BiFPN head
head:
[[-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
[-1, 1, Conv, [512, 3, 2]],
[[-1, 13, 6], 1, BiFPN_Add3, [256, 256]], #v5s通道数是默认参数的一半
[-1, 3, C3, [512, False]], # 20
[-1, 1, Conv, [512, 3, 2]],
[[-1, 10], 1, BiFPN_Add2, [256, 256]], # cat head P5
[-1, 3, C3, [1024, False]], # 23
[[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
]
现在开始实验有没有效果:
这里出现内存分配不足的情况,我的显卡显存是8G,查阅下资料,我把train的batchsize修改成-1,让训练的模型自适应epoch大小,不过可能会导致精度下降。
parser.add_argument('--epochs', type=int, default=30, help='total training epochs')
默认的batchsize是16,我修改成-1,再进行实验。
可以看出,进度图已经显示Bi_FPN结构,已经成功运行。
初学小白,难免有纰漏和错误,如有不同看法观点,欢迎交流。文章来源:https://www.toymoban.com/news/detail-427860.html
大神博客:https://yolov5.blog.csdn.net/article/details/125148552文章来源地址https://www.toymoban.com/news/detail-427860.html
到了这里,关于yolov5-7.0关于添加Bi_FPN的探讨的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!