残差神经网络

ResNet以及Inception 结构是深度神经网络在图像识别中近两年最成功的算法了,在这主要介绍下ResNet以及对残差网络论文中存在疑惑的解释。

残差神经网络_第1张图片

论文的开始给出了这张图,非常直观的可以看到在分类问题中,随着网络深度的增加,精度并没有一直提高,而是出现了大幅度的降低,主要原因还是神经网络一直存的问题——模型是否能在训练样本上有效的训练,显然深度增加到了一定的成都模型学习的难度也增加了很多。

在这结合源码说明两个问题:

  • 为什么叫残差网络?
  • 网络结构中,随着feature map 个数的增加(同样伴随着尺寸的缩小),怎么实现shortcut?

1. 为什么叫残差网络

残差神经网络_第2张图片

看过Highway Netword 的同学可能会注意到,ResNet是前者的一种特殊情况,不过前者借鉴了循环神经网络中的“门”的概念提出的,类似于LSTM中的输入输出门概念,当门不做限制的时候,这时候就是ResNet的结构了。在第一次看完论文后不太理解残差的概念,困惑了很久。

对于分类问题,假如在给定的数据集上,最终的模型是固定的,也是最优的,类似于SVM中的最优分类面,我们称之为H(X),也就说模型是指给定的输入:x, 输出H(x)。在网络中加入了shortcut后,真正的网络层其实拟合的函数就成为F(x),而这正好是模型输出与输入的残差,随意残差网络的每个block其实都是通过多层的神经网络去拟合残差函数F(x)。


2. 在多个block的级联过程中,feature map尺寸减半,个数增倍的时候,原输入x是怎么变化的?

在论文中有介绍到该部分,不过只找到了对feature map个数增倍给出的办法,如图所示:

残差神经网络_第3张图片

框红框的两部分,一个表示输出feature map的尺寸减半了,另一个则是feature map的个数增倍了,尺寸减半是通过卷积的时候步长设置为2导致的。对这种情况,论文给出了两种不同的方法来合并卷基层拟合的残差函数以及原输入x,分别是:

  • 直接对x做padding(0),然后直接对位相加;

  • 将x通过映射函数映射到相同的维度;
    我们看下源码是怎么实现的:

    #首先做了avg_pooling 降低尺寸到之前的1/2,然后对最后channel维度前后做了padding补0
    # tensor维度[batch_size, width, height, channel],对应tf.pad函数的四个list
    with tf.variable_scope('sub_add'):
    if in_filter != out_filter:
      orig_x = tf.nn.avg_pool(orig_x, stride, stride, 'VALID')
      orig_x = tf.pad(
          orig_x, [[0, 0], [0, 0], [0, 0],
                   [(out_filter-in_filter)//2, (out_filter-in_filter)//2]])
    x += orig_x
    
           
            
           

源码还给了另外一种实现方式 _bottleneck_residual,对卷积层做了改进:

with tf.variable_scope('sub1'):
  x = self._conv('conv1', x, 1, in_filter, out_filter/4, stride) # 通道数将为输出的1/4

with tf.variable_scope(‘sub2’):
x = self._batch_norm(‘bn2’, x)
x = self._relu(x, self.hps.relu_leakiness)
x = self._conv(‘conv2’, x, 3, out_filter/4, out_filter/4, [1, 1, 1, 1]) #

with tf.variable_scope(‘sub3’):
x = self._batch_norm(‘bn3’, x)
x = self._relu(x, self.hps.relu_leakiness)
x = self._conv(‘conv3’, x, 1, out_filter/4, out_filter, [1, 1, 1, 1]) # 通道数增加到输出维度

with tf.variable_scope(‘sub_add’):
if in_filter != out_filter:
orig_x = self._conv(‘project’, orig_x, 1, in_filter, out_filter, stride)
x += orig_x

当然在某些block之间还要做BN操作。

看起来代码没有几行,但是里边包含的思想确实非常奇妙,作者今年又搞了个ICCV 2017的best paper,有时间好好研读研读~

欢迎一块交流讨论,文中有错误的地方,还请指正,谢谢~
email: [email protected]

你可能感兴趣的:(深度学习)