深度学习之卷积神经网络(12)深度残差网络

深度学习之卷积神经网络(12)深度残差网络


AlexNet、VGG、GoogleLeNet等网络模型的出现将神经网络的法阵带入了几十层的阶段,研究人员发现网络的层数越深,越有可能获得更好的泛化能力。但是当模型加深以后,网络变得越来越难训练,这主要是由于梯度弥散和梯度爆炸现象造成的。在较深层数的神经网络中,梯度信息由网络的末层传向网络的首层时,传递的过程中会出现梯度接近于0或梯度值非常大的现象。网络层数越深,这种现象可能会越严重。

 那么怎么解决深层神经网络的梯度弥散和梯度爆炸现象呢?一个很自然的想法是,既然浅层神经网络不容易出现这些梯度现象,那么就可以尝试给深层神经网络添加一种 回退到浅层神经网络的机制。当深层神经网络可以轻松地回退到浅层神经网络时,深层神经网络可以获得与浅层设立相当的模型性能,而不至于更糟糕。

 通过在输入和输出之间添加一条直接连接的 Skip Connection可以让神经网络具有回退的能力。以VGG13深度神经网络为例,假设观察到VGG13模型出现梯度弥散现象,而10层的网络模型并没有观测到梯度弥散现象,那么可以考虑在最后的两个卷积层添加Skip Connection,如下图所示。通过这种方式,网络模型可以自动选择是否经由这两个卷积层完成特征变换,还是直接跳过这两个卷积层而选择Skip Connection,亦或结合两个卷积层和Skip Connection的输出。

在这里插入图片描述

添加了Skip Connection的VGG13网络结构


 2015年,微软亚洲研究院何凯明等人发表了基于Skip Connection的深度残差网络(Residual Nerual Network,简称ResNet)算法[1],并提出了18层、34层、50层、101层、152层的ResNet-18、ResNet-34、ResNet-50、ResNet-101和ResNet-152等模型,甚至成功训练出层数达到1202层的极深神经网络。ResNet在ILSVRC2015挑战赛ImageNet数据集上的分类、检测等任务上面均获得了最好性能,ResNet论文至今已经获得超25000的引用量,可见ResNet在人工智能行业的影响力。

[1] K. He, X. Zhang, S. Ren 和 J. Sun, “Deep Residual Learning for Image Recognition,” CoRR, 卷 abs/1512.03385, 2015.

ResNet原理

 ResNet通过在卷积层的输入和输出之间添加Skip Connec-tion实现层数回退机制,如下图所示,输入 x \boldsymbol x x通过两个卷积层,得到特征变换后的输出 F ( x ) \mathcal F(\boldsymbol x) F(x),与输入 x \boldsymbol x x进行对应元素的相加运算,得到最终输出 H ( x ) \mathcal H(\boldsymbol x) H(x):
H ( x ) = x + F ( x ) \mathcal H(\boldsymbol x)=\boldsymbol x+\mathcal F(\boldsymbol x) H(x)=x+F(x)
H ( x ) \mathcal H(\boldsymbol x) H(x)叫做残差模块(Residual Block,简称ResBlock)。由于被Skip Connection包围的卷积神经网络需要学习映射 F ( x ) = H ( x ) − x \mathcal F(\boldsymbol x)=\mathcal H(\boldsymbol x)-\boldsymbol x F(x)=H(x)x,故称为残差网络。

 为了能够满足输入 x \boldsymbol x x与卷积层的输出 F ( x ) \mathcal F(\boldsymbol x) F(x)能够相加运算,需要输入 x \boldsymbol x x的shape与 F ( x ) \mathcal F(\boldsymbol x) F(x)的shape完全一致。当出现shape不一致时,一般通过在Skip Connection上添加额外的卷积运算缓解将输入 x \boldsymbol x x变换到与 F ( x ) \mathcal F(\boldsymbol x) F(x)相同的shape,如下图中 identity ( x ) \text{identity}(\boldsymbol x) identity(x)函数所示,其中 identity ( x ) \text{identity}(\boldsymbol x) identity(x) 1 × 1 1×1 1×1的卷积运算居多,主要英语调整输入的通道数。

在这里插入图片描述

残差模块


 如下图所示,对比了34层的深度残差网络、34层的普通深度网络以及19层的VGG网络结构。可以看到,深度残差网络通过堆叠残差模块,达到了较深的网络层数,从而获得了训练稳定、性能优越的深层网络模型。

在这里插入图片描述

网络结构比较

ResBlock实现

 深度残差网络并没有增加新的网络层类型,只是通过在输入和输出之间添加一条Skip Connection,因此并没有针对ResNet的底层实现。在TensorFlow中通过调用普通卷积层即可实现残差模块。

在这里插入图片描述


在这里插入图片描述


 首先创建一个新类,在初始阶段创建残差块中需要的卷积层、激活函数层等,首先新建 F ( x ) \mathcal F(\boldsymbol x) F(x)卷积层,代码如下:

class BasicBlock(layers.Layer):
    # 残差模块
    def __init__(self, filter_num, stride=1):
        super(BasicBlock, self).__init__()
        # 第一个卷积单元
        self.conv1 = layers.Conv2D(filter_num, (3, 3), strides=stride, padding='same')
        self.bn1 = layers.BatchNormalization()
        self.relu = layers.Activation('relu')
        # 第二个卷积单元
        self.conv2 = layers.Conv2D(filter_num, (3, 3), strides=1, padding='same')
        self.bn2 = layers.BatchNormalization()


其中:

  • __init__(self, filter_num, stride=1)函数为初始化函数,filter_num为卷积核的数量,stride=1表示不对输入进行下采样;
  • strides=stride, padding='same'表示可以直接得到输入、输出同大小的卷积层;
  • layers.BatchNormalization()表示标准化层;

 当 F ( x ) \mathcal F(\boldsymbol x) F(x)的形状与 x \boldsymbol x x不同时,无法直接相加,我们需要新建 identity ( x ) \text{identity}(\boldsymbol x) identity(x)卷积层,来完成 x \boldsymbol x x的形状转换。紧跟上面代码,实现如下:

if stride != 1:# 通过1x1卷积完成shape匹配
    self.downsample = Sequential()
    self.downsample.add(layers.Conv2D(filter_num, (1, 1), strides=stride))
else:# shape匹配,直接短接
    self.downsample = lambda x:x


上述代码表示如果stride(即步长)如果不为1的话,那么输出与输入的shape不相同,那么此时我们就需要新建 identity ( x ) \text{identity}(\boldsymbol x) identity(x)卷积层,来完成 x \boldsymbol x x的形状转换,使之与输入的shape相同。

 在向前传播时,只需要将 F ( x ) \mathcal F(\boldsymbol x) F(x) identity ( x ) \text{identity}(\boldsymbol x) identity(x)相加,并添加ReLU激活函数即可。向前计算函数代码如下:

def call(self, inputs, training=None):

    # [b, h, w, c],通过第一个卷积单元
    out = self.conv1(inputs)
    out = self.bn1(out)
    out = self.relu(out)
    # 通过第二个卷积单元
    out = self.conv2(out)
    out = self.bn2(out)
    # 通过identity模块
    identity = self.downsample(inputs)
    # 2条路径输出直接相加
    output = layers.add([out, identity])
    output = tf.nn.relu(output) # 激活函数

    return output

猜你喜欢

转载自blog.csdn.net/weixin_43360025/article/details/120592137
今日推荐