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
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值