Pytorch与视觉竞赛入门3.2-使用Pytorch搭建ResNet
Pytorch与视觉竞赛入门3.2-使用Pytorch搭建ResNet
ResNet
原理
原理部分主要摘自:ResNet详解与分析
残差结构有什么作用可以查看之前的博客:Transformer相关——残差模块
残差模块结构图示
\(F(x)+x\)构成的block称之为Residual Block,即残差块,如下图所示,多个相似的Residual Block串联构成ResNet。
一个残差块有2条路径\(F(x)\)和\(x\),\(F(x)\)路径拟合残差,不妨称之为残差路径,\(x\)路径为identity mapping恒等映射,称之为”shortcut”。图中的\(⊕\)为element-wise addition,要求参与运算的\(F(x)\)和\(x\)的尺寸要相同。所以,随之而来的问题是:
- 残差路径如何设计?
- shortcut路径如何设计?
- Residual Block之间怎么连接?(也就是残差网络的结构)
残差路径如何设计?
在原论文中,残差路径可以大致分成2种,一种有bottleneck结构,即下图右中的1×1卷积层,用于先降维再升维,主要出于降低计算复杂度的现实考虑,称之为“bottleneck block”,另一种没有bottleneck结构,如下图左所示,称之为“basic block”。
shortcut路径如何设计?
shortcut路径大致也可以分成2种,取决于残差路径是否改变了feature map数量和尺寸,一种是将输入x原封不动地输出,另一种则需要经过1×1卷积来升维 or/and 降采样,主要作用是将输出与F(x)路径的输出保持shape一致,对网络性能的提升并不明显,两种结构如下图所示:
残差网络的结构
ResNet的设计有如下特点:
- 与plain net相比,ResNet多了很多“旁路”,即shortcut路径,其首尾圈出的layers构成一个Residual Block;
- ResNet中,所有的Residual Block都没有pooling层,降采样是通过conv的stride实现的;
- 分别在conv3_1、conv4_1和conv5_1 Residual Block,降采样1倍,同时feature map数量增加1倍,如图中虚线划定的block;
- 通过Average Pooling得到最终的特征,而不是通过全连接层;
- 每个卷积层之后都紧接着BatchNorm layer,为了简化,图中并没有标出;
ResNet结构非常容易修改和扩展,通过调整block内的channel数量以及堆叠的block数量,就可以很容易地调整网络的宽度和深度,来得到不同表达能力的网络,而不用过多地担心网络的“退化”问题,只要训练数据足够,逐步加深网络,就可以获得更好的性能表现。
不断地增加ResNet的深度,甚至增加到1000层以上,残差也没有发生“退化”,可见Residual Block的有效性。ResNet的动机在于认为拟合残差比直接拟合潜在映射更容易优化,下面通过绘制error surface直观感受一下shortcut路径的作用,图片截自Loss Visualization。
- ResNet-20(no short)浅层plain net的error surface还没有很复杂,优化也会很困难,但是增加到56层后复杂程度极度上升。对于plain net,随着深度增加,error surface 迅速“恶化”;
- 引入shortcut后,error suface变得平滑很多,梯度的可预测性变得更好,显然更容易优化;
改进
论文Identity Mappings in Deep Residual Networks进一步研究ResNet,通过ResNet反向传播的理论分析以及设计了以下几种不同的Residual Block结构。注意,这里的视角与之前不同,这里将shortcut路径视为主干路径,将残差路径视为旁路。
经过实验验证,(e)full pre-activation表现最好,具有更强的泛化能力,能更好地避免“退化”,堆叠大于1000层后,性能仍在变好。具体的变化在于:
- 通过保持shortcut路径的“纯净”,可以让信息在前向传播和反向传播中平滑传递,这点十分重要。为此,如无必要,不引入1×1卷积等操作,同时将上图灰色路径上的ReLU移到了F(x)路径上。
- 在残差路径上,将BN和ReLU统一放在weight前作为pre-activation,获得了“Ease of optimization”以及“Reducing overfitting”的效果。
代码实现
模型需求分析
对模型进行抽象和分析,以便写出更优美的代码~
需要实现:
- 两个不同残差模块的类
BasicBlock
和Bottleneck
;其中的3x3的卷积层
和1x1的卷积层
可以被进一步包装起来,注意在conv3_1、conv4_1和conv5_1 Residual Block,需要降采样1倍,但在conv2_1不需要,因此残差模块需要一个控制是否降采样的参数。 - ResNet模型:需要传入block类型及block的层数;
- 构建模型的函数:封装构建不同的ResNet。
代码
conv3x3和conv1x1
dilation表示空洞卷积间隔,空洞卷积是什么可以查看的博客:Pytorch与视觉竞赛入门2_冬于的博客-CSDN博客
from typing import Type, Any, Callable, Union, List, Optional |
BasicBlock
class BasicBlock(nn.Module): |
Bottleneck
class Bottleneck(nn.Module): |
ResNet模型
class ResNet(nn.Module): |
构建模型
def _resnet( |
打印模型
from torchsummary import summary |
#打印结果 |