前言:Hello大家好,我是小哥谈。在计算机视觉任务中,特征金字塔网络(FPN)是一种常用的方法,它通过构建不同尺度的特征图来捕获不同尺度的目标。然而,传统的FPN存在一些缺点,如特征融合效率低、信息流通不充分等。BIFPN则通过引入双向的特征融合机制和加权的特征融合方法来克服这些问题。🌈
目录
🚀1.基础概念
🚀2.网络结构
🚀3.添加步骤
🚀4.改进方法
🍀🍀步骤1:block.py文件修改
🍀🍀步骤2:__init__.py文件修改
🍀🍀步骤3:tasks.py文件修改
🍀🍀步骤4:创建自定义yaml文件
🍀🍀步骤5:新建train.py文件
🍀🍀步骤6:模型训练测试
🚀1.基础概念
加权双向特征金字塔(Weighted Bi-directional Feature Pyramid Network,简称BiFPN)是一种改进的特征金字塔网络架构,它是在传统的Feature Pyramid Network (FPN)基础上发展而来。相较于单向的上采样和下采样,BiFPN引入了双向信息流,即不仅从低层金字塔向上层传递特征,也从高层金字塔向下层传播加强过的特征。
在BiFPN中,每个级别都包含两个路径:一个是自底向上的路径,用于增强底层特征;另一个是从顶到底部的路径,通过跨层级融合的方式,使得顶层的高级语义信息能够传达给低层。这样做的目的是为了捕获更丰富、更大范围的上下文信息,提高特征表示的精确性和鲁棒性。
此外,BiFPN通常会采用一些权重机制来调整信息流动的方向和程度,以便优化特征融合的质量。这些权重可以根据特征的重要性或者其他学习策略动态计算。
性能比较:
核心思想:
双向特征融合:传统的FPN是单向的,即从高层特征图向低层特征图传递信息。而BiFPN在此基础上增加了反向的信息传递,即从低层特征图向高层特征图传递信息。这种双向的信息流动使得特征图之间的信息融合更加充分。
加权特征融合:在BiFPN中,不同尺度的特征图在融合时会分配不同的权重。这些权重是可学习的参数,模型在训练过程中会自动调整它们,以最优地融合不同尺度的特征。这样一来,模型能够更好地利用每个特征图的信息,提高整体的特征表示能力。
论文题目:《EfficientDet: Scalable and Efficient Object Detection》
论文地址: http://arxiv.org/pdf/1911.09070
代码实现: https://github.com/google/automl/tree/master/efficientdet
说明:♨️♨️♨️
BiFPN是本文中提出的最重要的一个结构。在此之前介绍一下其他的特征金字塔网络(FPN)结构:
FPN:采用一种自上而下的方法来组合多尺度特征
PANet:在FPN之上增加了一个额外的自下而上的路径聚合网络
STDL:提出了一个利用跨尺度特征的尺度转换模块
M2det:提出一种融合多尺度特征的U形模块
NAS-FPN:利用神经架构搜索来自动设计特征网络拓扑结构
BiFPN:高效的双向跨尺度连接和加权特征融合
🚀2.网络结构
本文的改进是基于YOLOv10,关于其网络结构具体如下图所示:
YOLOv10官方仓库地址:
https://github.com/THU-MIG/yolov10
本文所作的改进是将Neck网络更换为BiFPN。改进后的网络结构图具体如下图所示:
🚀3.添加步骤
针对本文的改进,具体步骤如下所示:👇
步骤1:block.py文件修改
步骤2:__init__.py文件修改
步骤3:tasks.py文件修改
步骤4:创建自定义yaml文件
步骤5:新建train.py文件
步骤6:模型训练测试
🚀4.改进方法
🍀🍀步骤1:block.py文件修改
在源码中找到block.py文件,具体位置是ultralytics/nn/modules/block.py,然后将BiFPN模块代码添加到block.py文件末尾位置。
BiFPN模块代码:
# By CSDN 小哥谈
class Concat_BiFPN(nn.Module):
def __init__(self, dimension=1):
super(Concat_BiFPN, self).__init__()
self.d = dimension
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]]
return torch.cat(x, self.d)
然后,在block.py文件最上方下图所示位置加入Concat_BiFPN。
🍀🍀步骤2:__init__.py文件修改
在源码中找到__init__.py文件,具体位置是ultralytics/nn/modules/__init__.py。
修改1:在下图所示位置加入Concat_BiFPN,具体如下图所示:
修改2:在下图所示位置加入Concat_BiFPN,具体如下图所示:
🍀🍀步骤3:tasks.py文件修改
在源码中找到tasks.py文件,具体位置是ultralytics/nn/tasks.py。
修改1:在from ultralytics.nn.modules import ()中加入Concat_BiFPN,具体如下图所示:
修改2:找到parse_model函数(829行左右),在下图中所示位置添加如下代码。
# ------------start--------------
elif m is Concat_BiFPN:
c2 = sum(ch[x] for x in f)
# ------------ end---------------
具体添加位置如下图所示:
🍀🍀步骤4:创建自定义yaml文件
在源码ultralytics/cfg/models/v10目录下创建yaml文件,并命名为:yolov10n_BiFPN.yaml。具体如下图所示:
备注:其他版本yaml文件同理。
关于yolov10n_BiFPN.yaml文件的完整代码如下所示:👇
# By CSDN 小哥谈
# 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]
# 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, SCDown, [512, 3, 2]] # 5-P4/16
- [-1, 6, C2f, [512, True]]
- [-1, 1, SCDown, [1024, 3, 2]] # 7-P5/32
- [-1, 3, C2f, [1024, True]]
- [-1, 1, SPPF, [1024, 5]] # 9
- [-1, 1, PSA, [1024]] # 10
# YOLOv8.0n head
head:
- [-1, 1, nn.Upsample, [None, 2, "nearest"]]
- [[-1, 6], 1, Concat, [1]] # cat backbone P4
- [-1, 3, C2f, [512]] # 13
- [-1, 1, nn.Upsample, [None, 2, "nearest"]]
- [[-1, 4], 1, Concat, [1]] # cat backbone P3
- [-1, 3, C2f, [256]] # 16 (P3/8-small)
- [-1, 1, Conv, [256, 3, 2]]
- [[-1, 13], 1, Concat, [1]] # cat head P4
- [-1, 3, C2f, [512]] # 19 (P4/16-medium)
- [-1, 1, SCDown, [512, 3, 2]]
- [[-1, 10], 1, Concat, [1]] # cat head P5
- [-1, 3, C2fCIB, [1024, True, True]] # 22 (P5/32-large)
- [[16, 19, 22], 1, v10Detect, [nc]] # Detect(P3, P4, P5)
🍀🍀步骤5:新建train.py文件
在所下载的YOLOv10源码根目录下新建train.py文件,文件完整代码如下所示:
# coding:utf-8
# By CSDN 小哥谈
from ultralytics import YOLOv10
# 模型配置文件
model_yaml_path = "ultralytics/cfg/models/v10/yolov10n_BiFPN.yaml"
# 数据集配置文件
data_yaml_path = 'ultralytics/cfg/datasets/helmet.yaml'
# 预训练模型
pre_model_name = 'weights/yolov10n.pt'
if __name__ == '__main__':
# 加载预训练模型
model = YOLOv10("ultralytics/cfg/models/v10/yolov10n_BiFPN.yaml").load('weights/yolov10n.pt')
# 训练模型
results = model.train(data=data_yaml_path,epochs=100,batch=8,name='train_v10')
🍀🍀步骤6:模型训练测试
在train.py文件,点击“运行”,在作者自制的安全帽佩戴检测数据集上,模型可以正常训练。
参数量对比:🌈
YOLOv10n.yaml | 385 layers | 2707820 parameters | 2707804 gradients | 8.4 GFLOPs |
YOLOv10n_BiFPN.yaml | 385 layers | 2707829 parameters | 2707813 gradients | 8.4 GFLOPs |
说明:关于测试数据集,小伙伴们可根据个人情况进行更换!~🍉 🍓 🍑 🍈 🍌 🍐