深度残差网络

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


怎么解决深层神经网络的梯度弥散和梯度爆炸现象呢?既然浅层神经网络不容易出现梯度现象,那么可以尝试给深层神经网络添加一种回退到浅层神经网络的机制当深层神经网络可以轻松地回退到浅层神经网络时,深层神经网络可以获得与浅层神经网络相当的模型性能
 

一、残差网络模型

通过在输入和输出之间添加一条直接连接的 Skip Connection 可以让神经网络具有回退的能力
深度残差网络_第1张图片

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

基于 Skip Connection 的深度残差网络(Residual Neural Network,简称 ResNet)算法,并提出了 18 层、 34 层、 50 层、 101层、 152 层的 ResNet-18、 ResNet-34、 ResNet-50、 ResNet-101 和 ResNet-152 等模型

ResNet 通过在卷积层的输入和输出之间添加 Skip Connection 实现层数回退机制,输入通过两个卷积层,得到特征变换后的输出ℱ(),与输入进行对应元素的相加运算,得到最终输出ℋ():
ℋ() = + ℱ(),ℋ()叫作残差模块(Residual Block,简称 ResBlock)。由于被 Skip Connection 包围的卷积神经网络需要学习映射ℱ() = ℋ() - ,故称为残差网络
深度残差网络_第2张图片

为了能够满足输入与卷积层的输出ℱ()能够相加运算,需要输入的 shape 与ℱ()的shape 完全一致。当出现 shape 不一致时一般通过在 Skip Connection 上添加额外的卷积运算环节将输入变换到与ℱ()相同的 shape,如图identity()函数所示,其中identity()以 1×1 的卷积运算居多,主要用于调整输入的通道数

深度残差网络_第3张图片

深度残差网络通过堆叠残差模块,达到了较深的网络层数,从而获得了训练稳定、性能优越的深层网络模型
 

 二、ResBlock 实现

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

首先创建一个新类,在初始化阶段创建残差块中需要的卷积层、 激活函数层等

# 残差模块类
class BasicBlock(layers.Layer):
    def __init__(self, filter_num, stride=1):
        super(BasicBlock, self).__init__()
        # f(x)包含两个普通卷积层
        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=stride, padding='same')
        self.bn2 = layers.BatchNormalization()

        # f(x) 与 x 形状不同,无法相加
        if stride != 1:  # 不相等,插入identity层
            self.downsample = Sequential()
            self.downsample.add(layers.Conv2D(filter_num, (1, 1), strides=stride))
        else:  # 直接相加
            self.downsample = lambda x: x

    # 向前传播函数
    def call(self, inputs, training=None):
        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)
        # f(x) + x
        output = layers.add([out, identity])
        # 再通过激活函数,放在前面也行
        output = tf.nn.relu(output)
        return output

首先新建ℱ()卷积层,当ℱ()的形状与不同时,无法直接相加,我们需要新建identity()卷积层,来完成的形状转换。在前向传播时,只需要将ℱ()与identity()相加,并添加 ReLU 激活函数即可
 

RseNet 通过堆叠多个 ResBlock 可以构成复杂的深层神经网络,如ResNet18,ResNet34......

三、DenseNet

DenseNet 将前面所有层的特征图信息通过 Skip Connection 与当前层输出进行聚合,与 ResNet 的对应位置相加方式不同, DenseNet 采用在通道轴维度进行拼接操作, 聚合特征信息

深度残差网络_第4张图片

输入0 通过H1卷积层得到输出1, 1与0 在通道轴上进行拼接,得到聚合后的特征张量,送入H2卷积层,得到输出2,同样的方法, 2与前面所有层的特征信息 1与0 进行聚合,再送入下一层。如此循环,直至最后一层的输出4和前面所有层的特征信息: {}=0, 1, 2, 3进行聚合得到模块的最终输出,  这样一种基于 Skip Connection 稠密连接的模块叫做 Dense Block

DenseNet 通过堆叠多个 Dense Block 可以构成复杂的深层神经网络
 

你可能感兴趣的:(#,深度学习,网络,神经网络,深度学习)