ResNet(残差网络)

1️⃣ ResNet介绍

  ResNet斩获2015ImageNet竞赛图像分类任务第一名, 目标检测第一名。其重要贡献是发现了神经网络的“退化现象”,即一直加深网络深度并不能总是提高性能,反而会导致性能下降。针对这种现象,发明了Shortcut connection,极大的消除了深度过大的神经网络训练困难问题。


2️⃣ 原理分析

ResNet有两种block,一种是两层的BasicBlock,一种是三层的Bottleneck

  • 两层的BasicBlock用于浅层网络(ResNet18、ResNet34),分实线和虚线的BasicBlock
    • 实线BasicBlock :包含两个卷积层
      根据这个图举个例子,例如输入是64×56×56
      • 第一个卷积:64个3×3卷积核, stride=1,padding=1
        输入:64×56×56
        输出:64×56×56
      • 第二个卷积:64个3×3卷积核, stride=1,padding=1
        输入:64×56×56
        输出:64×56×56
      • Shortcut connection
        直接相加
        相加后的最终维度:64×56×56
        在这里插入图片描述
    • 虚线BasicBlock:里面包含两个卷积层,但注意的是第一个卷积层stride=2,进行了高宽的降维,因此shortcut connect加入了1×1的卷积进行x和F(x)维度统一。
      根据这个图举个例子,例如输入是64×56×56
      • 第一个卷积:128个3×3卷积核, stride=2,padding=1
        输入:64×56×56
        输出:128×28×28
      • 第二个卷积:128个3×3卷积核, stride=1,padding=1
        输入:128×28×28
        输出:128×28×28
      • Shortcut connection
        原始的x是64×56×56,经过两层卷积后的F(x)维度128×28×28,x与F(x)维度不一致,因此x需要经过卷积层来统一维度:128个1×1卷积核,stride=2,padding=1
        • 输入x:64×56×56
        • 输出:128×28×28
        • 维度一致了,因此可以相加
        • 相加后的最终维度:128×28×28
          在这里插入图片描述
  • 三层的Bottleneck用于深层网络(ResNet50、ResNet101、ResNet152),也分实线和虚线
    • 实线Bottleneck:包含三个卷积层
      在这里插入图片描述
    • 虚线Bottleneck:包含三个卷积层,但注意的是第二个卷积层stride=2,进行了高宽的降维,因此shortcut connect加入了1×1的卷积进行x和F(x)维度统一

在这里插入图片描述

  • 两层的BasicBlock与三层的Bottleneck的对比,这里我们只分析实线的情况
    在这里插入图片描述
    Bottleneck能够减少参数和运算量,其中第一个1×1的卷积用于降维,第二个1×1的卷积用于升维。

    假设输入的通道数为256【CNN参数与输入的高宽无关】,Basicblock需要1179648个参数,右侧模块需要69632个参数
    在这里插入图片描述

    注:CNN参数个数(没算偏置) = 卷积核高度×卷积核宽度×输入通道数×卷积核个数


3️⃣ 网络结构

在这里插入图片描述

这里以ResNet18(1个卷积+16个卷积+1个fc)为例进行分析:
在这里插入图片描述

  • 网络输入:3×224×224

  • conv1:64个7×7卷积核,stride=2,padding=3
    输入:3×224×224
    输出:64×112×112
    经过BN(批量归一化)

  • maxpool:3×3,stride=2,padding=1
    输入:64×112×112
    输出:64×56×56

  • con2_x:两个实线BasicBlock,每个BasicBlock有两层卷积
    在这里插入图片描述

    • 第一个实线BasicBlock

      • 第一个卷积:64个3×3卷积核, stride=1,padding=1
        输入:64×56×56
        输出:64×56×56
        经过BN(批量归一化)
        经过ReLu
      • 第二个卷积:64个3×3卷积核, stride=1,padding=1
        输入:64×56×56
        输出:64×56×56
        经过BN(批量归一化)
      • Shortcut connection
        直接相加
        经过ReLu
        相加后的最终维度:64×56×56
    • 第二个实线BasicBlock

      • 第一个卷积:64个3×3卷积核, stride=1,padding=1
        输入:64×56×56
        输出:64×56×56
      • 第二个卷积:64个3×3卷积核, stride=1,padding=1
        输入:64×56×56
        输出:64×56×56
      • Shortcut connection
        直接相加
        经过ReLu
        相加后的最终维度:64×56×56
  • conv3_x:一个虚线BasicBlock,一个实线BasicBlock,每个BasicBlock有两层卷积
    在这里插入图片描述

    • 第一个BasicBlock(虚线,高宽减半,深度加倍)
      • 第一个卷积:128个3×3卷积核,stride=2【注意这里】,padding=1
        输入:64×56×56
        输出:128×28×28
        经过BN(批量归一化)
        经过ReLu
      • 第二个卷积:128个3×3卷积核,stride=1,padding=1
        输入:128×28×28
        输出:128×28×28
        经过BN(批量归一化)
      • Shortcut connection:原始的x是64×56×56,经过两层卷积后的F(x)维度128×28×28,x与F(x)维度不一致,因此x需要经过卷积层来统一维度:128个1×1卷积核,stride=2,padding=1
        • 输入x:64×56×56
        • 输出:128×28×28
        • 经过BN(批量归一化)
        • 维度一致了,因此可以相加
        • 经过ReLu
        • 相加后的最终维度:128×28×28
    • 第二个BasicBlock
      • 第一个卷积:128个3×3卷积核,stride=1,padding=1
        输入:64×56×56
        输出:128×28×28
        经过BN(批量归一化)
        经过ReLu
      • 第二个卷积:128个3×3卷积核,stride=1,padding=1
        输入:128×28×28
        输出:128×28×28
        经过BN(批量归一化)
      • Shortcut connection
        直接相加
        经过ReLu
        相加后的最终维度:128×28×28
  • con4_x:一个虚线BasicBlock,一个实线BasicBlock,每个BasicBlock有两层卷积
    在这里插入图片描述

    • 第一个BasicBlock(虚线,高宽减半,深度加倍)
      • 第一个卷积:256个3×3卷积核,stride=2【注意这里】,padding=1
        输入:128×28×28
        输出:256×14×14
        经过BN(批量归一化)
        经过ReLu
      • 第二个卷积:256个3×3卷积核,stride=1,padding=1
        输入:256×14×14
        输出:256×14×14
        经过BN(批量归一化)
      • Shortcut connection:原始的x是128×28×28,经过两层卷积后的F(x)维度256×14×14,x与F(x)维度不一致,因此x需要经过卷积层来统一维度:256个1×1卷积核,stride=2,padding=1
        • 输入x:128×28×28
        • 输出:256×14×14
        • 经过BN(批量归一化)
        • 维度一致了,因此可以相加
        • 经过ReLu
        • 相加后的最终维度:256×14×14
    • 第二个BasicBlock
      • 第一个卷积:256个3×3卷积核,stride=1,padding=1
        输入:256×14×14
        输出:256×14×14

      • 第二个卷积:256个3×3卷积核,stride=1,padding=1
        输入:256×14×14
        输出:256×14×14

      • Shortcut connection
        直接相加
        经过ReLu
        相加后的最终维度:256×14×14

  • con5_x:一个虚线BasicBlock,一个实线BasicBlock,每个BasicBlock有两层卷积
    在这里插入图片描述

    • 第一个BasicBlock(虚线,高宽减半,深度加倍)
      • 第一个卷积:512个3×3卷积核,stride=2【注意这里】,padding=1
        输入:256×14×14
        输出:512×7×7
        经过BN(批量归一化)
        经过ReLu
      • 第二个卷积:512个3×3卷积核,stride=1,padding=1
        输入:512×7×7
        输出:512×7×7
        经过BN(批量归一化)
      • Shortcut connection:原始的x是256×14×14,经过两层卷积后的F(x)维度512×7×7,x与F(x)维度不一致,因此x需要经过卷积层来统一维度:512个1×1卷积核,stride=2,padding=1
        • 输入x:256×14×14
        • 输出:512×7×7
        • 经过BN(批量归一化)
        • 维度一致了,因此可以相加
        • 经过ReLu
        • 相加后的最终维度:512×7×7
    • 第二个BasicBlock
      • 第一个卷积:512个3×3卷积核,stride=1,padding=1
        输入:512×7×7
        输出:512×7×7
        经过BN(批量归一化)
        经过ReLu
      • 第二个卷积:512个3×3卷积核,stride=1,padding=1
        输入:512×7×7
        输出:512×7×7
        经过BN(批量归一化)
      • Shortcut connection
        直接相加
        经过ReLu
        相加后的最终维度:512×7×7
  • avgpool:输出512, 1, 1

  • FC:10


4️⃣ 代码

1.这里以resnet18为例进行分析,创建一个名为resnet18.py的文件

# 仅针对Resnet18
import torch
from torch import nn
from torch.nn import functional as F

class BasicBlock(nn.Module):
    def __init__(self, input_channels, out_channels,
                 use_1x1conv=False, strides=1):
        super().__init__()
        self.conv1 = nn.Conv2d(input_channels, out_channels,
                               kernel_size=3, padding=1, stride=strides)
        self.bn1 = nn.BatchNorm2d(out_channels)
        self.relu1=nn.ReLU()
        
        self.conv2 = nn.Conv2d(out_channels, out_channels,
                               kernel_size=3, padding=1)
        self.bn2 = nn.BatchNorm2d(out_channels)
        
        if use_1x1conv:
            self.conv3 = nn.Conv2d(input_channels, out_channels,
                                   kernel_size=1, stride=strides)
            self.bn3=nn.BatchNorm2d(out_channels)
        else:
            self.conv3 = None

    def forward(self, x):
        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu1(out)

        out=self.conv2(out)
        out=self.bn2(out)
        
        if self.conv3:
            x = self.conv3(x)
            x=self.bn3(x)
        out += x
        out = F.relu(out)
        return out
    

def big_block(input_channels, out_channels, num_block,
                first_bigblock=False):
    blk = []
    for i in range(num_block):

        if first_bigblock:
            blk.append(BasicBlock(out_channels, out_channels))

        elif i==0:
            blk.append(BasicBlock(input_channels, out_channels,
                                use_1x1conv=True, strides=2))
        else:
            blk.append(BasicBlock(out_channels, out_channels))
    return blk

class Resnet(nn.Module):
    def __init__(self):
        super().__init__()
        self.b1 = nn.Sequential(nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3),
                   nn.BatchNorm2d(64), 
                   nn.MaxPool2d(kernel_size=3, stride=2, padding=
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值