最近学习了resnet,做了一些笔记,欢迎大神指点笔记中的错误和不清楚的地方
参考的大神链接
梯度消失:
每层误差梯度小于1时,反向传播时,每向前传播一层,就要乘一个小于1的梯度,层数越多梯度越趋近于0
梯度爆炸:
误差梯度大于1, 层数越多梯度越来越大
对数据进行标准化处理,初始化权重,batch normalization而不是dropout
残差结构
实线是验证集的错误率,虚线是训练集的错误率。层数越深,效果越好。
整个resnet是许多残差结构堆叠而成。在代码实现的过程中需要用到一些公式来保证输出与输入的特定关系(计算合适的padding/P):
卷积层输出图像大小:
O = ( I − K + 2 P ) / S + 1 O = (I-K+2P)/S+1 O=(I−K+2P)/S+1
O=输出图像的尺寸;I=输入图像的尺寸;K=卷积层的核尺寸;N=核数量;S=移动步长;P=填充数
池化层输出图像大小:
O = ( I − P s ) / S + 1 O = (I-Ps)/S+1 O=(I−Ps)/S+1
O=输出图像的尺寸;I=输入图像的尺寸;S=移动步长;Ps=池化层尺寸
卷积层参数数量:
W C W_C WC = 卷积层的weights数量
B C B_C BC = 卷积层的biases数量
P C P_C PC = 所有参数的数量
K = 核尺寸
N = 核数量
C = 输入图像通道数
卷积核的参数个数是核参数*输入通道数*输出通道数,偏置是当前核数量
全连接层的参数数量:
W c f W_{cf} Wcf= weights的数量
B c f B_{cf} Bcf= biases的数量
O= 前卷积层的输出图像的尺寸
N = 前卷积层的核数量
F = 全连接层的神经元数量
参数数量是图像尺寸(高*宽)*卷积核数量*全连接层神经元数,也就是上一层的输出乘全连接层
左图用于resnet34层数较少的网络,右图用于层数更多的网络,resnet-50/101/152。
每经过一次卷积后都有一个relu函数,除了block的最后一次卷积。最后一次卷积的结果要与输入(shortcut)相加(不是连接),再relu。相加要注意维度一定是相同的
右图中1*1的卷积核用来深度(channel)的降维和升维,减少参数总数。如果层数多的网络模型应用左图,则参数个数为:(这里让输入统一为256d来对比)
3 × 3 × 256 × 256 + 3 × 3 × 256 × 256 = 1179648 3 \times 3\times256\times256+3\times3\times256\times256 =1179648 3×3×256×256+3×3×256×256=1179648
如果应用右图,参数个数为:
1 × 1 × 256 × 64 + 3 × 3 × 64 × 64 + 1 × 1 × 64 × 256 = 69632 1\times1\times256\times64+3\times3\times64\times64+1\times1\times64\times256=69632 1×1×256×64+3×3×64×64+1×1×64×256=69632
可以看到参数少了十几倍
可以发现在以resnet的整体结构中,存在一些虚线连接,这些都出现在不同的conv的连接处,即conv的第一个残差结构。虚线的残差结构输入和输出的shape并不相同,并且通过stride为2完成了下采样。
下面以不同规模的resnet中conv3_x为例,且只说明原论文中尝试的效果最好的虚线残差连接方式:
conv3_x中的第一个残差结构,输入是上层的输出,即[56,56,64]而输出要变成[28,28,128],但依旧满足主分支与shortcut的shape相同,因为要进行相加的操作。
主分支通过将第一个卷积核stride变为2来将输入特征的宽和高减少一半(56->28),通过增加卷积核数量来增加输入特征的深度(64->128);
shortcut增加了stride为2的 1 × 1 1\times 1 1×1卷积核,也达到了减半宽高,增加深度的效果。
此时保证了主分支与shortcut的shape相同,可以进行相加的操作。
注:以下为pytorch的实现方式,原论文的第一个 1 × 1 1\times1 1×1的卷积核stride是2,第二个 3 × 3 3\times3 3×3的卷积核stride是1。pytorch实现的方式可以在imagenet的top1上略微提升准确率。
主分支 1 × 1 1\times1 1×1的卷积核用来深度(channel)的降维和升维, 3 × 3 3\times3 3×3的卷积核减半输入宽高([56,56,256]->[56,56,128]->[28,28,128]->[28,28,512])
shortcut与resnet34中同理。
同时需要注意,resnet50/101/152与resnet18/34的另一个区别是,最大池化下采样以后,输入深度就是64,符合resnet18/34的输入,因此conv2_x不需要改变深度(没有虚线残差连接),而resnet50/101/152在conv2_x中需要改变深度(64->256)(有虚线残差连接)。
通往大佬的细节介绍传送门
预处理的时候进行标准化处理可以加速网络的收敛。但即使输入满足均值为0,方差为1,输入经过卷积层后的结果未必满足。因此需要使用Batch Normalization来使下一层的输入重新标准化。如下图所示:
Batch Normalization的目的是使每个batch的feature map满足均值为0,方差为1,这里是指对一个batch的每个深度(channel)每层的标准化。
公式是:
其中 μ \mu μ和 σ \sigma σ是正向传播时统计得到的每个batch的均值和方差(是向量,每一维代表不同channel的数值),通过第三行的公式进行标准化,其中 ϵ \epsilon ϵ是为了防止分母为0。第四行中的 γ \gamma γ和 β \beta β用来调整方差和均值,是通过反向传播中训练得到的,默认值 γ = 1 , β = 0 \gamma=1,\beta=0 γ=1,β=0,从而得到真正的符合条件的下一层输入。
例子如下:
对一个batch中同一channel的所有feature进行标准化。(例子未涉及反向传播)
意义:
能够快速训练出理想效果,且数据集小的时候也可以训练出理想效果。
方式:(硬件或时间受限的时候,选择第二种或第三种,但第一种效果最好)
载入预训练参数后训练所有参数(最后的全连接层无法载入预训练参数,因为最后全连接层的size跟迁移学习后的分类个数有关)
载入预训练参数后只训练最后几层参数
载入权重后在原网络后面添加一层全连接层,仅训练最后一个全连接层(此时原模型的最后全连接层的参数可以载入)