【视频讲解】顶级期刊即插即用模块代码共享计划·2024第一期:02:非相邻层次间语义信息渐进式特征融合策略(2024 1区Top | 浙江大学开源)代码实现

视频讲解

【布尔大学士】啊!浙大大学一区Top!15分钟速通非相邻层次间语义信息渐进式特征融合策略~~~~

Asymptotic Feature Pyramid Network for Labeling Pixels and Regions 用于标记像素和区域的渐近特征金字塔网络(2024-1区)

论文地址:https://arxiv.org/pdf/2306.15988

在这里插入图片描述

1、摘要

在视觉任务中,多尺度特征对于编码不同尺度的物体至关重要。经典的自上而下和自下而上的特征金字塔网络是多尺度特征提取的常用策略。然而,这些方法都存在特征信息丢失或退化的问题,从而影响了非相邻层次的融合效果。

本文提出了一种渐近特征金字塔网络(AFPN),用于解决经典特征金字塔网络中非相邻层次间信息损失或降级的问题。

AFPN通过融合相邻低层特征开始,并逐步将更高层次的特征纳入融合过程,从而避免了不同层次间的较大语义差距。此外,为了缓解多对象信息冲突,引入了自适应空间融合操作。

2、背景

2.1 引言与现有工作存在的问题

传统的物体检测方法通常只从图像中提取单一尺度的特征,这限制了不同大小或场景下的物体检测性能。为了有效地编码具有尺度变化的对象,多尺度特征提取变得至关重要。

  • 一种常见的策略是采用经典的自顶向下和自底向上的特征金字塔网络。然而,这些方法在非相邻层次之间的特征融合中可能会导致信息损失或降级。
  • 为了解决这一局限性,GraphFPN [18] 引入了图神经网络,以实现非相邻尺度特征之间的直接交互。然而,额外的图神经网络结构大大增加了模型的参数和计算量,得不偿失。现有的特征金字塔网络通常会将骨干网络中的高级特征向上采样为低级特征。
  • 现有的特征金字塔架构要求高级特征通过多个中间尺度传播,并在与低级特征融合之前与这些尺度的特征交互。然而,这种传播和交互过程可能会导致高级特征的语义信息或低级特征的详细信息丢失或退化。

2.2 启发来源

HRNet [25], [26]在特征提取过程中会保留低层次特征,并反复合并低层次特征和高层次特征,以生成更丰富的低层次特征。这种方法在人体姿态估计方面取得了卓越的成果。

HRNet如下图所示

论文名称: Deep High-Resolution Representation Learning for Human Pose Estimation
论文下载地址:https://arxiv.org/abs/1902.09212
在这里插入图片描述

)

3、创新点与解决问题

AFPN的主要贡献在于:

  1. 渐近融合:AFPN支持非相邻层次之间直接的特征融合,避免了信息传输和交互过程中的损失或降级。
  2. 自适应空间融合:在多层特征融合过程中引入自适应空间融合操作,以抑制不同层次特征间的信息矛盾。
  3. 实验结果:AFPN在Faster R-CNN和Dynamic R-CNN等两阶段检测器以及YOLOv5等单阶段检测器中都表现出显著的性能提升,特别是在大目标检测方面。

3.1 . Asymptotic Architecture

非相邻分层特征之间的语义差距大于相邻分层特征之间的语义差距,尤其是底部和顶部特征。这就导致直接使用非相邻层次特征的融合效果不佳。因此,直接使用 C2、C3、C4 和 C5 进行特征融合是不合理的。AFPN 的架构如图 2 所示。

在骨干网络自下而上的特征提取过程中,AFPN 对低层、高层和顶层特征进行渐进式融合。具体来说,AFPN 首先融合低层次特征,然后融合深层次特征,最后融合最顶层的特征,即最抽象的特征。

在这里插入图片描述

由于 AFPN 的架构是渐近式的,这将使不同层次特征的语义信息在渐近式融合过程中更加接近,从而缓解上述问题。例如,C2 和 C3 之间的特征融合可以缩小它们之间的语义差距。由于 C3 和 C4 是相邻的分层特征,因此 C2 和 C4 之间的语义差距会减小。

为了对齐维度并为特征融合做准备,我们利用 1×1 卷积和双线性插值方法对特征进行上采样。另一方面,我们根据所需的下采样率,使用不同的卷积核和跨度进行下采样。例如,我们使用步长为 2 的 2×2 卷积来实现 2 倍降采样,使用步长为 4 的 4×4 卷积来实现 4 倍降采样,使用步长为 8 的 8×8 卷积来实现 8 倍降采样。

特征融合后,我们继续使用四个残差块学习特征,这与 ResNet [36] 类似。每个残差块由两个 3 × 3 卷积组成。由于 YOLO 只使用三级特征,因此不存在 8 次上采样和 8 次下采样。

3.2 . Adaptive spatial fusion

在多层次特征融合过程中,我们利用 ASFF [32],为不同层次的特征分配不同的空间权重,从而增强关键层次的重要性,并减轻来自不同对象的矛盾信息的影响。如图 3 所示,我们对三个层次的特征进行融合。

在这里插入图片描述

4、实验

MS COCO

如表 I 所示,当输入图像尺寸为 640×640 时,我们的方法取得了很好的性能,AP 为 39.0%,甚至超过了一些更大分辨率的模型。

在这里插入图片描述

PASVAL VOC

在这里插入图片描述

Two-stage Detectors

在这里插入图片描述

One-stage Detectors

AFPN 在 YOLOv5 上取得了更好的性能,同时使用了更少的参数。

在这里插入图片描述

Fusion Operation

为了研究自适应空间融合操作在我们的 AFPN 中的功效,我们在消融研究中用另外两种融合操作(即元素相加和元素相联)取代了自适应空间融合操作。我们的实验采用了以 ResNet50 为骨干的 Faster R-CNN 框架。

在这里插入图片描述

Number of Blocks

为了确定最佳块数,对不同数量的块进行了实验并记录了相应的性能指标,如表 X 所示。实验结果表明,在 AFPN 中,当块数从 1 增加到 4 时,AP 明显增加了 1.2%。然而,当区块数增加到 5 和 6 时,模型的准确度开始下降。同样,在 LightAFPN 中,当块的数量从 1 增加到 2 时,精度有所提高,但当块的数量增加到 3 时,精度略有下降,当块的数量增加到 4 时,与最佳的 2 块数量相比,精度下降了 0.3%。

因此得出结论:虽然增加块数可以提高模型的准确性,但过多的块数会导致准确性下降。基于这些结论,为 AFPN 选择了 4 个残差块,为 LightAFPN 选择了 2 个轻量级块,从而实现了最佳性能。

5、结论

在本文中,我们提出了渐近特征金字塔网络(AFPN)来解决非相邻层级之间的间接交互导致的信息丢失和退化问题。我们的 AFPN 使用渐近方式进行特征融合和自适应空间融合操作,以在融合过程中提取更多有用信息。

对于轻量级 AFPN,我们进一步提出了受重新参数化启发的轻量级渐近特征金字塔网络(LightAFPN)。与 AFPN 相比,它具有更少的参数、更少的计算和更快的推理速度。

大量实验结果表明,与各种检测和分割框架中的基线方法相比,我们的方法具有更优异的性能。未来,我们将探索我们的方法在其他视觉任务中的适用性。

6、即插即用代码

from collections import OrderedDict

import torch
import torch.nn as nn
import torch.nn.functional as F

# 论文:https://arxiv.org/pdf/2306.15988
# 题目:Asymptotic Feature Pyramid Network for Labeling Pixels and Regions(2024-1区)
# 中文:用于标记像素和区域的渐近特征金字塔网络(2024-1区)

def BasicConv(filter_in, filter_out, kernel_size, stride=1, pad=None):
    # 如果没有指定填充(pad),则根据卷积核的大小计算默认的填充
    if not pad:
        # 当卷积核大小为奇数时,计算两边对称的填充值
        pad = (kernel_size - 1) // 2 if kernel_size else 0
    else:
        # 如果指定了填充,则使用指定的填充值
        pad = pad

    # 使用nn.Sequential创建一个有序字典形式的序列模型
    return nn.Sequential(OrderedDict([
        # 卷积层
        ("conv", nn.Conv2d(in_channels=filter_in, out_channels=filter_out,
                           kernel_size=kernel_size, stride=stride, padding=pad, bias=False)),
        # 批量归一化层
        ("bn", nn.BatchNorm2d(num_features=filter_out)),
        # ReLU激活函数
        ("relu", nn.ReLU(inplace=True)),
    ]))

class BasicBlock(nn.Module):
    def __init__(self, filter_in, filter_out):
        # 初始化父类 nn.Module
        super(BasicBlock, self).__init__()
        # 定义第一个卷积层
        self.conv1 = nn.Conv2d(in_channels=filter_in, out_channels=filter_out, kernel_size=3, padding=1)
        # 定义第一个批量归一化层
        self.bn1 = nn.BatchNorm2d(num_features=filter_out, momentum=0.1)
        # 定义 ReLU 激活函数
        self.relu = nn.ReLU(inplace=True)
        # 定义第二个卷积层
        self.conv2 = nn.Conv2d(in_channels=filter_out, out_channels=filter_out, kernel_size=3, padding=1)
        # 定义第二个批量归一化层
        self.bn2 = nn.BatchNorm2d(num_features=filter_out, momentum=0.1)

    def forward(self, x):
        # 保存输入作为残差连接
        residual = x
        # 第一层卷积 + 批量归一化 + ReLU 激活
        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu(out)
        # 第二层卷积 + 批量归一化
        out = self.conv2(out)
        out = self.bn2(out)
        # 残差连接
        out += residual
        # 最后一次 ReLU 激活
        out = self.relu(out)
        # 返回最终输出
        return out

class Upsample(nn.Module):
    def __init__(self, in_channels, out_channels, scale_factor=2):
        super(Upsample, self).__init__()

        # 定义上采样层,先进行1x1卷积调整通道数,再进行双线性插值上采样
        self.upsample = nn.Sequential(
            BasicConv(in_channels, out_channels, 1),  # 1x1卷积
            nn.Upsample(scale_factor=scale_factor, mode='bilinear')  # 上采样
        )

    def forward(self, x):
        # 通过上采样层
        x = self.upsample(x)

        return x

class Downsample_x2(nn.Module):
    def __init__(self, in_channels, out_channels):
        super(Downsample_x2, self).__init__()

        # 定义下采样层,使用步长为2的卷积实现2倍下采样
        self.downsample = nn.Sequential(
            BasicConv(in_channels, out_channels, 2, 2, 0)  # 2x2卷积,步长2
        )

    def forward(self, x):
        # 通过下采样层
        x = self.downsample(x)

        return x

class Downsample_x4(nn.Module):
    def __init__(self, in_channels, out_channels):
        super(Downsample_x4, self).__init__()

        # 定义下采样层,使用步长为4的卷积实现4倍下采样
        self.downsample = nn.Sequential(
            BasicConv(in_channels, out_channels, 4, 4, 0)  # 4x4卷积,步长4
        )

    def forward(self, x):
        # 通过下采样层
        x = self.downsample(x)

        return x

class Downsample_x8(nn.Module):
    def __init__(self, in_channels, out_channels):
        super(Downsample_x8, self).__init__()

        # 定义下采样层,使用步长为8的卷积实现8倍下采样
        self.downsample = nn.Sequential(
            BasicConv(in_channels, out_channels, 8, 8, 0)  # 8x8卷积,步长8
        )

    def forward(self, x):
        # 通过下采样层
        x = self.downsample(x)

        return x

class ASFF_2(nn.Module):
    def __init__(self, inter_dim=512):
        super(ASFF_2, self).__init__()

        self.inter_dim = inter_dim
        compress_c = 8

        # 定义两个压缩卷积层
        self.weight_level_1 = BasicConv(self.inter_dim, compress_c, 1, 1)
        self.weight_level_2 = BasicConv(self.inter_dim, compress_c, 1, 1)

        # 定义权重融合层
        self.weight_levels = nn.Conv2d(compress_c * 2, 2, kernel_size=1, stride=1, padding=0)

        # 定义融合后的卷积层
        self.conv = BasicConv(self.inter_dim, self.inter_dim, 3, 1)

    def forward(self, input1, input2):
        # 对两个输入特征图进行压缩
        level_1_weight_v = self.weight_level_1(input1)
        level_2_weight_v = self.weight_level_2(input2)

        # 将压缩后的特征图拼接
        levels_weight_v = torch.cat((level_1_weight_v, level_2_weight_v), 1)

        # 计算每个级别的权重
        levels_weight = self.weight_levels(levels_weight_v)
        levels_weight = F.softmax(levels_weight, dim=1)

        # 根据权重融合特征图
        fused_out_reduced = input1 * levels_weight[:, 0:1, :, :] + \
                            input2 * levels_weight[:, 1:2, :, :]

        # 通过卷积层进一步处理融合后的特征图
        out = self.conv(fused_out_reduced)

        return out

class ASFF_3(nn.Module):
    def __init__(self, inter_dim=512):
        super(ASFF_3, self).__init__()

        self.inter_dim = inter_dim
        compress_c = 8

        # 定义三个压缩卷积层
        self.weight_level_1 = BasicConv(self.inter_dim, compress_c, 1, 1)
        self.weight_level_2 = BasicConv(self.inter_dim, compress_c, 1, 1)
        self.weight_level_3 = BasicConv(self.inter_dim, compress_c, 1, 1)

        # 定义权重融合层
        self.weight_levels = nn.Conv2d(compress_c * 3, 3, kernel_size=1, stride=1, padding=0)

        # 定义融合后的卷积层
        self.conv = BasicConv(self.inter_dim, self.inter_dim, 3, 1)

    def forward(self, input1, input2, input3):
        # 对三个输入特征图进行压缩
        level_1_weight_v = self.weight_level_1(input1)
        level_2_weight_v = self.weight_level_2(input2)
        level_3_weight_v = self.weight_level_3(input3)

        # 将压缩后的特征图拼接
        levels_weight_v = torch.cat((level_1_weight_v, level_2_weight_v, level_3_weight_v), 1)

        # 计算每个级别的权重
        levels_weight = self.weight_levels(levels_weight_v)
        levels_weight = F.softmax(levels_weight, dim=1)

        # 根据权重融合特征图
        fused_out_reduced = input1 * levels_weight[:, 0:1, :, :] + \
                            input2 * levels_weight[:, 1:2, :, :] + \
                            input3 * levels_weight[:, 2:, :, :]

        # 通过卷积层进一步处理融合后的特征图
        out = self.conv(fused_out_reduced)

        return out

class ASFF_4(nn.Module):
    def __init__(self, inter_dim=512):
        super(ASFF_4, self).__init__()

        self.inter_dim = inter_dim
        compress_c = 8

        # 定义四个压缩卷积层
        self.weight_level_0 = BasicConv(self.inter_dim, compress_c, 1, 1)
        self.weight_level_1 = BasicConv(self.inter_dim, compress_c, 1, 1)
        self.weight_level_2 = BasicConv(self.inter_dim, compress_c, 1, 1)
        self.weight_level_3 = BasicConv(self.inter_dim, compress_c, 1, 1)

        # 定义权重融合层
        self.weight_levels = nn.Conv2d(compress_c * 4, 4, kernel_size=1, stride=1, padding=0)

        # 定义融合后的卷积层
        self.conv = BasicConv(self.inter_dim, self.inter_dim, 3, 1)

    def forward(self, input0, input1, input2, input3):
        # 对四个输入特征图进行压缩
        level_0_weight_v = self.weight_level_0(input0)
        level_1_weight_v = self.weight_level_1(input1)
        level_2_weight_v = self.weight_level_2(input2)
        level_3_weight_v = self.weight_level_3(input3)

        # 将压缩后的特征图拼接
        levels_weight_v = torch.cat((level_0_weight_v, level_1_weight_v, level_2_weight_v, level_3_weight_v), 1)

        # 计算每个级别的权重
        levels_weight = self.weight_levels(levels_weight_v)
        levels_weight = F.softmax(levels_weight, dim=1)

        # 根据权重融合特征图
        fused_out_reduced = input0 * levels_weight[:, 0:1, :, :] + \
                            input1 * levels_weight[:, 1:2, :, :] + \
                            input2 * levels_weight[:, 2:3, :, :] + \
                            input3 * levels_weight[:, 3:, :, :]

        # 通过卷积层进一步处理融合后的特征图
        out = self.conv(fused_out_reduced)

        return out

import torch.nn as nn

class BlockBody(nn.Module):
    def __init__(self, channels=[64, 128, 256, 512]):
        # 初始化父类 nn.Module
        super(BlockBody, self).__init__()

        # 定义四个1x1卷积层,用于通道调整
        self.blocks_scalezero1 = nn.Sequential(
            BasicConv(channels[0], channels[0], 1),
        )
        self.blocks_scaleone1 = nn.Sequential(
            BasicConv(channels[1], channels[1], 1),
        )
        self.blocks_scaletwo1 = nn.Sequential(
            BasicConv(channels[2], channels[2], 1),
        )
        self.blocks_scalethree1 = nn.Sequential(
            BasicConv(channels[3], channels[3], 1),
        )

        # 定义2倍下采样和2倍上采样操作
        self.downsample_scalezero1_2 = Downsample_x2(channels[0], channels[1])
        self.upsample_scaleone1_2 = Upsample(channels[1], channels[0], scale_factor=2)

        # 定义两个特征融合模块 ASFF_2
        self.asff_scalezero1 = ASFF_2(inter_dim=channels[0])
        self.asff_scaleone1 = ASFF_2(inter_dim=channels[1])

        # 定义两个残差块序列
        self.blocks_scalezero2 = nn.Sequential(
            BasicBlock(channels[0], channels[0]),
            BasicBlock(channels[0], channels[0]),
            BasicBlock(channels[0], channels[0]),
            BasicBlock(channels[0], channels[0]),
        )
        self.blocks_scaleone2 = nn.Sequential(
            BasicBlock(channels[1], channels[1]),
            BasicBlock(channels[1], channels[1]),
            BasicBlock(channels[1], channels[1]),
            BasicBlock(channels[1], channels[1]),
        )

        # 定义多个上采样和下采样操作
        self.downsample_scalezero2_2 = Downsample_x2(channels[0], channels[1])
        self.downsample_scalezero2_4 = Downsample_x4(channels[0], channels[2])
        self.downsample_scaleone2_2 = Downsample_x2(channels[1], channels[2])
        self.upsample_scaleone2_2 = Upsample(channels[1], channels[0], scale_factor=2)
        self.upsample_scaletwo2_2 = Upsample(channels[2], channels[1], scale_factor=2)
        self.upsample_scaletwo2_4 = Upsample(channels[2], channels[0], scale_factor=4)

        # 定义三个特征融合模块 ASFF_3
        self.asff_scalezero2 = ASFF_3(inter_dim=channels[0])
        self.asff_scaleone2 = ASFF_3(inter_dim=channels[1])
        self.asff_scaletwo2 = ASFF_3(inter_dim=channels[2])

        # 定义三个残差块序列
        self.blocks_scalezero3 = nn.Sequential(
            BasicBlock(channels[0], channels[0]),
            BasicBlock(channels[0], channels[0]),
            BasicBlock(channels[0], channels[0]),
            BasicBlock(channels[0], channels[0]),
        )
        self.blocks_scaleone3 = nn.Sequential(
            BasicBlock(channels[1], channels[1]),
            BasicBlock(channels[1], channels[1]),
            BasicBlock(channels[1], channels[1]),
            BasicBlock(channels[1], channels[1]),
        )
        self.blocks_scaletwo3 = nn.Sequential(
            BasicBlock(channels[2], channels[2]),
            BasicBlock(channels[2], channels[2]),
            BasicBlock(channels[2], channels[2]),
            BasicBlock(channels[2], channels[2]),
        )

        # 定义多个上采样和下采样操作
        self.downsample_scalezero3_2 = Downsample_x2(channels[0], channels[1])
        self.downsample_scalezero3_4 = Downsample_x4(channels[0], channels[2])
        self.downsample_scalezero3_8 = Downsample_x8(channels[0], channels[3])
        self.upsample_scaleone3_2 = Upsample(channels[1], channels[0], scale_factor=2)
        self.downsample_scaleone3_2 = Downsample_x2(channels[1], channels[2])
        self.downsample_scaleone3_4 = Downsample_x4(channels[1], channels[3])
        self.upsample_scaletwo3_4 = Upsample(channels[2], channels[0], scale_factor=4)
        self.upsample_scaletwo3_2 = Upsample(channels[2], channels[1], scale_factor=2)
        self.downsample_scaletwo3_2 = Downsample_x2(channels[2], channels[3])
        self.upsample_scalethree3_8 = Upsample(channels[3], channels[0], scale_factor=8)
        self.upsample_scalethree3_4 = Upsample(channels[3], channels[1], scale_factor=4)
        self.upsample_scalethree3_2 = Upsample(channels[3], channels[2], scale_factor=2)

        # 定义四个特征融合模块 ASFF_4
        self.asff_scalezero3 = ASFF_4(inter_dim=channels[0])
        self.asff_scaleone3 = ASFF_4(inter_dim=channels[1])
        self.asff_scaletwo3 = ASFF_4(inter_dim=channels[2])
        self.asff_scalethree3 = ASFF_4(inter_dim=channels[3])

        # 定义四个残差块序列
        self.blocks_scalezero4 = nn.Sequential(
            BasicBlock(channels[0], channels[0]),
            BasicBlock(channels[0], channels[0]),
            BasicBlock(channels[0], channels[0]),
            BasicBlock(channels[0], channels[0]),
        )
        self.blocks_scaleone4 = nn.Sequential(
            BasicBlock(channels[1], channels[1]),
            BasicBlock(channels[1], channels[1]),
            BasicBlock(channels[1], channels[1]),
            BasicBlock(channels[1], channels[1]),
        )
        self.blocks_scaletwo4 = nn.Sequential(
            BasicBlock(channels[2], channels[2]),
            BasicBlock(channels[2], channels[2]),
            BasicBlock(channels[2], channels[2]),
            BasicBlock(channels[2], channels[2]),
        )
        self.blocks_scalethree4 = nn.Sequential(
            BasicBlock(channels[3], channels[3]),
            BasicBlock(channels[3], channels[3]),
            BasicBlock(channels[3], channels[3]),
            BasicBlock(channels[3], channels[3]),
        )

    def forward(self, x):
        # 解包输入特征图
        x0, x1, x2, x3 = x

        # 通过1x1卷积层调整通道
        x0 = self.blocks_scalezero1(x0)
        x1 = self.blocks_scaleone1(x1)
        x2 = self.blocks_scaletwo1(x2)
        x3 = self.blocks_scalethree1(x3)

        # 特征融合:ASFF_2
        scalezero = self.asff_scalezero1(x0, self.upsample_scaleone1_2(x1))
        scaleone = self.asff_scaleone1(self.downsample_scalezero1_2(x0), x1)

        # 通过残差块序列
        x0 = self.blocks_scalezero2(scalezero)
        x1 = self.blocks_scaleone2(scaleone)

        # 特征融合:ASFF_3
        scalezero = self.asff_scalezero2(x0, self.upsample_scaleone2_2(x1), self.upsample_scaletwo2_4(x2))
        scaleone = self.asff_scaleone2(self.downsample_scalezero2_2(x0), x1, self.upsample_scaletwo2_2(x2))
        scaletwo = self.asff_scaletwo2(self.downsample_scalezero2_4(x0), self.downsample_scaleone2_2(x1), x2)

        # 通过残差块序列
        x0 = self.blocks_scalezero3(scalezero)
        x1 = self.blocks_scaleone3(scaleone)
        x2 = self.blocks_scaletwo3(scaletwo)

        # 特征融合:ASFF_4
        scalezero = self.asff_scalezero3(x0, self.upsample_scaleone3_2(x1), self.upsample_scaletwo3_4(x2), self.upsample_scalethree3_8(x3))
        scaleone = self.asff_scaleone3(self.downsample_scalezero3_2(x0), x1, self.upsample_scaletwo3_2(x2), self.upsample_scalethree3_4(x3))
        scaletwo = self.asff_scaletwo3(self.downsample_scalezero3_4(x0), self.downsample_scaleone3_2(x1), x2, self.upsample_scalethree3_2(x3))
        scalethree = self.asff_scalethree3(self.downsample_scalezero3_8(x0), self.downsample_scaleone3_4(x1), self.downsample_scaletwo3_2(x2), x3)

        # 通过残差块序列
        scalezero = self.blocks_scalezero4(scalezero)
        scaleone = self.blocks_scaleone4(scaleone)
        scaletwo = self.blocks_scaletwo4(scaletwo)
        scalethree = self.blocks_scalethree4(scalethree)

        # 返回最终融合后的特征图
        return scalezero, scaleone, scaletwo, scalethree

class AFPN(nn.Module):
    def __init__(self,
                 in_channels=[256, 512, 1024, 2048],  # 输入特征图的通道数列表
                 out_channels=256):  # 输出特征图的通道数
        super(AFPN, self).__init__()

        # 设置是否使用半精度浮点数 fp16
        self.fp16_enabled = False

        # 定义1x1卷积层,用于调整输入特征图的通道数
        self.conv0 = BasicConv(in_channels[0], in_channels[0] // 8, 1)
        self.conv1 = BasicConv(in_channels[1], in_channels[1] // 8, 1)
        self.conv2 = BasicConv(in_channels[2], in_channels[2] // 8, 1)
        self.conv3 = BasicConv(in_channels[3], in_channels[3] // 8, 1)

        # 定义 BlockBody 模块,用于多尺度特征融合
        self.body = nn.Sequential(
            BlockBody([in_channels[0] // 8, in_channels[1] // 8, in_channels[2] // 8, in_channels[3] // 8])
        )

        # 定义1x1卷积层,用于调整输出特征图的通道数到统一的 `out_channels`
        # in_channels[0] // 8 的目的是为了减少输入特征图的通道数,从而达到降低计算复杂度、减少模型参数以及优化特征表示的目的。
        self.conv00 = BasicConv(in_channels[0] // 8, out_channels, 1)
        self.conv11 = BasicConv(in_channels[1] // 8, out_channels, 1)
        self.conv22 = BasicConv(in_channels[2] // 8, out_channels, 1)
        self.conv33 = BasicConv(in_channels[3] // 8, out_channels, 1)
        self.conv44 = nn.MaxPool2d(kernel_size=1, stride=2)  # 用于生成额外的下采样特征图

        # 初始化权重
        for m in self.modules():
            if isinstance(m, nn.Conv2d):  # 如果是卷积层
                nn.init.xavier_normal_(m.weight, gain=0.02)  # 使用 Xavier 正态分布初始化权重
            elif isinstance(m, nn.BatchNorm2d):  # 如果是批量归一化层
                torch.nn.init.normal_(m.weight.data, 1.0, 0.02)  # 初始化权重为正态分布
                torch.nn.init.constant_(m.bias.data, 0.0)  # 初始化偏置为常数0

    def forward(self, x):
        # 解包输入特征图
        x0, x1, x2, x3 = x

        # 通过1x1卷积层调整输入特征图的通道数
        x0 = self.conv0(x0)
        x1 = self.conv1(x1)
        x2 = self.conv2(x2)
        x3 = self.conv3(x3)

        # 通过 BlockBody 模块进行多尺度特征融合
        out0, out1, out2, out3 = self.body([x0, x1, x2, x3])

        # 通过1x1卷积层调整输出特征图的通道数
        out0 = self.conv00(out0)
        out1 = self.conv11(out1)
        out2 = self.conv22(out2)
        out3 = self.conv33(out3)

        # 生成额外的下采样特征图
        out4 = self.conv44(out3)

        # 返回最终的融合后的特征图
        return out0, out1, out2, out3, out4

if __name__ == "__main__":
    # 创建实例,输入通道数为 32
    block = AFPN()

    # 创建随机输入张量 1,形状为 [batch_size, channels, height, width]
    input1 = torch.rand(16, 256, 200, 200)
    # 创建随机输入张量 2,形状与输入张量 1 相同
    input2 = torch.rand(16, 512, 100, 100)
    # 创建随机输入张量 3,形状为 [batch_size, channels, height, width]
    input3 = torch.rand(16, 1024, 50, 50)
    # 创建随机输入张量 4,形状与输入张量 1 相同
    input4 = torch.rand(16, 2048, 25, 25)

    # 应用 Model 实例处理输入张量
    x = (input1, input2,input3, input4)
    output = block(x)
    output1, output2, output3, output4,output5 = output
    # 打印输出张量的形状
    print(output1.size())
    print(output2.size())
    print(output3.size())
    print(output4.size())
    print(output5.size())
    print("抖音、B站、小红书、CSDN同号")
    print("布尔大学士 提醒您:代码无误~~~~")
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

布尔大学士

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值