介绍
ResNet(Residual Network)是由微软亚洲研究院的何凯明等人在2015年提出的深度学习模型。ResNet采用残差学习的思想,通过在网络中添加跨层连接(shortcut connection)来解决深度神经网络训练过程中的梯度消失和梯度爆炸问题,使神经网络的训练深度可以达到数百层甚至上千层。
方法历史
在深度学习模型的发展历程中,人们发现随着网络深度的增加,模型的性能并不能一直提升,反而会出现性能下降的情况。这一现象被称为“退化问题”(Degradation Problem)。传统的深度学习网络的训练过程中,每一层的输入都是上一层的输出,因此每一层都需要学习到原始输入的信息,这就导致了信息的丢失和模型的性能下降。
为了解决这一问题,ResNet提出了残差学习的思想。残差学习的核心思想是:在训练过程中,学习网络的残差,即学习网络输出和输入之间的差异,而不是学习网络输出本身。这样可以有效地避免梯度消失和梯度爆炸问题,使得网络的训练深度可以达到数百层甚至上千层。
方法优点
ResNet的主要优点有:
- 可以训练非常深的神经网络,达到数百层甚至上千层。
- 在保持网络深度的同时,减少了网络参数的数量,降低了模型的复杂度。
- 在训练过程中,可以有效地避免梯度消失和梯度爆炸问题,提高了模型的训练效率和精度。
步骤详细
残差块
ResNet的基本单元是残差块(Residual Block),如下图所示:
残差块中包含两个卷积层和一个跨层连接。跨层连接将输入直接加到输出上,即 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(yl−1)+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,ni−1)+Hi,ni(yi−1,ni−1)
其中 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=yi−1,ni−1, 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,ni−1(⋯Fi,1(yi,0)⋯))+Hi,ni(yi−1,ni−1)
将 y i , 0 = y i − 1 , n i − 1 y_{i,0}=y_{i-1,n_{i-1}} yi,0=yi−1,ni−1代入上式得到:
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,ni−1(⋯Fi,1(yi−1,ni−1)⋯))+Hi,ni(yi−1,ni−1)
将上式展开得到:
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,ni−1(⋯Fi,1(Fi−1,ni−1(⋯F1,n1(x0)⋯))⋯))+Hi,ni(yi−1,ni−1)
将 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,nm−1(⋯Fm,1(Fm−1,nm−1(⋯F1,n1(x0)⋯))⋯))+Hm,nm(ym−1,nm−1)
上式表示一个深度为 L L L的网络的输出。由于网络中存在跨层连接,因此可以在网络中添加更多的层,使得网络的深度可以达到数百层甚至上千层。
计算步骤
ResNet的计算步骤如下:
- 输入数据经过一个卷积层,得到特征图。
- 特征图经过多个残差块,其中每个残差块包含两个卷积层和一个跨层连接。
- 最后一个残差块的输出经过一个全局平均池化层,得到一个特征向量。
- 特征向量经过一个全连接层,得到分类结果。
结构图
下面是ResNet的结构图,其中每个残差块包含两个卷积层和一个跨层连接:
其中,残差块包含两个卷积层和一个跨层连接,如下图所示:
跨层连接可以使用1x1卷积来改变通道数,如下图所示:
数组说明
下面是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)的四维张量,表示跨层连接的卷积核。