ResNet模型详解及代码复现

目录

一. ResNet网络模型

ResNet的提出

残差网络原理

ResNet 网络模型

二. 代码复现


一. ResNet网络模型

ResNet的提出

        ResNet残差网络主要是通过残差块组成的,在提出残差网络之前,网络结构无法很深,在VGG中,卷积网络达到了19层,在GoogLeNet中,网络达到了22层。随着网络层数的增加,网络发生了退化(degradation)的现象:随着网络层数的增多,训练集loss逐渐下降,然后趋于饱和,当你再增加网络深度的话,训练集loss反而会增大。而引入残差块后,网络可以达到很深,网络的效果也随之变好

残差网络原理

        与普通网络的串行结构相比,残差单元增加了跳跃映射,将输入与输出直接进行相加,补充卷积过程中损失的特征信息,这点与U-net的跳跃连接结构有点类似,不过Res中的跳跃连接做的是Add操作,而U-net的跳跃连接做的是Concatenate操作,还是有本质的不同。

ResNet模型详解及代码复现_第1张图片

Fig1. 故此网络块的输出为:
因为相加必须保证与是同维度的,因此可以写成通式如下式,用于匹配维度。
有两种维度匹配的方式(A)用zero-padding增加维度 (B)用1x1卷积增加维度

ResNet 网络模型

resnet18、resnet34、resnet50、resnet101、resnet152结构
ResNet模型详解及代码复现_第2张图片
Fig2.降采样由conv3 1、conv4 1和conv5 1以2的步幅执行

 ResNet模型详解及代码复现_第3张图片
Fig3.ImageNet的网络架构示例。左:VGG-19模型[40](196亿次失败)作为参考。Mid-dle:具有34个参数层(36亿次浮点运算)的普通网络。右图:具有34个参数层(36亿次浮点运算)的剩余网络。虚线快捷方式增加了维度。表1显示了更多细节和其他变体

二. 代码复现

1. 网络搭建

从上图Fig2, 可以看出(18layer\34layer)和(50\101\152layer) 的残差结构不一样,如下:
ResNet模型详解及代码复现_第4张图片
Fig4.ResNet的深度残差函数F。左图:称为BasicBlock结构 ,用于ResNet-18/34。右图:称为Bottleneck结构, 同于 ResNet-50/101/152的“瓶颈”构建块。

下面代码中所演示的是 BasicBlock, 若想改成ResNet-50/101/152, 改下网络结构即可

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, Sequential

class BasicBlock(layers.Layer):
    def __init__(self, filter_num, strides=1):
        super(BasicBlock, self).__init__()

        # 这里解释下padding=same的规则,他是在输入上做padding的
        # 如果stride不是1,那么他会通过补全输入的维度,使得输出是你预计的值

        # 第一层,如果stride不是1,那么其实进行了一次采样,会导致输出的结果形状比输入小
        self.conv1 = layers.Conv2D(filter_num, (3, 3), strides=strides, 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()

        # 如果短接了,那么也要保证结果的shape是一样的
        if strides != 1:
            self.downsample = Sequential()
            self.downsample.add(layers.Conv2D(filter_num, (1, 1), strides=strides))
        else:
            self.downsample = lambda x:x

    def call(self, inputs, training=None):
        # [b, h, w, c]
        out = self.conv1(inputs)
        out = self.bn1(out, training=training)
        out = self.relu(out)

        out = self.conv2(out, training=training)
        out = self.bn2(out)

        identity = self.downsample(inputs)

        # layers.add 就是简单的把两个张量加到一起
        output = layers.add([out, identity])
        output = tf.nn.relu(output)

        return output

class ResNet(keras.Model):
    def __init__(self, layer_dims): # layer_dims: [2, 2, 2, 2], num_classes 就是输出结果数
        super(ResNet, self).__init__()

        # 根节点,对数据进行预处理,不是一个resblock
        self.stem = Sequential([
            layers.Conv2D(64, (3, 3), strides=(1, 1)),
            layers.BatchNormalization(),
            layers.Activation('relu'),
            layers.MaxPool2D(pool_size=(2, 2), strides=(1, 1), padding='same')
        ])

        # 四层 resblock
        self.layer1 = self.build_resblock(64, layer_dims[0])
        self.layer2 = self.build_resblock(128, layer_dims[1], strides=2)
        self.layer3 = self.build_resblock(256, layer_dims[2], strides=2)
        self.layer4 = self.build_resblock(512, layer_dims[3], strides=2)

        # 输出层,到这一层处理前,形状是 [b, 512, h, w],
        # 我们经过3次strides=2 处理,结果形状应该被缩小到 1/8,如果输入是 (32, 32) 那么到这里就是 (4,4),而我们需要h w变成 (1, 1),
        # 因此加一个平均层,无论最后(h,w)值如何,都会被取一个平均值而缩减到(1,1),比如如果是 (4,4) 那么就取所有16个值的平均值
        self.avgpool = layers.Flatten() #
        self.fc = layers.Dense(1, activation=tf.nn.sigmoid) # 全连接层输出结果

    def call(self, inputs, training=None):
        x = self.stem(inputs, training=training)

        x = self.layer1(x, training=training)
        x = self.layer2(x, training=training)
        x = self.layer3(x, training=training)
        x = self.layer4(x, training=training)

        x = self.avgpool(x)
        x = self.fc(x)

        return x

    # 一个resblock 中间包含了n个basicblock
    def build_resblock(self, filter_num, blocks, strides=1):

        res_block = Sequential()
        res_block.add(BasicBlock(filter_num, strides=strides))

        for _ in range(1, blocks):
            res_block.add(BasicBlock(filter_num, strides=1))

        return res_block

def resnet10():
    return ResNet([1, 1, 1, 1])

def resnet18():
    return ResNet([2, 2, 2, 2])

def resnet34():
    return ResNet([3, 4, 6, 3])

2. 源码地址

https://github.com/mcuwangzaiacm/ResNet_tensorflow2.0

你可能感兴趣的:(#,分类,ResNet,残差结构,卷积神经网络,分类,VGG)