ResNet 学习笔记
ResNet
出自论文Deep Residual Learning for Image Recognition
1.提出背景
1. 梯度爆炸/消失问题(这篇博客讲的很好)
2. 退化问题(随着网络深度增加,准确率达到饱和,然后迅速下降)
3. 为了使网络能够有更深的深度
2.什么是残差函数?
我们使用一般网络拟合的是h(x),使得输入x,h(x)能够得到正确的解来帮助我们预测分类
而在ResNet中引入了残差函数f(x)=h(x)-x(即目标值与输入值的偏差),通过训练拟合f(x),进而由f(x)+x得到h(x)
使用残差函数的好处有哪些呢?我的理解有以下几点(仅个人理解,如有错误请指出~)
- 解决了退化问题同时很好的避免(或者只能说是减少?)了梯度爆炸/消失。ResNet的出发点并不是为了解决梯度爆炸/消失问题,达到这种效果纯粹是无意之举,关于原因的话,我阅读了大量的博客和解析,他们用公式的解释方法我并不是很理解。
- 首先为什么解决了退化问题。当网络达到一定深度,若网络已经最优化,那么当继续增加网络时,f(x)将被push为0,h(x)变为恒等映射,那么对于整个网络来说,深度的增加也并不会对网络的performance带来太大的影响了。那么,我的理解是,梯度爆炸/消失的原因是随着深度增加,梯度的同向变化传递所导致的(如对每个单元选择同样的**函数,导数同时大于1或小于1,分别对应梯度爆炸和梯度消失,因此最简单的解决方法是使用ReLU**函数,而ResNet也同样选择了ReLU作为**函数),在ResNet中随着网络深度的增加,后续的网络由于采用恒等映射,因此并不会继续传递这种变化,从而避免了梯度爆炸/消失。(同样的原因,也有学者diss ResNet并没有真正的增加深度,只是使网络退化为了浅层网络)
- 进而,由于恒等映射的学习比一般的线性映射要简单的多,所以训练速度也相对同样的深层网络来说使比较快的。
- 同时,网络在深层如果真正学习到了一些东西,而不是简单的恒等映射,那么将会有更好的performance
- 此外,使用残差函数f(x)对映射后的输出更敏感,如由h(x)=f(x)+x=5.1,h1(x)=f(x)+x=5.2,x=5,f(x)=0.1->f(x)=0.2那么变化率由0.1/5放大至0.1/0.1=100%.
3.Residual Block
在实现网络时,使用了Residual Block来拟合h(x)=f(x)+x,整个网络由多个包含相同形式的Residual Blocks(每一层的第一个Block的第一个卷积层的stride可能和其他的不同)的layers堆叠形成
Residual Block 图示
我们称图中的弧线为short connection,随着网络不断优化达到最优时,f(x)被push为0,只剩下short connection,这时,Block表示一个恒等映射。
吴恩达的.deaplearning课程中对Residual Block的数学解释
这里的每一个节点都执行了线性函数和ReLU**函数。所以插入的时机是在线性变换之后,ReLU**之前
两种不同Residual Block 图示
左边的这种结构一般用于34层以上的网络,右边的结构用于于深层网络。
其中右侧的第一个1x1卷积用于降维,第三个卷积恢复维度,以减少参数量和计算。
那么如果f(x)维度和x不同该怎么相加?
这种情况一般发生在两个layer的交接处,即每个layer的第一个block,特征图像的尺寸和通道数发生了变化。为解决这个问题,论文中作者提出使用矩阵Ws左乘x后,再与f(x)相加,说白了,在实现的时候就是通过卷积达到同维度(我在实现CIFAR10的ResNet时使用一个1x1的卷积核,stride=2,即可实现特征图像尺寸减半同时通道数翻倍)
Residual Network
ResNet网络构造
.deeplearning课程对ResNet的解释
该图表示ResNet在Plain Network上增加了short connetion,可以看出ResNet的训练误差并没有发生退化。
网络以average pooling -> 1000-d fc -> softmax结束
Details
- 图像水平翻转,减去均值,224x224随机裁剪
- 每个卷积后和**前采用BN
- batchsize =256,lr = 0.1 当误差稳定时更新lr = lr*0.1,SGD优化函数,weight_decay=0.0001,momentum=0.9,
- 不采用dropout
用于CIFAR10的ResNet结构
CIFAR10的图像均为32x32,论文中ResNet的网络结构设置如下:
layer name | Input Size | Output Size | Block & Kernel Setting |
conv1 | 32x32,3(channels) | 32x32,16 | {3x3,16} x 1 , s=1 |
conv2 | 32x32,16 | 32x32,16 | {3x3,16} x n , 首个block的首层s=1 |
conv3 | 32x32,16 | 16x16, 32 | {3x3,32} x n , 首个block的首层s=2 |
conv4 | 16x16,32 | 8x8,64 | {3x3,64} x n , 首个block的首层s=2 |
average pool & 10d-fc | 8x8,64 | 1x1,64 | pool kernerl:{1x1,64} ; linear(64,10) |
其中n = {3,5,7,9},表示每个层中block的个数,分别形成Res20,32,44,56的网络
(depth= 6n+2)
具体实现请见我的github