2015 ImageNet classification top1
2016 CVPR Best
Deep Residual Learning for Image Recognition
Resnet及其变种的结构梳理、有效性分析与代码解读(PyTorch)
1、ResNet解决了什么?
-
2015年
-
ImageNet比赛classification任务上获得第一名
-
更深的网络会伴随梯度消失/爆炸问题,阻碍网络收敛,degradation problem
- Batch normalization
- Identity mapping(构建恒等映射)——ResNet
2、ResNe怎么解决网络退化?
残差学习的结构如图有点类似与电路中的“短路”,所以是一种短路连接(shortcut connection)。
增加一个identity mapping(恒等映射),将原始所需要学的函数H(x)转换成F(x)+x,F(x)的优化 会比H(x)简单的多。
Residual block通过shortcut connection实现,通过shortcut将这个block的输入和输出进行一个element-wise的加叠,这个简单的加法并不会给网络增加额外的参数和计算量,同时却可以大大增加模型的训练速度、提高训练效果,并且当模型的层数加深时,这个简单的结构能够很好的解决退化问题。
上面公式中:
- h 表示 shortcut 使用什么形式的变换(Identity map)
- F 是 residual function。F= y-h(x)
- f 为Residual Units输出处使用的函数(relu)
3、ResNet网络结构
输入部分、输出部分和中间卷积部分(四个stage),网络之间的不同主要在于中间卷积部分的block参数和个数存在差异。
ResNet34及ResNet101分别具有两种不同的基本“shortcut connection”结构。
ResNet34使用BasicBlock,ResNet101使用 Bottleneck作为“shortcut connection”。
“shortcut connection” 只是元素级相加操作
-
shortcut
def __init__(self, in_planes, planes, stride=1): super(BasicBlock, self).__init__() self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=3, stride=stride, padding=1, bias=False) self.bn1 = nn.BatchNorm2d(planes) self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=1, padding=1, bias=False) self.bn2 = nn.BatchNorm2d(planes) self.shortcut = nn.Sequential() if stride != 1 or in_planes != self.expansion*planes:# 上图虚线shortcut self.shortcut = nn.Sequential( nn.Conv2d(in_planes, self.expansion*planes, kernel_size=1, stride=stride, bias=False), nn.BatchNorm2d(self.expansion*planes) ) 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
downsample 主要用来处理H(x)=F(x)+x中F(x)和xchannel维度不匹配问题
由bootlenect和直线shortcut组成。整个bootlenect+shortcut称为Residual uint。几个Residual uint的叠加称为Residual block。Resnet结构都是由4个Residual block组成的。
ResNet34使用BasicBlock,ResNet101使用 Bottleneck作为“bootlenect”,引入1×1卷积
- 1x1卷积作用?
- 对通道数进行升维和降维(跨通道信息整合)
- 比其他尺寸的运算复杂度低
- 引入了更多的非线性映射(relu)
- downsample,它的作用是对输入特征图大小进行减半处理,每个stage都有且只有一个downsample。
全局自适应平滑池化,把所有的特征图拉成1*1,然后接全连接层输出,输出节点个数与预测类别个数一致。
-
设计规律
- 卷积层主要是3×3卷积
- 不使用dropout,全部使用BN
- 对于相同的输出特征图大小的层,即同一stage,具有相同数量的3x3滤波器;
- 当feature map大小降低一半时,feature map的数量增加一倍,这保持了网络层的复杂度。
- 每个stage通过步长为2的卷积层执行下采样(downsample),而却这个下采样只会在每一个stage的第一个卷积完成,有且仅有一次。
- 网络以全局平均池化层和softmax的1000路全连接层结束。
4、ResNet常见改进
- 改进downsample部分,减少信息流失。
每个stage的第一个conv都有下采样的步骤。- ResNet-B的改进就是就是将下采样移到后面的3x3卷积里面去做,避免了信息的大量流失。
- ResNet-D则是在ResNet-B的基础上将identity部分的下采样交给avgpool去做,避免出现1x1卷积和stride同时出现造成信息流失。
- ResNet V2
- a:相加后需要进入ReLU做一个非线性激活再输出,使残差块输出永远是非负的,制约了模型的表达能力
- e:a中输出处ReLU移入残差块内部
5、反向传播
这一块有没有大佬补充一下~~~
6、Resnet代码
resnet18,第一层是没有downsample的,因为输入与输出通道数一样,其余层都有downsample。
-
layers1:
[ResidualBlock(
(conv1): Conv2d(16, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace)
(conv2): Conv2d(16, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
),
ResidualBlock(
(conv1): Conv2d(16, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace)
(conv2): Conv2d(16, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
),
ResidualBlock(
(conv1): Conv2d(16, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace)
(conv2): Conv2d(16, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)] -
layers2:
[ResidualBlock(
(conv1): Conv2d(16, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace)
(conv2): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(downsample): Sequential(
(0): Conv2d(16, 32, kernel_size=(3, 3), stride=(2, 2)