yolov5-7.0关于添加Bi_FPN的探讨

慢慢学,慢慢干。

 大神博客: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_Add2BiFPN_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://yolov5.blog.csdn.net/article/details/125148552

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
xiaoxingxing的头像xiaoxingxing管理团队
上一篇 2023年5月27日
下一篇 2023年5月27日

相关推荐