学习资源站

RT-DETR改进策略【Neck】BiFPN:双向特征金字塔网络-跨尺度连接和加权特征融合_bifpn改进rtdetr-

RT-DETR改进策略【Neck】| BiFPN:双向特征金字塔网络-跨尺度连接和加权特征融合

一、本文介绍

本文记录的是 基于BiFPN结构的RT-DETR颈部网络改进方法研究 。在 RT-DETR 的Neck颈部网络中使用的 FPN+PAN 的结构,但是 FPN 在融合不同输入特征时简单地将它们相加,没有区分不同特征的重要性; PAN 虽然增加了额外的自底向上路径聚合网络,但参数和计算量较大。 为了解决这些问题,本文将颈部结构换成 BiFPN ,利用多尺度特征融合网络,使模型既能考虑不同输入特征的重要性,又能提高模型效率。



二、BiFPN介绍

EfficientDet : 可扩展的高效物体检测

BiFPN(加权双向特征金字塔网络) 是该论文中提出的一种用于高效多尺度特征融合的网络结构,其设计原理和优势如下:

2.1、BiFPN原理

  • 问题 formulation :多尺度特征融合的目标是聚合不同分辨率的特征,给定多尺度特征列表 P → i n = ( P i n 1 , P i n 2 , … ) \overrightarrow{P}_{in} = (P_{in}^1, P_{in}^2, \ldots) P in = ( P in 1 , P in 2 , ) ,其中 P i n i P_{in}^i P in i 代表第 i i i 级的特征,目标是找到一个变换 f f f ,使得 P → o u t = f ( P → i n ) \overrightarrow{P}_{out} = f(\overrightarrow{P}_{in}) P o u t = f ( P in ) 。以传统的 FPN 为例,它以自上而下的方式聚合多尺度特征,如 P 7 o u t = C o n v ( P 7 i n ) P_{7}^{out} = Conv(P_{7}^{in}) P 7 o u t = C o n v ( P 7 in ) P 6 o u t = C o n v ( P 6 i n + R e s i z e ( P 7 o u t ) ) P_{6}^{out} = Conv(P_{6}^{in} + Resize(P_{7}^{out})) P 6 o u t = C o n v ( P 6 in + R es i ze ( P 7 o u t )) 等。

2.1.1 跨尺度连接

  • 为了改进传统 FPN 单向信息流的局限性, PANet 增加了自底向上的路径聚合网络,其他研究也进一步探讨了跨尺度连接。通过研究 FPN PANet NAS-FPN 这三个网络的性能和效率,本文对跨尺度连接进行了优化:
  • 移除只有一个输入边缘的节点,因为这样的节点对旨在融合不同特征的特征网络贡献较小,从而得到简化的双向网络。
  • 如果原始输入和输出节点在同一级别,添加一个额外的边缘,以便在不增加太多成本的情况下融合更多特征。
  • 将每个双向(自上而下和自底向上)路径视为一个特征网络层,并多次重复该层以实现更多高级特征融合,从而命名为 双向特征金字塔网络(BiFPN)

2.1.2 加权特征融合

当融合不同分辨率的特征时,常见的方法是先将它们调整到相同分辨率然后相加,但这种方法没有区分不同输入特征的重要性。为了解决这个问题,提出为每个输入添加一个额外的权重,让网络学习每个输入特征的重要性,并考虑了三种加权融合方法:
- Unbounded fusion O = ∑ i w i ⋅ I i O = \sum_{i} w_{i} \cdot I_{i} O = i w i I i ,其中 w i w_{i} w i 是可学习的权重,可以是标量(每个特征)、向量(每个通道)或多维张量(每个像素)。通过权重归一化来限制每个权重的值范围,以避免训练不稳定。
- Softmax - based fusion O = ∑ i e w i ∑ j e w j ⋅ I i O = \sum_{i} \frac{e^{w_{i}}}{\sum_{j} e^{w_{j}}} \cdot I_{i} O = i j e w j e w i I i ,对每个权重应用Softmax,使所有权重归一化为0到1之间的概率,表示每个输入的重要性。但在GPU硬件上会导致显著的减速。
- Fast normalized fusion O = ∑ i w i ϵ + ∑ j w j ⋅ I i O = \sum_{i} \frac{w_{i}}{\epsilon + \sum_{j} w_{j}} \cdot I_{i} O = i ϵ + j w j w i I i ,通过在每个 w i w_{i} w i 后应用Relu确保 w i ≥ 0 w_{i} \geq 0 w i 0 ϵ = 0.0001 \epsilon = 0.0001 ϵ = 0.0001 是一个小值以避免数值不稳定。这种方法的每个归一化权重的值也落在0和1之间,且没有Softmax操作,因此更高效。

最终的BiFPN集成了双向跨尺度连接和快速归一化融合。例如,对于图2(d)中BiFPN在级别6的两个融合特征,计算方式为:
- P 6 t d = C o n v ( w 1 ⋅ P 6 i n + w 2 ⋅ R e s i z e ( P 7 i n ) w 1 + w 2 + ϵ ) P_{6}^{td} = Conv(\frac{w_{1} \cdot P_{6}^{in} + w_{2} \cdot Resize(P_{7}^{in})}{w_{1} + w_{2} + \epsilon}) P 6 t d = C o n v ( w 1 + w 2 + ϵ w 1 P 6 in + w 2 R es i ze ( P 7 in ) )
- P 6 o u t = C o n v ( w 1 ′ ⋅ P 6 i n + w 2 ′ ⋅ P 6 t d + w 3 ′ ⋅ R e s i z e ( P 5 o u t ) w 1 ′ + w 2 ′ + w 3 ′ + ϵ ) P_{6}^{out} = Conv(\frac{w_{1}' \cdot P_{6}^{in} + w_{2}' \cdot P_{6}^{td} + w_{3}' \cdot Resize(P_{5}^{out})}{w_{1}' + w_{2}' + w_{3}' + \epsilon}) P 6 o u t = C o n v ( w 1 + w 2 + w 3 + ϵ w 1 P 6 in + w 2 P 6 t d + w 3 R es i ze ( P 5 o u t ) )

为了进一步提高效率,使用深度可分离卷积进行特征融合,并在每次卷积后添加批量归一化和激活。

在这里插入图片描述

在这里插入图片描述

2.2、优势

  • 通过优化跨尺度连接和加权特征融合, BiFPN 能够更好地融合多尺度特征,提高特征的表达能力。
  • 与其他特征网络相比, BiFPN 在实现相似精度的情况下,使用更少的参数和计算量,提高了模型效率。
  • 快速归一化融合方法与Softmax-based融合相比,具有非常相似的学习行为和精度,但在GPU上运行速度更快。

论文: https://arxiv.org/pdf/1911.09070.pdf
源码: https://github.com/google/automl/tree/master/efficientdet

三、BiFPN模块的实现代码

BiFPN模块 的实现代码如下:

import torch.nn as nn
import torch

class swish(nn.Module):
    def forward(self, x):
        return x * torch.sigmoid(x)

class BiFPN(nn.Module):
    def __init__(self, length):
        super().__init__()
        self.weight = nn.Parameter(torch.ones(length, dtype=torch.float32), requires_grad=True)
        self.swish = swish()
        self.epsilon = 0.0001

    def forward(self, x):
        weights = self.weight / (torch.sum(self.swish(self.weight), dim=0) + self.epsilon) 
        weighted_feature_maps = [weights[i] * x[i] for i in range(len(x))]
        stacked_feature_maps = torch.stack(weighted_feature_maps, dim=0)
        result = torch.sum(stacked_feature_maps, dim=0)
        return result
        

四、添加步骤

4.1 修改ultralytics/nn/modules/block.py

此处需要修改的文件是 ultralytics/nn/modules/block.py

block.py中定义了网络结构的通用模块 ,我们想要加入新的模块就只需要将模块代码放到这个文件内即可。

BiFPN 模块代码添加到此文件下。

在这里插入图片描述

4.2 修改ultralytics/nn/modules/ init .py

此处需要修改的文件是 ultralytics/nn/modules/__init__.py

__init__.py 文件中定义了所有模块的初始化,我们只需要将 block.py 中的新的模块命添加到对应的函数即可。

Bi_FPN block.py 中实现,所有要添加在 from .block import

from .block import (
    C1,
    C2,
    ...
    BiFPN
)

在这里插入图片描述

4.3 修改ultralytics/nn/modules/tasks.py

tasks.py 文件中,需要在两处位置添加各模块类名称。

首先:在函数声明中引入 BiFPN

在这里插入图片描述

在这里插入图片描述

其次:在 parse_model函数 中注册 BiFPN 模块

在这里插入图片描述

elif m is BiFPN:
    length = len([ch[x] for x in f])
    args = [length]

在这里插入图片描述


五、yaml模型文件

5.1 模型改进⭐

在代码配置完成后,配置模型的YAML文件。

此处以 ultralytics/cfg/models/rtdetr/rtdetr-l.yaml 为例,在同目录下创建一个用于自己数据集训练的模型文件 rtdetr-l-BiFPN.yaml

rtdetr-l.yaml 中的内容复制到 rtdetr-l-BiFPN.yaml 文件下,修改 nc 数量等于自己数据中目标的数量。

📌 修改方法是将 BiFPN模块 替换 rtdetr 颈部网络中的 concat ,并修改结构。

# Ultralytics YOLO 🚀, AGPL-3.0 license
# RT-DETR-l object detection model with P3-P5 outputs. For details see https://docs.ultralytics.com/models/rtdetr

# Parameters
nc: 1 # number of classes
scales: # model compound scaling constants, i.e. 'model=yolov8n-cls.yaml' will call yolov8-cls.yaml with scale 'n'
  # [depth, width, max_channels]
  l: [1.00, 1.00, 1024]

backbone:
  # [from, repeats, module, args]
  - [-1, 1, HGStem, [32, 48]] # 0-P2/4
  - [-1, 6, HGBlock, [48, 128, 3]] # stage 1

  - [-1, 1, DWConv, [128, 3, 2, 1, False]] # 2-P3/8
  - [-1, 6, HGBlock, [96, 512, 3]] # stage 2

  - [-1, 1, DWConv, [512, 3, 2, 1, False]] # 4-P4/16
  - [-1, 6, HGBlock, [192, 1024, 5, True, False]] # cm, c2, k, light, shortcut
  - [-1, 6, HGBlock, [192, 1024, 5, True, True]]
  - [-1, 6, HGBlock, [192, 1024, 5, True, True]] # stage 3

  - [-1, 1, DWConv, [1024, 3, 2, 1, False]] # 8-P5/32
  - [-1, 6, HGBlock, [384, 2048, 5, True, False]] # stage 4

head:
  - [-1, 1, Conv, [256, 1, 1, None, 1, 1, False]] # 10 input_proj.2
  - [-1, 1, AIFI, [1024, 8]]
  - [-1, 1, Conv, [256, 1, 1]] # 12, Y5, lateral_convs.0

  - [3, 1, Conv, [256]]  # 13-P3/8
  - [7, 1, Conv, [256]]  # 14-P4/16
  - [12, 1, Conv, [256]]  # 15-P5/32

  - [-1, 1, nn.Upsample, [None, 2, 'nearest']] # 16 P5->P4
  - [[-1, 14], 1, BiFPN, []] # 17
  - [-1, 3, RepC3, [256]] # 18-P4/16
  
  - [-1, 1, nn.Upsample, [None, 2, 'nearest']] # 19 P4->P3
  - [[-1, 13], 1, BiFPN, []] # 20
  - [-1, 3, RepC3, [256]] # 21-P3/8

  - [1, 1, Conv, [256, 3, 2]] # 22 P2->P3
  - [[-1, 13, 21], 1, BiFPN, []] # 23
  - [-1, 3, RepC3, [256]] # 24-P3/8

  - [-1, 1, Conv, [256, 3, 2]] # 25 P3->P4
  - [[-1, 14, 18], 1, BiFPN, []] # 26
  - [-1, 3, RepC3, [256]] # 27-P4/16

  - [-1, 1, Conv, [256, 3, 2]] # 28 P4->P5
  - [[-1, 15], 1, BiFPN, []] # 29
  - [-1, 3, RepC3, [256]] # 30-P5/32

  - [[24, 27, 28], 1, RTDETRDecoder, [nc, 256, 300, 4, 8, 3]]  # Detect(P3, P4, P5)


六、成功运行结果

分别打印网络模型可以看到 BiFPN模块 已经加入到模型中,并可以进行训练了。

rtdetr-l-BiFPN

rtdetr-l-BiFPN summary: 649 layers, 31,294,304 parameters, 31,294,304 gradients, 131.0 GFLOPs

                   from  n    params  module                                       arguments                     
  0                  -1  1     25248  ultralytics.nn.modules.block.HGStem          [3, 32, 48]                   
  1                  -1  6    155072  ultralytics.nn.modules.block.HGBlock         [48, 48, 128, 3, 6]           
  2                  -1  1      1408  ultralytics.nn.modules.conv.DWConv           [128, 128, 3, 2, 1, False]    
  3                  -1  6    839296  ultralytics.nn.modules.block.HGBlock         [128, 96, 512, 3, 6]          
  4                  -1  1      5632  ultralytics.nn.modules.conv.DWConv           [512, 512, 3, 2, 1, False]    
  5                  -1  6   1695360  ultralytics.nn.modules.block.HGBlock         [512, 192, 1024, 5, 6, True, False]
  6                  -1  6   2055808  ultralytics.nn.modules.block.HGBlock         [1024, 192, 1024, 5, 6, True, True]
  7                  -1  6   2055808  ultralytics.nn.modules.block.HGBlock         [1024, 192, 1024, 5, 6, True, True]
  8                  -1  1     11264  ultralytics.nn.modules.conv.DWConv           [1024, 1024, 3, 2, 1, False]  
  9                  -1  6   6708480  ultralytics.nn.modules.block.HGBlock         [1024, 384, 2048, 5, 6, True, False]
 10                  -1  1    524800  ultralytics.nn.modules.conv.Conv             [2048, 256, 1, 1, None, 1, 1, False]
 11                  -1  1    789760  ultralytics.nn.modules.transformer.AIFI      [256, 1024, 8]                
 12                  -1  1     66048  ultralytics.nn.modules.conv.Conv             [256, 256, 1, 1]              
 13                   3  1    131584  ultralytics.nn.modules.conv.Conv             [512, 256]                    
 14                   7  1    262656  ultralytics.nn.modules.conv.Conv             [1024, 256]                   
 15                  12  1     66048  ultralytics.nn.modules.conv.Conv             [256, 256]                    
 16                  -1  1         0  torch.nn.modules.upsampling.Upsample         [None, 2, 'nearest']          
 17            [-1, 14]  1         2  ultralytics.nn.AddModules.BiFPN.BiFPN        [2]                           
 18                  -1  3   2101248  ultralytics.nn.modules.block.RepC3           [256, 256, 3]                 
 19                  -1  1         0  torch.nn.modules.upsampling.Upsample         [None, 2, 'nearest']          
 20            [-1, 13]  1         2  ultralytics.nn.AddModules.BiFPN.BiFPN        [2]                           
 21                  -1  3   2101248  ultralytics.nn.modules.block.RepC3           [256, 256, 3]                 
 22                   1  1    295424  ultralytics.nn.modules.conv.Conv             [128, 256, 3, 2]              
 23        [-1, 13, 21]  1         3  ultralytics.nn.AddModules.BiFPN.BiFPN        [3]                           
 24                  -1  3   2101248  ultralytics.nn.modules.block.RepC3           [256, 256, 3]                 
 25                  -1  1    590336  ultralytics.nn.modules.conv.Conv             [256, 256, 3, 2]              
 26        [-1, 14, 18]  1         3  ultralytics.nn.AddModules.BiFPN.BiFPN        [3]                           
 27                  -1  3   2101248  ultralytics.nn.modules.block.RepC3           [256, 256, 3]                 
 28                  -1  1    590336  ultralytics.nn.modules.conv.Conv             [256, 256, 3, 2]              
 29            [-1, 15]  1         2  ultralytics.nn.AddModules.BiFPN.BiFPN        [2]                           
 30                  -1  3   2101248  ultralytics.nn.modules.block.RepC3           [256, 256, 3]                 
 31        [24, 27, 28]  1   3917684  ultralytics.nn.modules.head.RTDETRDecoder    [1, [256, 256, 256], 256, 300, 4, 8, 3]
rtdetr-l-BiFPN summary: 649 layers, 31,294,304 parameters, 31,294,304 gradients, 131.0 GFLOPs