1. 简介

  随着神经网络的进一步加深,可能会出现如下问题:

   (1)梯度消失、梯度爆炸

   (2)退化问题--训练集上准确率下降(不等于过拟合--表现为在训练集上表现更好)

是2015年ILSVRC 比赛2015第一名的算法。主要是针对更深的神经网络难以训练的问题,提出了一种残差学习的结构,不仅增加了网络的深度,而且减少了参数的数量,易于训练,取得了很好的效果。

2. 残差学习结构(residual learning)

                                 

residual learning 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)解决梯度消失问题:...

  具体到应用中,形成了以下两种结构:

                             

residual learning residual learning什么意思_residual learning_02

通道,经过3×3的卷积核(64通道)--->relu线性激活--->3×3的卷积核(64)--->加上64通道的输入--->relu。

×1卷积核,主要是为了调整输入的通道与卷积后的结果同维度,便于相加操作(不同于GoogLeNet的拼接!)

 

3. ResNet 

                                              

residual learning residual learning什么意思_residual learning_03

 

 

 

  上图中最右边是一个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