ResNet50 网络结构搭建(PyTorch)

ResNet50是一个经典的特征提取网络结构,虽然Pytorch已有官方实现,但为了加深对网络结构的理解,还是自己动手敲敲代码搭建一下。需要特别说明的是,笔者是以熟悉网络各层输出维度变化为目的的,只对建立后的网络赋予伪输入并测试各层输出,并没有用图像数据集训练过该网络(后续会用图像数据集测试并更新博客)。

1 预备理论

在动手搭建ResNet50以前,首先需要明确ResNet系列网络的基本结构,其次复习与卷积相关的几个知识点,以便更好地理解网络中间输出维度的变化。

1.1 ResNet系列

1.1.1 几种网络基本配置

ResNet原文中的表格列出了几种基本的网络结构配置:

在这里插入图片描述

从上表可以看出,对于不同深度的ResNet有以下几个特点,请特别关注(3)(4):

(1)起始阶段都经历了相同的conv1和maxpool的过程。

(2)不同深度的ResNet都是由基本残差块堆叠而成。 18,34-layer的基本模块记为Basicblock,包含2次卷积;50,101,152layer的基本模块记为Bottleneck,包含3次卷积(1.1.2节会详细说明)。

在这里插入图片描述

n-layer确定的情况下,称i阶段为convi_x过程,i∈{2,3,4,5}:

(3)2阶段堆叠的残差块完全相同。 因为输入到输出是56→56,无下采样过程。

(4)3至5阶段堆叠的第一个残差块和其余残差块是不同的。 解释:每个阶段均对特征图像大小进行下采样。以50layer–conv3_x为例,仔细思考残差块的堆叠模式可以发现,下采样过程发生在4个堆叠残差块中的第一个,因为这里实现了特征图尺寸从56→28的过程;而对于其余3个残差块,特征图的维度全部是28→28,因此这3个的结构是完全相同的(如果这里我没有表述清楚,可以参看下面的图和末尾的表)。其余阶段同理。

1.1.2 基本残差块的两种模式

以下将以问答的形式来理解基本残差块的两种模式,由于本文关注ResNet50的实现,因此以下以Bottleneck为例说明,对于Basicblock可以类比。

  • 为什么需要下采样?

    下采样是才特征提取网络中经常使用的操作,潜在的作用是增强特征的变换不变性,减少特征参数防止过拟合,具体表现为特征图像尺寸逐渐缩小,通道数逐渐增加

  • 为什么Bottleneck有两种模式?

    请回顾1.1.1节中的表格,对于绿色交界处,特征图维度不变,而对于红色交界,特征图维度变化,说明这里需要进行一次下采样。以50layer–conv3_x为例,这一阶段共堆叠了4个残差块。红色交界有一次下采样,并且在第一个残差块实现,我们称其为Bottleneck_down;对于后面3个残差块,特征图的尺寸均不发生变化,不进行下采样,记为Bottleneck_norm。具体可以参考下面的红绿线辅助理解。这一规律对于conv3_x, conv4_x, conv5_x都是成立的。

    在这里插入图片描述

    特别的是,对于conv2_x,其堆叠的残差块都是相同的。

    在这里插入图片描述

  • 下采样的卷积实现思路?

    在PyTorch中使用nn.Conv2d实现卷积,通常会使用的参数如下:

    torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, bias=True)
    

    因此实现下采样会用到如下操作(虽然还不够具体,是个思路雏形):

    • 特征图尺寸减半:卷积步长stride=2。

    • 特征图通道加倍:卷积核数目out_channels=4*in_channels(因为H/2,W/2,特征图缩小为1/4,所以通道数x4)。

  • 下采样的具体实现?

    参照上面的思路,Bottleneck的两种模式如下:实现的关键点就是我们需要判断出当前位置需要哪种模式,并设置正确的卷积步长。具体实现请参看2.1节。

    在这里插入图片描述

1.2 2维卷积后的特征图维度变化

下面我们来回顾一下卷积的过程前后输入特征图维度的变化。

下图是一个k x k x C_in大小的卷积核在 H_in x W_in x C_in 大小的特征图上进行二维卷积的过程。对于单个卷积核而言,它将在 H_in x W_in 的平面上进行滑动,并按照一下公式输出一张维度为 H_out x W_out x 1 大小的特征图;而输出特征图的数量取决于卷积核的数量filter_num。
在这里插入图片描述

2 代码实现

以下我们分别将Bottleneck和ResNet50作为类来实现,而Bottleneck是ResNet50中堆叠的基本残差块。

2.1 Bottleneck实现

为了实现Bottleneck的两种模式配置,我们需要利用downsample控制shortcut支路特征图尺寸和通道数变换(这里先知道downsample的功能即可,具体实现见ResNet类)。

这里解释一下,downsample是shortcut支路的网络结构。如果当前残差块的输入和输出的特征维度大小相同,那么shortcut的输出直接继承原始输入x就好的(代码中暂存为了identity变量);而如果当前残差块的输入与输出特征图的尺寸大小和通道数不一致,即需要在shortcut支路也完成下采样的操作。

def forward(self, x):
    identity = x    # 将原始输入暂存为shortcut的输出
    if self.downsample is not None:
        identity = self.downsample(x)   # 如果需要下采样,那么shortcut后:H/2,W/2。C: out_channel -> 4*out_channel(见ResNet中的downsample实现)

以下是Bottleneck类的完整实现,可以对照ResNet50的表格查看。

# todo Bottleneck
class Bottleneck(nn.Module):
    """
    __init__
        in_channel:残差块输入通道数
        out_channel:残差块输出通道数
        stride:卷积步长
        downsample:在_make_layer函数中赋值,用于控制shortcut图片下采样 H/2 W/2
    """
    expansion = 4   # 残差块第3个卷积层的通道膨胀倍率
    def __init__(self, in_channel, out_channel, stride=1, downsample=None):
        super(Bottleneck, self).__init__()

        self.conv1 = nn.Conv2d(in_channels=in_channel, out_channels=out_channel, kernel_size=1, stride=1, bias=False)   # H,W不变。C: in_channel -> out_channel
  
### ResNet50 模型架构概述 ResNet50 是一种基于残差网络(Residual Network, ResNet)的深度卷积神经网络模型,它由 Microsoft Research 提出并广泛应用于图像分类任务中。ResNet50 的核心特点是引入了 **跳跃连接** 或称为 **短接路径(skip connections)**,这使得深层网络中的梯度能够更有效地传播[^1]。 #### 架构细节 ResNet50 的整体架构可以分为以下几个部分: - 输入层:接受大小为 \(224 \times 224\) 的 RGB 图像作为输入。 - 卷积层:初始卷积层使用 \(7 \times 7\) 大小的滤波器进行特征提取,并通过最大池化操作进一步减少空间维度。 - 残差块:ResNet50 中的核心组件是由多个残差单元组成的堆叠结构。每个残差单元通常包含三个卷积层(\(1 \times 1\), \(3 \times 3\), 和 \(1 \times 1\)),并通过跳跃连接将输入直接加到输出上[^2]。 - 全局平均池化层:用于将最后一个卷积层的输出转换为固定长度的向量。 - 全连接层:最终输出类别概率分布。 以下是 ResNet50 的典型架构图解: | 层名称 | 输出尺寸 | 参数数量 | |----------------|---------------|----------| | 初始卷积层 | \(112 \times 112 \times 64\) | ~9.4K | | 最大池化层 | \(56 \times 56 \times 64\) | —— | | 残差块组 1 | \(56 \times 56 \times 256\) | ~18M | | 残差块组 2 | \(28 \times 28 \times 512\) | ~37M | | 残差块组 3 | \(14 \times 14 \times 1024\) | ~74M | | 残差块组 4 | \(7 \times 7 \times 2048\) | ~147M | | 平均池化层 | \(1 \times 1 \times 2048\) | —— | | 全连接层 | 类别数 | 可配置 | 总参数量约为 25.6 百万。 --- ### PyTorch 实现代码示例 以下是一个简单的 ResNet50 模型实现代码片段,适用于 PyTorch 框架: ```python import torch.nn as nn import torchvision.models as models class ResNet50Model(nn.Module): def __init__(self, num_classes=1000): super(ResNet50Model, self).__init__() # 加载预训练的 ResNet50 模型 self.resnet50 = models.resnet50(pretrained=True) # 替换最后一层全连接层以适配自定义分类任务 in_features = self.resnet50.fc.in_features self.resnet50.fc = nn.Linear(in_features, num_classes) def forward(self, x): return self.resnet50(x) # 创建实例 model = ResNet50Model(num_classes=10).cuda() print(model) ``` 此代码加载了一个带有 ImageNet 预训练权重的 ResNet50 模型,并将其最后的全连接层替换为适合特定任务的新层。 --- ### 预训练模型下载链接 对于 ResNet50 的预训练模型,可以直接从官方库或第三方资源获取。以下是几个常见的下载方式: - 使用 TensorFlow/Keras 官方接口加载预训练模型: ```python from tensorflow.keras.applications import ResNet50 model = ResNet50(weights='imagenet', include_top=False) ``` - 使用 PyTorch 官方接口加载预训练模型: ```python import torchvision.models as models resnet50 = models.resnet50(pretrained=True) ``` 如果需要手动下载权重文件,可以从以下地址访问: - [PyTorch 官方 ResNet 权重](https://pytorch.org/docs/stable/torchvision/models.html#resnet) - [TensorFlow Keras 官方 ResNet 权重](https://keras.io/api/applications/resnet/)[^2] --- ###
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值