ResNet: 残差网络

37 篇文章 1 订阅
27 篇文章 1 订阅

介绍

ResNet(Residual Network)是由微软亚洲研究院的何凯明等人在2015年提出的深度学习模型。ResNet采用残差学习的思想,通过在网络中添加跨层连接(shortcut connection)来解决深度神经网络训练过程中的梯度消失和梯度爆炸问题,使神经网络的训练深度可以达到数百层甚至上千层。

方法历史

在深度学习模型的发展历程中,人们发现随着网络深度的增加,模型的性能并不能一直提升,反而会出现性能下降的情况。这一现象被称为“退化问题”(Degradation Problem)。传统的深度学习网络的训练过程中,每一层的输入都是上一层的输出,因此每一层都需要学习到原始输入的信息,这就导致了信息的丢失和模型的性能下降。

为了解决这一问题,ResNet提出了残差学习的思想。残差学习的核心思想是:在训练过程中,学习网络的残差,即学习网络输出和输入之间的差异,而不是学习网络输出本身。这样可以有效地避免梯度消失和梯度爆炸问题,使得网络的训练深度可以达到数百层甚至上千层。

方法优点

ResNet的主要优点有:

  • 可以训练非常深的神经网络,达到数百层甚至上千层。
  • 在保持网络深度的同时,减少了网络参数的数量,降低了模型的复杂度。
  • 在训练过程中,可以有效地避免梯度消失和梯度爆炸问题,提高了模型的训练效率和精度。

步骤详细

残差块

ResNet的基本单元是残差块(Residual Block),如下图所示:

输入
卷积层
ReLU
卷积层
输出

残差块中包含两个卷积层和一个跨层连接。跨层连接将输入直接加到输出上,即 y = F ( x ) + x y = F(x) + x y=F(x)+x,其中 F ( x ) F(x) F(x)表示残差块中的卷积操作, x x x表示输入, y y y表示输出。

残差网络

ResNet的整个网络结构如下图所示:

输入
卷积层
残差块
残差块
残差块
全局平均池化
全连接层
输出

ResNet由多个残差块组成,其中每个残差块包含多个卷积层和一个跨层连接。网络的最后一层是一个全连接层,用于输出分类结果。

PyTorch实现

下面是使用PyTorch实现ResNet的示例代码:

import torch
import torch.nn as nn
import torch.nn.functional as F

class ResidualBlock(nn.Module):
    def __init__(self, in_channels, out_channels, stride=1):
        super(ResidualBlock, self).__init__()
        self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(out_channels)
        self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False)
        self.bn2 = nn.BatchNorm2d(out_channels)
        self.shortcut = nn.Sequential()
        if stride != 1 or in_channels != out_channels:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride, bias=False),
                nn.BatchNorm2d(out_channels)
            )
        
    def forward(self, x):
        out = F.relu(self.bn1(self.conv1(x)))
        out = self.bn2(self.conv2(out))
        out += self.shortcut(x)
        out = F.relu(out)
        return out

class ResNet(nn.Module):
    def __init__(self, block, num_blocks, num_classes=10):
        super(ResNet, self).__init__()
        self.in_channels = 64
        self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(64)
        self.layer1 = self.make_layer(block, 64, num_blocks[0], stride=1)
        self.layer2 = self.make_layer(block, 128, num_blocks[1], stride=2)
        self.layer3 = self.make_layer(block, 256, num_blocks[2], stride=2)
        self.layer4 = self.make_layer(block, 512, num_blocks[3], stride=2)
        self.avg_pool = nn.AdaptiveAvgPool2d((1, 1))
        self.fc = nn.Linear(512, num_classes)
        
    def make_layer(self, block, out_channels, num_blocks, stride):
        strides = [stride] + [1] * (num_blocks - 1)
        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 = F.relu(self.bn1(self.conv1(x)))
        out = self.layer1(out)
        out = self.layer2(out)
        out = self.layer3(out)
        out = self.layer4(out)
        out = self.avg_pool(out)
        out = out.view(out.size(0), -1)
        out = self.fc(out)
        return out

def ResNet18():
    return ResNet(ResidualBlock, [2, 2, 2, 2])

def ResNet34():
    return ResNet(ResidualBlock, [3, 4, 6, 3])

def ResNet50():
    return ResNet(Bottleneck, [3, 4, 6, 3])

def ResNet101():
    return ResNet(Bottleneck, [3, 4, 23, 3])

def ResNet152():
    return ResNet(Bottleneck, [3, 8, 36, 3])

理论推导过程

假设在网络中添加一个残差块,其中 x x x表示输入, y y y表示输出, F ( x ) F(x) F(x)表示残差块中的卷积操作, H ( x ) H(x) H(x)表示跨层连接,即 y = F ( x ) + H ( x ) y = F(x) + H(x) y=F(x)+H(x)

在传统的神经网络中,每一层的输出都是上一层的输入,即 y = f ( x ) y = f(x) y=f(x),其中 f f f表示网络的映射函数。在残差网络中,每一层的输出是上一层输入的残差,即 y = f ( x ) + x y = f(x) + x y=f(x)+x。这样可以避免信息的丢失,使得网络的训练深度可以达到数百层甚至上千层。

假设在网络中添加多个残差块,其中 x l x_l xl表示第 l l l层的输入, y l y_l yl表示第 l l l层的输出, F l ( x l ) F_l(x_l) Fl(xl)表示第 l l l层的映射函数, H l ( x l ) H_l(x_l) Hl(xl)表示第 l l l层的跨层连接。则第 l l l层的输出可以表示为:

y l = F l ( y l − 1 ) + H l ( x l ) y_l = F_l(y_{l-1}) + H_l(x_l) yl=Fl(yl1)+Hl(xl)

其中 l = 1 , 2 , ⋯   , L l=1,2,\cdots,L l=1,2,,L y 0 = x 0 y_0=x_0 y0=x0 y L y_L yL表示网络的输出。

对于一个深度为 L L L的网络,可以将其分为若干个子网络,每个子网络包含若干个残差块。假设第 i i i个子网络包含 n i n_i ni个残差块,则第 i i i个子网络的输出可以表示为:

y i , n i = F i , n i ( y i , n i − 1 ) + H i , n i ( y i − 1 , n i − 1 ) y_{i,n_i} = F_{i,n_i}(y_{i,n_i-1}) + H_{i,n_i}(y_{i-1,n_{i-1}}) yi,ni=Fi,ni(yi,ni1)+Hi,ni(yi1,ni1)

其中 i = 1 , 2 , ⋯   , m i=1,2,\cdots,m i=1,2,,m n i n_i ni表示第 i i i个子网络的残差块的数量, y i , 0 = y i − 1 , n i − 1 y_{i,0}=y_{i-1,n_{i-1}} yi,0=yi1,ni1 y 0 , 0 = x 0 y_{0,0}=x_0 y0,0=x0 y m , n m y_{m,n_m} ym,nm表示网络的输出。

将上式展开得到:

y i , n i = F i , n i ( F i , n i − 1 ( ⋯ F i , 1 ( y i , 0 ) ⋯   ) ) + H i , n i ( y i − 1 , n i − 1 ) y_{i,n_i} = F_{i,n_i}(F_{i,n_i-1}(\cdots F_{i,1}(y_{i,0}) \cdots)) + H_{i,n_i}(y_{i-1,n_{i-1}}) yi,ni=Fi,ni(Fi,ni1(Fi,1(yi,0)))+Hi,ni(yi1,ni1)

y i , 0 = y i − 1 , n i − 1 y_{i,0}=y_{i-1,n_{i-1}} yi,0=yi1,ni1代入上式得到:

y i , n i = F i , n i ( F i , n i − 1 ( ⋯ F i , 1 ( y i − 1 , n i − 1 ) ⋯   ) ) + H i , n i ( y i − 1 , n i − 1 ) y_{i,n_i} = F_{i,n_i}(F_{i,n_i-1}(\cdots F_{i,1}(y_{i-1,n_{i-1}}) \cdots)) + H_{i,n_i}(y_{i-1,n_{i-1}}) yi,ni=Fi,ni(Fi,ni1(Fi,1(yi1,ni1)))+Hi,ni(yi1,ni1)

将上式展开得到:

y i , n i = F i , n i ( F i , n i − 1 ( ⋯ F i , 1 ( F i − 1 , n i − 1 ( ⋯ F 1 , n 1 ( x 0 ) ⋯   ) ) ⋯   ) ) + H i , n i ( y i − 1 , n i − 1 ) y_{i,n_i} = F_{i,n_i}(F_{i,n_i-1}(\cdots F_{i,1}(F_{i-1,n_{i-1}}(\cdots F_{1,n_1}(x_0) \cdots)) \cdots)) + H_{i,n_i}(y_{i-1,n_{i-1}}) yi,ni=Fi,ni(Fi,ni1(Fi,1(Fi1,ni1(F1,n1(x0)))))+Hi,ni(yi1,ni1)

y 0 , 0 = x 0 y_{0,0}=x_0 y0,0=x0代入上式得到:

y m , n m = F m , n m ( F m , n m − 1 ( ⋯ F m , 1 ( F m − 1 , n m − 1 ( ⋯ F 1 , n 1 ( x 0 ) ⋯   ) ) ⋯   ) ) + H m , n m ( y m − 1 , n m − 1 ) y_{m,n_m} = F_{m,n_m}(F_{m,n_m-1}(\cdots F_{m,1}(F_{m-1,n_{m-1}}(\cdots F_{1,n_1}(x_0) \cdots)) \cdots)) + H_{m,n_m}(y_{m-1,n_{m-1}}) ym,nm=Fm,nm(Fm,nm1(Fm,1(Fm1,nm1(F1,n1(x0)))))+Hm,nm(ym1,nm1)

上式表示一个深度为 L L L的网络的输出。由于网络中存在跨层连接,因此可以在网络中添加更多的层,使得网络的深度可以达到数百层甚至上千层。

计算步骤

ResNet的计算步骤如下:

  1. 输入数据经过一个卷积层,得到特征图。
  2. 特征图经过多个残差块,其中每个残差块包含两个卷积层和一个跨层连接。
  3. 最后一个残差块的输出经过一个全局平均池化层,得到一个特征向量。
  4. 特征向量经过一个全连接层,得到分类结果。

结构图

下面是ResNet的结构图,其中每个残差块包含两个卷积层和一个跨层连接:

输入
卷积层
BN
ReLU
残差块1
残差块1
跨层连接
残差块2
残差块2
跨层连接
残差块3
残差块3
跨层连接
全局平均池化
全连接层
输出

其中,残差块包含两个卷积层和一个跨层连接,如下图所示:

输入
卷积层
BN
ReLU
卷积层
BN
跨层连接
ReLU

跨层连接可以使用1x1卷积来改变通道数,如下图所示:

输入
1x1卷积
BN
ReLU
1x1卷积
BN
跨层连接
ReLU

数组说明

下面是ResNet中每个残差块使用的数组:

  • 输入 x x x:形状为 ( N , C , H , W ) (N, C, H, W) (N,C,H,W)的四维张量,表示输入数据的特征图。
  • 第一个卷积层的权重 w 1 w_1 w1:形状为 ( F , C , k , k ) (F, C, k, k) (F,C,k,k)的四维张量,表示第一个卷积层的卷积核。
  • 第一个卷积层的偏置 b 1 b_1 b1:形状为 ( F , ) (F,) (F,)的一维张量,表示第一个卷积层的偏置。
  • 第二个卷积层的权重 w 2 w_2 w2:形状为 ( F , F , k , k ) (F, F, k, k) (F,F,k,k)的四维张量,表示第二个卷积层的卷积核。
  • 第二个卷积层的偏置 b 2 b_2 b2:形状为 ( F , ) (F,) (F,)的一维张量,表示第二个卷积层的偏置。
  • 跨层连接的权重 w h w_h wh:形状为 ( F , C , 1 , 1 ) (F, C, 1, 1) (F,C,1,1)的四维张量,表示跨层连接的卷积核。
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值