学习资源站

RT-DETR模型结构详解,在Ultralytics项目中配置rtdetr-resnet18、rtdetr-resnet34_ultralytics中修改配置文件rt-detr-resnet50改为rt-detr-resnet-

RT-DETR模型结构详解,在Ultralytics项目中配置rtdetr-resnet18、rtdetr-resnet34

前言

RT-DETR 是一种实时端到端的目标检测模型,主要由 骨干网络 (backbone) 高效混合编码器 (efficient hybrid encoder) 带有辅助预测头的 Transformer 解码器 (Transformer decoder with auxiliary prediction heads) 组成。本文对其进行了详细分析,并在 Ultralytics 项目中配置了 rtdetr-resnet18 rtdetr-resnet34 结构。


原文翻译可查看:

一、骨干网络

它是模型的基础部分, 负责提取图像的特征

RT-DETR 中,会将骨干网络最后三个阶段的特征 S 3 , S 4 , S 5 S_{3}, S_{4}, S_{5} S 3 , S 4 , S 5 输入到后续的编码器中。常见的骨干网络可以是 ResNet 等,在实验中作者使用了在 ImageNet 上预训练的 ResNet 作为骨干网络。

二、高效混合编码器

2.1 多尺度特征处理的优化

传统的多尺度特征引入虽然 有助于训练收敛 ,但会 使输入到编码器的序列长度大幅增加,进而导致计算成本剧增 ,成为 Transformer 编码器的计算瓶颈。

例如在一些相关研究中,如 Deformable-DETR,编码器的计算量占比很大但对精度的贡献相对较小。

RT-DETR 的高效混合编码器对此进行了改进,它重新思考了多尺度特征的处理方式。通过分析发现,对连接的多尺度特征进行全面的特征交互存在 冗余 ,因为 高层特征已经从低层特征中提取了丰富的语义信息 。所以,它设计了一系列编码器变体进行实验验证。

比如从 DINO-Deformable-R50 中移除多尺度 Transformer 编码器作为变体 A,然后在 A 的基础上逐步改进。

在这里插入图片描述

通过这些实验表明,同时进行尺度内和尺度间的特征交互效率低下,为后续的结构设计提供了依据。

2.2 结构创新设计

2.2.1 Attention-based Intra-scale Feature Interaction (AIFI)

AIFI 模块 是高效混合编码器的重要组成部分。它基于前期的分析和实验结果, 仅对 S 5 S_{5} S 5 使用单尺度 Transformer 编码器进行尺度内交互 。这是因为高层特征 S 5 S_{5} S 5 具有更丰富的语义概念 ,对其应用自注意力操作 能够有效捕捉概念实体之间的联系 ,这对于后续模块准确地定位和识别物体非常有帮助。

对较低层特征进行尺度内交互不仅没有必要,还会因为缺乏语义概念以及可能与高层特征交互产生重复和混淆,从而增加计算成本。

在这里插入图片描述

实验数据也证明了这一点,在变体 D 的基础上仅对 S 5 S_{5} S 5 进行尺度内交互得到的 D S 5 D_{S_{5}} D S 5 ,与变体 D 相比,不仅延迟降低了 35%,而且精度还提高了 0.4% AP。

2.2.2 CNN-based Cross-scale Feature Fusion (CCFF)

CCFF 模块 主要 负责尺度间的特征融合

它在融合路径中插入了由卷积层组成的融合块。这些融合块的结构设计十分精巧,每个融合块包含两个 1 × 1 1×1 1 × 1 卷积,其作用是 调整特征的通道数,使其能够更好地进行融合 。同时,还有 N N N 个由 RepConv 组成的 RepBlocks 用于实际的特征融合操作,最后通过逐元素相加的方式将两路输出进行融合。

这种结构设计能够 高效地融合相邻尺度的特征 ,减少了不必要的计算开销,进一步提高了编码器的效率。

例如,变体 B 插入单尺度 Transformer 编码器进行尺度内特征交互后拼接输出;变体 C 在 B 的基础上引入尺度间特征融合并输入多尺度 Transformer 编码器;变体 D 则将尺度内交互和尺度间融合解耦;变体 E 进一步增强了交互和融合,也就是最终的高效混合编码器。

三、不确定性最小查询选择(Uncertainty-minimal Query Selection)

  • 现有问题与改进思路 :之前的一些工作大多是用置信度分数从编码器中选择前 K K K 个特征来初始化对象查询。然而, 检测器需要同时对物体的类别和位置进行建模 ,而当前的查询选择方法只考虑了 置信度分数 ,忽略了这一点, 导致选择的特征存在很大的不确定性 ,影响了解码器的初始化和检测器的性能。所以作者提出了 不确定性最小查询选择 方案,通过 显式地构建和优化认知不确定性来对编码器特征的联合潜在变量进行建模 ,从而为解码器提供高质量的查询。

  • 具体方法 :将特征不确定性 u u u 定义为定位 P P P 和分类 c c c 的预测分布之间的差异,即 U ( X ^ ) = ∥ P ( X ^ ) − C ( X ^ ) ∥ \mathcal{U}(\hat{\mathcal{X}})=\| \mathcal{P}(\hat{\mathcal{X}})-\mathcal{C}(\hat{\mathcal{X}})\| U ( X ^ ) = P ( X ^ ) C ( X ^ ) X ^ ∈ R D \hat{\mathcal{X}} \in \mathbb{R}^{D} X ^ R D 。为了最小化查询的不确定性,将其集成到损失函数中进行基于梯度的优化,如 L ( X ^ , Y ^ , Y ) = L b o x ( b ^ , b ) + L c l s ( U ( X ^ ) , c ^ , c ) \mathcal{L}(\hat{\mathcal{X}}, \hat{\mathcal{Y}}, \mathcal{Y})=\mathcal{L}_{b o x}(\hat{b}, b)+\mathcal{L}_{c l s}(\mathcal{U}(\hat{\mathcal{X}}), \hat{c}, c) L ( X ^ , Y ^ , Y ) = L b o x ( b ^ , b ) + L c l s ( U ( X ^ ) , c ^ , c )
    在这里插入图片描述

从图中可以看到,通过可视化在 COCO val2017 上选择的特征的分类分数和 IoU 分数,对比使用不确定性最小查询选择和普通查询选择训练的模型,可以发现不确定性最小查询选择产生的高质量编码器特征更多,紫色点(代表不确定性最小查询选择训练的模型)更集中在图的右上角,说明其对应的特征质量更高,即 预测的类别和框更有可能描述真实物体

在这里插入图片描述

四、解码器Decoder

RT-DETR模型的解码器结构主要围绕着接收来自编码器的特征和初始查询,并通过迭代优化生成最终的类别和边界框预测展开。

  • 输入与初始化 :解码器接收来自高效混合编码器处理后的特征以及通过不确定性最小查询选择确定的初始对象查询。这些初始对象查询是经过精心筛选和优化的,旨在为解码器提供高质量的起始信息,以便后续能够更准确地进行目标定位和分类。
  • 迭代优化过程 :解码器带有辅助预测头,在接收到输入后,会进行迭代优化操作。在这个过程中,它不断地对输入的对象查询进行调整和完善,通过多层的结构逐步提取更丰富的特征信息和语义信息。每一层都基于前一层的结果进行进一步的分析和处理,逐步缩小预测的范围和提高预测的准确性。
  • 与编码器的协同 :解码器的工作与编码器密切相关。编码器负责对多尺度特征进行高效处理和转换,为解码器提供合适的特征序列。解码器则在此基础上,利用其自身的结构和机制,对这些特征进行深入挖掘和分析,两者相辅相成,共同推动目标检测任务的完成。
  • 速度与精度的平衡 :在设计上,RT-DETR的解码器支持灵活的速度调整。通过调整解码器的层数,可以在一定程度上控制模型的推理速度。例如,减少解码器层数虽然可能会对精度产生一定的影响,但能够显著提高推理速度,这使得模型能够根据不同的应用场景和需求,在速度和精度之间找到合适的平衡点,增强了模型的实用性和适用性。

在这里插入图片描述

五、rtdetr-resnet18配置

在骨干网络中:
RT-DETR 官方代码的 rtdetr_paddle\ppdet\modeling\backbones\resnet.py 路径中,配置了 ResNet18 ~ ResNet152 的模块层数:

在这里插入图片描述

在颈部和检测头中:

ResNet18 相较于 ResNet50 主要在高效混合编码器中的 expansion ,为隐藏层缩放倍数 0.5 ,以及解码器中的 num_decoder_layers ,解码器层数 3

在这里插入图片描述

针对ResNet18和ResNet34需重新配置骨干,按照ultralytics官方提供的ResNet50模型结构直接配置ResNet18时会出现通道数无法对齐的情况,本文做如下修改:

① 在 ultralytics/nn/ 目录下新建 AddModules 文件夹用于存放模块代码

② 在 AddModules 文件夹下新建 ResNet.py ,将下方代码粘贴进去:

在这里插入图片描述

from collections import OrderedDict
import torch.nn as nn
import torch.nn.functional as F
 
class ConvNormLayer(nn.Module):
    def __init__(self,
                 ch_in,
                 ch_out,
                 filter_size,
                 stride,
                 groups=1,
                 act=None):
        super(ConvNormLayer, self).__init__()
        self.act = act
        self.conv = nn.Conv2d(
            in_channels=ch_in,
            out_channels=ch_out,
            kernel_size=filter_size,
            stride=stride,
            padding=(filter_size - 1) // 2,
            groups=groups)
 
        self.norm = nn.BatchNorm2d(ch_out)
 
    def forward(self, inputs):
        out = self.conv(inputs)
        out = self.norm(out)
        if self.act:
            out = getattr(F, self.act)(out)
        return out
 
class SELayer(nn.Module):
    def __init__(self, ch, reduction_ratio=16):
        super(SELayer, self).__init__()
        self.avg_pool = nn.AdaptiveAvgPool2d(1)
        self.fc = nn.Sequential(
            nn.Linear(ch, ch // reduction_ratio, bias=False),
            nn.ReLU(inplace=True),
            nn.Linear(ch // reduction_ratio, ch, bias=False),
            nn.Sigmoid()
        )
 
    def forward(self, x):
        b, c, _, _ = x.size()
        y = self.avg_pool(x).view(b, c)
        y = self.fc(y).view(b, c, 1, 1)
        return x * y.expand_as(x)
 
class BasicBlock(nn.Module):
    expansion = 1
 
    def __init__(self,
                 ch_in,
                 ch_out,
                 stride,
                 shortcut,
                 act='relu',
                 variant='b',
                 att=False):
        super(BasicBlock, self).__init__()
        self.shortcut = shortcut
        if not shortcut:
            if variant == 'd' and stride == 2:
                self.short = nn.Sequential()
                self.short.add_sublayer(
                    'pool',
                    nn.AvgPool2d(
                        kernel_size=2, stride=2, padding=0, ceil_mode=True))
                self.short.add_sublayer(
                    'conv',
                    ConvNormLayer(
                        ch_in=ch_in,
                        ch_out=ch_out,
                        filter_size=1,
                        stride=1))
            else:
                self.short = ConvNormLayer(
                    ch_in=ch_in,
                    ch_out=ch_out,
                    filter_size=1,
                    stride=stride)
 
        self.branch2a = ConvNormLayer(
            ch_in=ch_in,
            ch_out=ch_out,
            filter_size=3,
            stride=stride,
            act='relu')
 
        self.branch2b = ConvNormLayer(
            ch_in=ch_out,
            ch_out=ch_out,
            filter_size=3,
            stride=1,
            act=None)
 
        self.att = att
        if self.att:
            self.se = SELayer(ch_out)
 
    def forward(self, inputs):
        out = self.branch2a(inputs)
        out = self.branch2b(out)
 
        if self.att:
            out = self.se(out)
 
        if self.shortcut:
            short = inputs
        else:
            short = self.short(inputs)
 
        out = out + short
        out = F.relu(out)
 
        return out
 
class BottleNeck(nn.Module):
    expansion = 4
 
    def __init__(self, ch_in, ch_out, stride, shortcut, act='relu', variant='d', att=False):
        super().__init__()
 
        if variant == 'a':
            stride1, stride2 = stride, 1
        else:
            stride1, stride2 = 1, stride
 
        width = ch_out
 
        self.branch2a = ConvNormLayer(ch_in, width, 1, stride1, act=act)
        self.branch2b = ConvNormLayer(width, width, 3, stride2, act=act)
        self.branch2c = ConvNormLayer(width, ch_out * self.expansion, 1, 1)
 
        self.shortcut = shortcut
        if not shortcut:
            if variant == 'd' and stride == 2:
                self.short = nn.Sequential(OrderedDict([
                    ('pool', nn.AvgPool2d(2, 2, 0, ceil_mode=True)),
                    ('conv', ConvNormLayer(ch_in, ch_out * self.expansion, 1, 1))
                ]))
            else:
                self.short = ConvNormLayer(ch_in, ch_out * self.expansion, 1, stride)
 
        self.att = att
        if self.att:
            self.se = SELayer(ch_out * 4)
 
    def forward(self, x):
        out = self.branch2a(x)
        out = self.branch2b(out)
        out = self.branch2c(out)
 
        if self.att:
            out = self.se(out)
 
        if self.shortcut:
            short = x
        else:
            short = self.short(x)
 
        out = out + short
        out = F.relu(out)
 
        return out
 
class Blocks(nn.Module):
    def __init__(self,
                 ch_in,
                 ch_out,
                 count,
                 block,
                 stage_num,
                 att=False,
                 variant='b'):
        super(Blocks, self).__init__()
        self.blocks = nn.ModuleList()
        block = globals()[block]
        for i in range(count):
            self.blocks.append(
                block(
                    ch_in,
                    ch_out,
                    stride=2 if i == 0 and stage_num != 2 else 1,
                    shortcut=False if i == 0 else True,
                    variant=variant,
                    att=att)
            )
            if i == 0:
                ch_in = ch_out * block.expansion
 
    def forward(self, inputs):
        block_out = inputs
        for block in self.blocks:
            block_out = block(block_out)
        return block_out

③ 在 AddModules 文件夹下新建 __init__.py (已有则不用新建),在文件内导入模块: from .ResNet import *

在这里插入图片描述

④ 在 ultralytics/nn/modules/tasks.py 文件中,需要在下方位置添加各模块类名称。

首先:导入模块

在这里插入图片描述

然后,在 parse_model函数 中添加如下代码,并注册 Blocks , ConvNormLayer

# args = [c1, c2, *args[1:]]  # 这行原始代码需注释
if m is Blocks:
    if args[1][:10] in 'BottleNeck':
        c1, c2 = ch[f], args[0] * 4
        args = [c1, args[0], *args[1:]]
    else:
        args = [c1, c2, *args[1:]]
else:
    args = [c1, c2, *args[1:]]

在这里插入图片描述

⑤ 在 ultralytics/nn/modules/block.py 中找到 RepC3 模块,注释掉并替换成如下代码,否则无法进行缩放。此时替换后在其它大小的模型中也是通用的。

class RepC3(nn.Module):
    """Rep C3."""
 
    def __init__(self, c1, c2, n=3, e=1.0):
        """Initialize CSP Bottleneck with a single convolution using input channels, output channels, and number."""
        super().__init__()
        c_ = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c1, c_, 1, 1)
        self.m = nn.Sequential(*[RepConv(c_, c_) for _ in range(n)])
        self.cv3 = Conv(c_, c2, 1, 1) if c_ != c2 else nn.Identity()
 
    def forward(self, x):
        """Forward pass of RT-DETR neck layer."""
        return self.cv3(self.m(self.cv1(x)) + self.cv2(x))

在这里插入图片描述

⑥ 创建一个用于自己数据集训练的模型文件 rtdetr-resnet18.yaml ,粘贴如下结构:

# Ultralytics YOLO 🚀, AGPL-3.0 license
# RT-DETR-ResNet50 object detection model with P3-P5 outputs.

# Parameters
nc: 80 # 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, ConvNormLayer, [32, 3, 2, 1, 'relu']] # 0-P1
  - [-1, 1, ConvNormLayer, [32, 3, 1, 1, 'relu']] # 1
  - [-1, 1, ConvNormLayer, [64, 3, 1, 1, 'relu']] # 2
  - [-1, 1, nn.MaxPool2d, [3, 2, 1]] # 3-P2
 
  - [-1, 2, Blocks, [64,  BasicBlock, 2, False]] # 4
  - [-1, 2, Blocks, [128, BasicBlock, 3, False]] # 5-P3
  - [-1, 2, Blocks, [256, BasicBlock, 4, False]] # 6-P4
  - [-1, 2, Blocks, [512, BasicBlock, 5, False]] # 7-P5
 
head:
  - [-1, 1, Conv, [256, 1, 1, None, 1, 1, False]]  # 8 input_proj.2
  - [-1, 1, AIFI, [1024, 8]]
  - [-1, 1, Conv, [256, 1, 1]]  # 10, Y5, lateral_convs.0
 
  - [-1, 1, nn.Upsample, [None, 2, 'nearest']] # 11
  - [6, 1, Conv, [256, 1, 1, None, 1, 1, False]]  # 12 input_proj.1
  - [[-2, -1], 1, Concat, [1]]
  - [-1, 3, RepC3, [256, 0.5]]  # 14, fpn_blocks.0
  - [-1, 1, Conv, [256, 1, 1]]  # 15, Y4, lateral_convs.1
 
  - [-1, 1, nn.Upsample, [None, 2, 'nearest']] # 16
  - [5, 1, Conv, [256, 1, 1, None, 1, 1, False]]  # 17 input_proj.0
  - [[-2, -1], 1, Concat, [1]]  # 18 cat backbone P4
  - [-1, 3, RepC3, [256, 0.5]]  # X3 (19), fpn_blocks.1
 
  - [-1, 1, Conv, [256, 3, 2]]  # 20, downsample_convs.0
  - [[-1, 15], 1, Concat, [1]]  # 21 cat Y4
  - [-1, 3, RepC3, [256, 0.5]]  # F4 (22), pan_blocks.0
 
  - [-1, 1, Conv, [256, 3, 2]]  # 23, downsample_convs.1
  - [[-1, 10], 1, Concat, [1]]  # 24 cat Y5
  - [-1, 3, RepC3, [256, 0.5]]  # F5 (25), pan_blocks.1
 
  - [[19, 22, 25], 1, RTDETRDecoder, [nc, 256, 300, 4, 8, 3]]  # Detect(P3, P4, P5)

运行结果:

                   from  n    params  module                                       arguments                     
  0                  -1  1       960  ultralytics.nn.AddModules.ResNet.ConvNormLayer[3, 32, 3, 2, 1, 'relu']      
  1                  -1  1      9312  ultralytics.nn.AddModules.ResNet.ConvNormLayer[32, 32, 3, 1, 1, 'relu']     
  2                  -1  1     18624  ultralytics.nn.AddModules.ResNet.ConvNormLayer[32, 64, 3, 1, 1, 'relu']     
  3                  -1  1         0  torch.nn.modules.pooling.MaxPool2d           [3, 2, 1]                     
  4                  -1  2    152512  ultralytics.nn.AddModules.ResNet.Blocks      [64, 64, 2, 'BasicBlock', 2, False]
  5                  -1  2    526208  ultralytics.nn.AddModules.ResNet.Blocks      [64, 128, 2, 'BasicBlock', 3, False]
  6                  -1  2   2100992  ultralytics.nn.AddModules.ResNet.Blocks      [128, 256, 2, 'BasicBlock', 4, False]
  7                  -1  2   8396288  ultralytics.nn.AddModules.ResNet.Blocks      [256, 512, 2, 'BasicBlock', 5, False]
  8                  -1  1    131584  ultralytics.nn.modules.conv.Conv             [512, 256, 1, 1, None, 1, 1, False]
  9                  -1  1    789760  ultralytics.nn.modules.transformer.AIFI      [256, 1024, 8]                
 10                  -1  1     66048  ultralytics.nn.modules.conv.Conv             [256, 256, 1, 1]              
 11                  -1  1         0  torch.nn.modules.upsampling.Upsample         [None, 2, 'nearest']          
 12                   6  1     66048  ultralytics.nn.modules.conv.Conv             [256, 256, 1, 1, None, 1, 1, False]
 13            [-2, -1]  1         0  ultralytics.nn.modules.conv.Concat           [1]                           
 14                  -1  3    657920  ultralytics.nn.modules.block.RepC3           [512, 256, 3, 0.5]            
 15                  -1  1     66048  ultralytics.nn.modules.conv.Conv             [256, 256, 1, 1]              
 16                  -1  1         0  torch.nn.modules.upsampling.Upsample         [None, 2, 'nearest']          
 17                   5  1     33280  ultralytics.nn.modules.conv.Conv             [128, 256, 1, 1, None, 1, 1, False]
 18            [-2, -1]  1         0  ultralytics.nn.modules.conv.Concat           [1]                           
 19                  -1  3    657920  ultralytics.nn.modules.block.RepC3           [512, 256, 3, 0.5]            
 20                  -1  1    590336  ultralytics.nn.modules.conv.Conv             [256, 256, 3, 2]              
 21            [-1, 15]  1         0  ultralytics.nn.modules.conv.Concat           [1]                           
 22                  -1  3    657920  ultralytics.nn.modules.block.RepC3           [512, 256, 3, 0.5]            
 23                  -1  1    590336  ultralytics.nn.modules.conv.Conv             [256, 256, 3, 2]              
 24            [-1, 10]  1         0  ultralytics.nn.modules.conv.Concat           [1]                           
 25                  -1  3    657920  ultralytics.nn.modules.block.RepC3           [512, 256, 3, 0.5]            
 26        [19, 22, 25]  1   3917684  ultralytics.nn.modules.head.RTDETRDecoder    [1, [256, 256, 256], 256, 300, 4, 8, 3]
rtdetr-resnet18 summary: 389 layers, 20,087,700 parameters, 20,087,700 gradients, 58.3 GFLOPs

六、rtdetr-resnet34配置

rtdetr-resnet34.yaml 模型结构如下:


# 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: 80  # 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, ConvNormLayer, [32, 3, 2, 1, 'relu']] # 0-P1
  - [-1, 1, ConvNormLayer, [32, 3, 1, 1, 'relu']] # 1
  - [-1, 1, ConvNormLayer, [64, 3, 1, 1, 'relu']] # 2
  - [-1, 1, nn.MaxPool2d, [3, 2, 1]] # 3-P2

  - [-1, 3, Blocks, [64,  BasicBlock, 2, False]] # 4
  - [-1, 4, Blocks, [128, BasicBlock, 3, False]] # 5-P3
  - [-1, 6, Blocks, [256, BasicBlock, 4, False]] # 6-P4
  - [-1, 3, Blocks, [512, BasicBlock, 5, False]] # 7-P5
 
head:
  - [-1, 1, Conv, [256, 1, 1, None, 1, 1, False]]  # 8 input_proj.2
  - [-1, 1, AIFI, [1024, 8]]
  - [-1, 1, Conv, [256, 1, 1]]  # 10, Y5, lateral_convs.0
 
  - [-1, 1, nn.Upsample, [None, 2, 'nearest']] # 11
  - [6, 1, Conv, [256, 1, 1, None, 1, 1, False]]  # 12 input_proj.1
  - [[-2, -1], 1, Concat, [1]]
  - [-1, 3, RepC3, [256, 0.5]]  # 14, fpn_blocks.0
  - [-1, 1, Conv, [256, 1, 1]]  # 15, Y4, lateral_convs.1
 
  - [-1, 1, nn.Upsample, [None, 2, 'nearest']] # 16
  - [5, 1, Conv, [256, 1, 1, None, 1, 1, False]]  # 17 input_proj.0
  - [[-2, -1], 1, Concat, [1]]  # 18 cat backbone P4
  - [-1, 3, RepC3, [256, 0.5]]  # X3 (19), fpn_blocks.1
 
  - [-1, 1, Conv, [256, 3, 2]]  # 20, downsample_convs.0
  - [[-1, 15], 1, Concat, [1]]  # 21 cat Y4
  - [-1, 3, RepC3, [256, 0.5]]  # F4 (22), pan_blocks.0
 
  - [-1, 1, Conv, [256, 3, 2]]  # 23, downsample_convs.1
  - [[-1, 10], 1, Concat, [1]]  # 24 cat Y5
  - [-1, 3, RepC3, [256, 0.5]]  # F5 (25), pan_blocks.1
 
  - [[19, 22, 25], 1, RTDETRDecoder, [nc, 256, 300, 4, 8, 4]]  # Detect(P3, P4, P5)

运行结果:

                   from  n    params  module                                       arguments                     
  0                  -1  1       960  ultralytics.nn.AddModules.ResNet.ConvNormLayer[3, 32, 3, 2, 1, 'relu']      
  1                  -1  1      9312  ultralytics.nn.AddModules.ResNet.ConvNormLayer[32, 32, 3, 1, 1, 'relu']     
  2                  -1  1     18624  ultralytics.nn.AddModules.ResNet.ConvNormLayer[32, 64, 3, 1, 1, 'relu']     
  3                  -1  1         0  torch.nn.modules.pooling.MaxPool2d           [3, 2, 1]                     
  4                  -1  3    226624  ultralytics.nn.AddModules.ResNet.Blocks      [64, 64, 3, 'BasicBlock', 2, False]
  5                  -1  4   1117568  ultralytics.nn.AddModules.ResNet.Blocks      [64, 128, 4, 'BasicBlock', 3, False]
  6                  -1  6   6825728  ultralytics.nn.AddModules.ResNet.Blocks      [128, 256, 6, 'BasicBlock', 4, False]
  7                  -1  3  13117952  ultralytics.nn.AddModules.ResNet.Blocks      [256, 512, 3, 'BasicBlock', 5, False]
  8                  -1  1    131584  ultralytics.nn.modules.conv.Conv             [512, 256, 1, 1, None, 1, 1, False]
  9                  -1  1    789760  ultralytics.nn.modules.transformer.AIFI      [256, 1024, 8]                
 10                  -1  1     66048  ultralytics.nn.modules.conv.Conv             [256, 256, 1, 1]              
 11                  -1  1         0  torch.nn.modules.upsampling.Upsample         [None, 2, 'nearest']          
 12                   6  1     66048  ultralytics.nn.modules.conv.Conv             [256, 256, 1, 1, None, 1, 1, False]
 13            [-2, -1]  1         0  ultralytics.nn.modules.conv.Concat           [1]                           
 14                  -1  3    657920  ultralytics.nn.modules.block.RepC3           [512, 256, 3, 0.5]            
 15                  -1  1     66048  ultralytics.nn.modules.conv.Conv             [256, 256, 1, 1]              
 16                  -1  1         0  torch.nn.modules.upsampling.Upsample         [None, 2, 'nearest']          
 17                   5  1     33280  ultralytics.nn.modules.conv.Conv             [128, 256, 1, 1, None, 1, 1, False]
 18            [-2, -1]  1         0  ultralytics.nn.modules.conv.Concat           [1]                           
 19                  -1  3    657920  ultralytics.nn.modules.block.RepC3           [512, 256, 3, 0.5]            
 20                  -1  1    590336  ultralytics.nn.modules.conv.Conv             [256, 256, 3, 2]              
 21            [-1, 15]  1         0  ultralytics.nn.modules.conv.Concat           [1]                           
 22                  -1  3    657920  ultralytics.nn.modules.block.RepC3           [512, 256, 3, 0.5]            
 23                  -1  1    590336  ultralytics.nn.modules.conv.Conv             [256, 256, 3, 2]              
 24            [-1, 10]  1         0  ultralytics.nn.modules.conv.Concat           [1]                           
 25                  -1  3    657920  ultralytics.nn.modules.block.RepC3           [512, 256, 3, 0.5]            
 26        [19, 22, 25]  1   5046425  ultralytics.nn.modules.head.RTDETRDecoder    [1, [256, 256, 256], 256, 300, 4, 8, 4]
rtdetr-resnet34 summary: 470 layers, 31,328,313 parameters, 31,328,313 gradients, 90.2 GFLOPs

后续会讲解如何在 BasicBlock 模块中融入本专栏中的模块~