CNN网络:ResNet(四)

ResNet论文[https://arxiv.org/pdf/1512.03385.pdf]。RestNet网络结构ResNet在2015年被提出,在ImageNet比赛classification任务上获得第一名,因为它“简单与实用”并存,之后很多方法都建立在ResNet50或者ResNet101的基础上完成的,检测,分割,识别等领域里得到广泛的应用。

当更深的网络能够开始收敛时,一个退化问题就暴露出来了。随着网络深度的增加,精度达到饱和,然后开始退化。出乎意料的是,这种退化不是由过拟合引起的,向适当深度的模型中添加更多的层会导致更高的训练误差。ReNet网络就是来解决网络退化的问题,通过在网络架构多加入一条恒等映射,经过一次卷积,如果效果变差,则保持权重参数不变,相当于并没有做此次卷积。这样的话,就保证了神经网络不会随着网络层数的增加反而效果变差,也就阻止了模型退化的问题。

ResNet模型亮点
  • 提出Residual模块

  • 使用Batch Normalization加速训练,丢弃dropout

  • 易于收敛,很好地解决了退化的问题,模型可以很深,准确率大大提高

ResNet的网络结构如图所示:

我们按照ResNet-18来分析一下:

第一层卷积conv1:输入图像的size是224*224*3,使用64个size是7*7,stride步长为2,padding填充为3的卷积核,卷积后的输出size是112*112*64。

第二层池化:使用size是3*3的核进行最大池化,stride步长为2,池化后的输出size是56*56*64。

第三层conv2_x:两个卷积构成一个残差块,后面的x2的意思就是两个3*3,64的卷积层。残差块的公式如下。残差块分成两部分直接映射部分和残差部分。 h(xl) 是直接映射,反应在图1中是左边的曲线; F(xl,Wl) 是残差部分,conv2_x就是由两个卷积操作构成。

在卷积网络中, xl 可能和 xl+1 的Feature Map的数量不一样,这时候就需要使用 1×1 卷积进行升维或者降维。这时,残差块表示如下公式。其中 h(xl)=Wl′x 。其中 Wl′ 是 1×1 卷积操作,但是实验结果 1×1 卷积对模型性能提升有限,所以一般是在升维或者降维时才会使用。

残差块的PyTorch实现:

class BasicBlock(nn.Module):
    def __init__(self,in_channels,out_channels,stride=[1,1],padding=1) -> None:
        super(BasicBlock, self).__init__()
        # 残差部分
        self.layer = nn.Sequential(
            nn.Conv2d(in_channels,out_channels,kernel_size=3,stride=stride[0],padding=padding,bias=False),
            nn.BatchNorm2d(out_channels),
            nn.ReLU(inplace=True), # 原地替换 节省内存开销
            nn.Conv2d(out_channels,out_channels,kernel_size=3,stride=stride[1],padding=padding,bias=False),
            nn.BatchNorm2d(out_channels)
        )

        # shortcut 部分
        # 由于存在维度不一致的情况 所以分情况
        self.shortcut = nn.Sequential()
        if stride[0] != 1 or in_channels != out_channels:
            self.shortcut = nn.Sequential(
                # 卷积核为1 进行升降维
                # 注意跳变时 都是stride==2的时候 也就是每次输出信道升维的时候
                nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride[0], bias=False),
                nn.BatchNorm2d(out_channels)
            )

    def forward(self, x):
        out = self.layer(x)
        out += self.shortcut(x)
        out = F.relu(out)
        return out

ResNet-18的PyTorch实现

# 采用bn的网络中,卷积层的输出并不加偏置
class ResNet18(nn.Module):
    def __init__(self, BasicBlock, num_classes=10) -> None:
        super(ResNet18, self).__init__()
        self.in_channels = 64
        # 第一层作为单独的 因为没有残差快
        self.conv1 = nn.Sequential(
            nn.Conv2d(3,64,kernel_size=7,stride=2,padding=3,bias=False),
            nn.BatchNorm2d(64),
            nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        )
        # conv2_x
        self.conv2 = self._make_layer(BasicBlock,64,[[1,1],[1,1]])

        # conv3_x
        self.conv3 = self._make_layer(BasicBlock,128,[[2,1],[1,1]])

        # conv4_x
        self.conv4 = self._make_layer(BasicBlock,256,[[2,1],[1,1]])

        # conv5_x
        self.conv5 = self._make_layer(BasicBlock,512,[[2,1],[1,1]])

        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
        self.fc = nn.Linear(512, num_classes)

    #这个函数主要是用来,重复同一个残差块
    def _make_layer(self, block, out_channels, strides):
        layers = []
        for stride in strides:
            layers.append(block(self.in_channels, out_channels, stride))
            self.in_channels = out_channels
        return nn.Sequential(*layers)
    def forward(self, x):
        out = self.conv1(x)
        out = self.conv2(out)
        out = self.conv3(out)
        out = self.conv4(out)
        out = self.conv5(out)

        # out = F.avg_pool2d(out,7)
        out = self.avgpool(out)
        out = out.reshape(x.shape[0], -1)
        out = self.fc(out)
        return out

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值