1. 简介
随着神经网络的进一步加深,可能会出现如下问题:
(1)梯度消失、梯度爆炸
(2)退化问题--训练集上准确率下降(不等于过拟合--表现为在训练集上表现更好)
ResNet是2015年ILSVRC 比赛2015第一名的算法。主要是针对更深的神经网络难以训练的问题,提出了一种残差学习的结构,不仅增加了网络的深度,而且减少了参数的数量,易于训练,取得了很好的效果。
2. 残差学习结构(residual learning)
在上图中,通过引入深度残差学习框架(上述也称为’shortcut connections’)解决了退化问题。(具体来说:假设模块的输入为x,卷积层参数为F(x),输出为H(x),那么经过上述模块:输出H(x)=F(x)+x。则,F(x)=H(x)-x,网络block F(x)可以看成是在学习实际输出和输入x之间的残差。)
因此:
(1)解决退化问题:如果一个恒等映射是最优的,那么将残差置为零(即F(x)=0)比通过一堆非线性层来拟合恒等映射更容易。通过残差学习的重构,如果恒等映射是最优的,求解器可能简单地将多个非线性连接的权重推向零来接近恒等映射,大大减少了冗余卷积层的影响。
(2)解决梯度消失问题:...
具体到应用中,形成了以下两种结构:
如左图:输入是64通道,经过3×3的卷积核(64通道)--->relu线性激活--->3×3的卷积核(64)--->加上64通道的输入--->relu。
右图中多出来的1×1卷积核,主要是为了调整输入的通道与卷积后的结果同维度,便于相加操作(不同于GoogLeNet的拼接!)
3. ResNet
上图中最右边是一个34层的ResNet,实线是表示将输入直接加到卷积后的结果。虚线是表示通过不同通道数的1×1卷积核来调整输入的维度,便于与卷积后的结果相加操作。
在最后采用了平均池化,然后是1000维度的全连接层。
4. 代码参考(Keras定义一个卷积块)
部分参考自网络,添加批规范化(Batch Normalnizations)处理
def identity_block(X, f, filters, stage, block): """ ##参数说明 ## X:输入 ## f:整数,中间conv2D的维度 ##filter:卷积核的维度 ## block 用于命名网络中的层 ##返回值: 维度为(n_H, n_W, n_C) """ ##定义变量名 conv_name_base = ‘res‘ + str(stage) + block + ‘_branch‘ bn_name_base = ‘bn‘ + str(stage) + block + ‘_branch‘ #过滤核 F1, F2, F3 = filters #保存输入的值 X_shortcut = X X = Conv2D(filters = F1, kernel_size = (1, 1), strides = (1,1), padding = ‘valid‘, name = conv_name_base + ‘2a‘, kernel_initializer = glorot_uniform(seed=0))(X) X = BatchNormalization(axis = 3, name = bn_name_base + ‘2a‘)(X) X = Activation(‘relu‘)(X)
#中间层卷积 X = Conv2D(filters = F2, kernel_size = (f, f), strides = (1,1), padding = ‘same‘, name = conv_name_base + ‘2b‘, kernel_initializer = glorot_uniform(seed=0))(X) X = BatchNormalization(axis=3, name = bn_name_base + ‘2b‘)(X) X = Activation(‘relu‘)(X) X = Conv2D(filters = F3, kernel_size = (1, 1), strides = (1,1), padding = ‘valid‘, name = conv_name_base + ‘2c‘, kernel_initializer = glorot_uniform(seed=0))(X) X = BatchNormalization(axis=3, name = bn_name_base + ‘2c‘)(X) #将输入与卷积后的结果相加 X = layers.add([X, X_shortcut]) X = Activation(‘relu‘)(X) return X