讲清楚BiFPN原理和项目部署,提供代码和添加位置
论文名称:Path Aggregation Network for Instance Segmentation
论文地址:https://arxiv.org/abs/1803.01534
相较于其他特征融合网络,BiFPN的不同之处在于:首先,删除那些只有一个输入边的节点。我们的直觉很简单:如果一个节点只有一个输入边而没有特征融合,那么它将对旨在融合不同特征的特征网络的贡献较小。这导致了一个简化的双向网络;其次,如果它们处于同一级别,我们从原始输入到输出节点添加一条额外的边,以便在不增加太多成本的情况下融合更多特征;第三,与只有一条自顶向下和一条自底向上路径的 PANet不同,我们将每个双向(自顶向下和自底向上)路径视为一个特征网络层,并多次重复同一层以启用更高级的特征融合。
加权计算
参考这篇博文
https://blog.csdn.net/PLANTTHESON/article/details/133324866
添加代码
在yolo8代码的ultralytics-8.2.0\ultralytics\nn文件夹下新建BiFPN.py
import math
import numpy as np
import torch
import torch.nn as nn
# 结合BiFPN 设置可学习参数 学习不同分支的权重
# 两个分支concat操作
class BiFPN_Concat2(nn.Module):
def __init__(self, dimension=1):
super(BiFPN_Concat2, self).__init__()
self.d = dimension
self.w = nn.Parameter(torch.ones(2, dtype=torch.float32), requires_grad=True)
self.epsilon = 0.0001
def forward(self, x):
w = self.w
weight = w / (torch.sum(w, dim=0) + self.epsilon) # 将权重进行归一化
# Fast normalized fusion
x = [weight[0] * x[0], weight[1] * x[1]]
return torch.cat(x, self.d)
# 三个分支concat操作
class BiFPN_Concat3(nn.Module):
def __init__(self, dimension=1):
super(BiFPN_Concat3, self).__init__()
self.d = dimension
# 设置可学习参数 nn.Parameter的作用是:将一个不可训练的类型Tensor转换成可以训练的类型parameter
# 并且会向宿主模型注册该参数 成为其一部分 即model.parameters()会包含这个parameter
# 从而在参数优化的时候可以自动一起优化
self.w = nn.Parameter(torch.ones(3, dtype=torch.float32), requires_grad=True)
self.epsilon = 0.0001
def forward(self, x):
w = self.w
weight = w / (torch.sum(w, dim=0) + self.epsilon) # 将权重进行归一化
# Fast normalized fusion
x = [weight[0] * x[0], weight[1] * x[1], weight[2] * x[2]]
return torch.cat(x, self.d)
在yolo8代码的ultralytics-8.2.0\ultralytics\cfg\models\v8文件夹下新建yolov8bifpn.yaml文件。本人使用yolov8m模型。
# Ultralytics YOLO 🚀, AGPL-3.0 license
# YOLOv8 object detection model with P3-P5 outputs. For Usage examples see https://docs.ultralytics.com/tasks/detect
# Parameters
nc: 80 # number of classes
scales: # model compound scaling constants, i.e. 'model=yolov8n.yaml' will call yolov8.yaml with scale 'n'
# [depth, width, max_channels]
# n: [0.33, 0.25, 1024] # YOLOv8n summary: 225 layers, 3157200 parameters, 3157184 gradients, 8.9 GFLOPs
# s: [0.33, 0.50, 1024] # YOLOv8s summary: 225 layers, 11166560 parameters, 11166544 gradients, 28.8 GFLOPs
m: [0.67, 0.75, 768] # YOLOv8m summary: 295 layers, 25902640 parameters, 25902624 gradients, 79.3 GFLOPs
# l: [1.00, 1.00, 512] # YOLOv8l summary: 365 layers, 43691520 parameters, 43691504 gradients, 165.7 GFLOPs
# x: [1.00, 1.25, 512] # YOLOv8x summary: 365 layers, 68229648 parameters, 68229632 gradients, 258.5 GFLOPs
# 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, BiFPN_Concat2, [1]] # cat backbone P4
- [-1, 3, C2f, [512]] # 12
- [-1, 1, nn.Upsample, [None, 2, "nearest"]]
- [[-1, 4], 1, BiFPN_Concat2, [1]] # cat backbone P3
- [-1, 3, C2f, [256]] # 15 (P3/8-small)
- [-1, 1, Conv, [256, 3, 2]]
- [[-1, 6, 12], 1, BiFPN_Concat3, [1]] # cat head P4
- [-1, 3, C2f, [512]] # 18 (P4/16-medium)
- [-1, 1, Conv, [512, 3, 2]]
- [[-1, 9], 1, BiFPN_Concat2, [1]] # cat head P5
- [-1, 3, C2f, [1024]] # 21 (P5/32-large)
- [[15, 18, 21], 1, Detect, [nc]] # Detect(P3, P4, P5)
在yolo8代码的ultralytics-8.2.0\ultralytics\nn\tasks.py中导入创建好的类
from ultralytics.nn.BiFPN import BiFPN_Concat2, BiFPN_Concat3
在yolo8代码的ultralytics-8.2.0\ultralytics\nn\tasks.py中的parse_model函数的下面代码下
elif m is Concat:
c2 = sum(ch[x] for x in f)
添加如下内容
elif m is BiFPN_Concat2:
c2 = sum(ch[x] for x in f)
elif m is BiFPN_Concat3:
c2 = sum(ch[x] for x in f)
最后在yolov8的主目录ultralytics-8.2.0下新建main.py文件
from ultralytics import YOLO
def train_model():
model = YOLO("yolov8bifpn.yaml").load('yolov8m.pt')
model.train(data="rail_defects.yaml", epochs=300)
if __name__ == '__main__':
train_model()
运行结果如下
from n params module arguments
0 -1 1 1392 ultralytics.nn.modules.conv.Conv [3, 48, 3, 2]
1 -1 1 41664 ultralytics.nn.modules.conv.Conv [48, 96, 3, 2]
2 -1 2 111360 ultralytics.nn.modules.block.C2f [96, 96, 2, True]
3 -1 1 166272 ultralytics.nn.modules.conv.Conv [96, 192, 3, 2]
4 -1 4 813312 ultralytics.nn.modules.block.C2f [192, 192, 4, True]
5 -1 1 664320 ultralytics.nn.modules.conv.Conv [192, 384, 3, 2]
6 -1 4 3248640 ultralytics.nn.modules.block.C2f [384, 384, 4, True]
7 -1 1 1991808 ultralytics.nn.modules.conv.Conv [384, 576, 3, 2]
8 -1 2 3985920 ultralytics.nn.modules.block.C2f [576, 576, 2, True]
9 -1 1 831168 ultralytics.nn.modules.block.SPPF [576, 576, 5]
10 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest']
11 [-1, 6] 1 2 ultralytics.nn.BiFPN.BiFPN_Concat2 [1]
12 -1 2 1993728 ultralytics.nn.modules.block.C2f [960, 384, 2]
13 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest']
14 [-1, 4] 1 2 ultralytics.nn.BiFPN.BiFPN_Concat2 [1]
15 -1 2 517632 ultralytics.nn.modules.block.C2f [576, 192, 2]
16 -1 1 332160 ultralytics.nn.modules.conv.Conv [192, 192, 3, 2]
17 [-1, 6, 12] 1 3 ultralytics.nn.BiFPN.BiFPN_Concat3 [1]
18 -1 2 1993728 ultralytics.nn.modules.block.C2f [960, 384, 2]
19 -1 1 1327872 ultralytics.nn.modules.conv.Conv [384, 384, 3, 2]
20 [-1, 9] 1 2 ultralytics.nn.BiFPN.BiFPN_Concat2 [1]
21 -1 2 4207104 ultralytics.nn.modules.block.C2f [960, 576, 2]
22 [15, 18, 21] 1 3776854 ultralytics.nn.modules.head.Detect [2, [192, 384, 576]]