【深度学习】resnet-50网络结构

本文详细介绍了ResNet-50的网络结构,包括layer0、layer1以及后续的layer2至layer5的构建。重点解析了ResNet-50与ResNet-18的区别,特别是bottleneck结构。此外,还提供了ResNet-50的PyTorch实现代码,便于理解和复用。通过对ResNet-50的深入理解,有助于在目标检测等任务中更好地应用此模型。
摘要由CSDN通过智能技术生成

最近许多目标检测网络的backbone都有用到resnet-50的部分结构,于是找到原论文,看了一下网络结构,在这里做一个备份,需要的时候再来看看。

整体结构

在这里插入图片描述

layer0

首先是layer0,这部分在各个网络都一样,如图,由一个7x7,步距为2的卷积+BN+relu,加上3x3最大值池化,步长为2的池化层构成。在这里插入图片描述

layer1

以18和50举例
18的basicblock如下,layer1共有2个下图的basicblock
resnet-18的block称为basicblock
resnet-50称为bottleneck
identity和卷积之后的输出对应数值相加!
在这里插入图片描述

resnet-50的bottleneck如下,layer1共有3个bottleneck,
第2个和第3个block的输入channel为256
在这里插入图片描述

layer2及layer3,4,5

layer2以及之后的3,4,5与layer1的结构大致上相同,只不过每个layer的第一个block,resnet-18的第一个conv层的stride=2,resnet-50的3x3conv层stride=2。其他的block都与layer1一致。

例如resnet-50的layer2第一个bottleneck:

identity的卷积核大小应为3x3,为了保证residual前后的shape一致,能够相加,进行了下采样
在这里插入图片描述

第2,3,4个bottleneck为
在这里插入图片描述

将block和整体结构对应,理解resnet50结构并不难,

34与18类似,101,152与50类似,只是block数量不一样。

代码实现

import torch.nn as nn



# bottleneck多次用到,所以封装成类调用 这是layer2及之后的Bottleneck
class Bottleneck(nn.Module):
    def __init__(self, inchannels, outchannels, stride=1, identity=None):
        """
        :param inchannels: 输入维度
        :param outchannels: 输出维度
        :param stride: 第二个卷积层的步长
        :param identity: 输入到输出的处理,维度相同直接相加,维度不同要1x1卷积
        """
        super(Bottleneck, self).__init__()
        # 第一个卷积和第二个卷积的channel
        self.mid_channels = outchannels // 4;
        # 定义三个卷积层
        self.conv1 = nn.Conv2d(inchannels, self.mid_channels, kernel_size=1, stride=1)
        self.bn1 = nn.BatchNorm2d(self.mid_channels)

        self.conv2 = nn.Conv2d(self.mid_channels, self.mid_channels, kernel_size=3, stride=stride, padding=1)
        self.bn2 = nn.BatchNorm2d(self.mid_channels)

        self.conv3 = nn.Conv2d(self.mid_channels, outchannels, kernel_size=1, stride=1)
        self.bn3 = nn.BatchNorm2d(outchannels)

        # inplace=True 新的值直接代替旧的值 节省内存
        self.relu = nn.ReLU(inplace=True)

        self.stride = stride
        if identity is not None:
            self.identity = identity
        else:
            # 卷积然后BN,RELU,stride和3x3卷积的一致,保持shape一致
            self.identity = nn.Sequential(
                nn.Conv2d(inchannels,outchannels,kernel_size=1,stride=stride,bias=False),
                nn.BatchNorm2d(outchannels),
                nn.ReLU(inplace=True)
            )


    def forward(self,x):
        # 走identity线
        resduial=x

        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu(out)

        out = self.conv2(out)
        out = self.bn2(out)
        out = self.relu(out)

        out = self.conv3(out)
        out = self.bn3(out)
        out = self.relu(out)

        if self.identity is not None:
            resduial = self.identity(resduial)

        out += resduial
        out = self.bn3(out)
        out = self.relu(out)

        return out




class resnet_50(nn.Module):
    def __init__(self,image_shape):
        super(resnet_50, self).__init__()
        # 一般后接BN层不需要偏置
        self.layer_0_conv1 = nn.Conv2d(3,64,kernel_size=7,stride=2,padding=3,bias=False)
        self.layer_0_bn1 = nn.BatchNorm2d(64)
        self.layer_0_pool = nn.MaxPool2d(kernel_size=3,stride=2,padding=1)

        self.layer_1 = self.makelayer(3,64,256,1)
        self.layer_2 = self.makelayer(4,256,512,2)
        self.layer_3 = self.makelayer(6,512,1024,2)
        self.layer_4 = self.makelayer(3,1024,2048,2)
        self.a_pool = nn.AvgPool2d(3,stride=1,padding=1)
        self.fc = nn.Linear(7*7*2048,1000)



    def forward(self,x):

        out = self.layer_0_conv1(x)
        out = self.layer_0_bn1(out)
        out = self.layer_0_pool(out)

        out = self.layer_1(out)
        out = self.layer_2(out)
        out = self.layer_3(out)
        out = self.layer_4(out)
        out = self.a_pool(out)
        out = out.view(-1,7*7*2048)
        out = self.fc(out)

        return out


    # 生成layer
    def makelayer(self,number,inchannels, last_channels, stride=1):
        """
        :param number: 由几个bottleneck构成
        :param last_channels: bottleneck的最后一个卷积层输出的channel
        :param stride: bottleneck 3x3卷积的步长
        :param is_layer1: 是否为第一层
        :return:
        """

        layer = []
        # 构建生成每一层layer的方法
        # 每个layer的第一层的stride为2 单独构造
        bottle = Bottleneck(inchannels,last_channels,stride=stride)
        layer.append(bottle)
        
        for i in range(number-1):
            bottle = Bottleneck(last_channels,last_channels,stride=1,)
            layer.append(bottle)
        # *list 提取list中的每一个元素
        return nn.Sequential(*layer)


ResNet(Residual Network)是一种非常流行的深度残差网络,它在2015年由Kaiming He等人提出。ResNet通过引入残差连接(residual connection)解决了深度神经网络训练过程中的梯度消失和梯度爆炸问题,使得网络可以更深更容易训练。 ResNet34和ResNet50ResNet系列中的两个典型模型,它们的结构图如下所示: 1. ResNet34结构图: ``` 输入 | 卷积层(64个3x3卷积核) | 残差块(3个残差单元) | 全局平均池化 | 全连接层(输出类别数) | 输出 ``` ResNet34由一个初始的卷积层和4个残差块组成,每个残差块包含多个残差单元。每个残差单元由两个卷积层和一个跳跃连接组成,其中第一个卷积层用于降低特征图的维度,第二个卷积层用于恢复特征图的维度。最后通过全局平均池化和全连接层得到最终的输出。 2. ResNet50结构图: ``` 输入 | 卷积层(64个7x7卷积核,步长2) | 批量归一化 | 激活函数(ReLU) | 最大池化(3x3池化核,步长2) | 残差块(3个残差单元,每个残差单元包含3个卷积层) | 残差块(4个残差单元,每个残差单元包含4个卷积层) | 残差块(6个残差单元,每个残差单元包含6个卷积层) | 残差块(3个残差单元,每个残差单元包含3个卷积层) | 全局平均池化 | 全连接层(输出类别数) | 输出 ``` ResNet50相比于ResNet34更深更复杂,它由一个初始的卷积层、4个残差块和多个残差单元组成。每个残差块内的残差单元数量不同,分别为3、4、6和3个。每个残差单元内部包含多个卷积层和跳跃连接。最后通过全局平均池化和全连接层得到最终的输出。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值