Tensorflow 2.0 --- ResNet 实战 CIF

作者: Hongtao洪滔 | 来源:发表于2020-01-13 22:27 被阅读0次
    img

    Image from unsplash.com by @ripato

    前面的文章我们学习了如何使用 Tensorflow 2.0 训练卷积神经网络,今天我们将学习如何用卷积神经网络的升级版 ResNet 来实战 CIFAR100 数据集。

    关注微信公众号获取源代码(二维码见文末)

    1. 深度神经网络的困扰

    自从2012 年,5层卷积神经网络在 ILSVRC12 挑战赛 ImageNet 数据集分类取得冠军之后,卷积神经网络再次火爆起来。随着计算机的运算能力的提高,越来约深的神经网络得到了应用。随着神经网络深度的增加,神经网络的准确率也得到了相应的提升。经典网络如 VGG 就将网络深度提高到了19层。

    然而神经网络超过20层之后,错误率不仅不会下降,反而上升了,并且训练起来也越来越困难,这主要是由于深度神经网络存在梯度消失和梯度爆炸的问题。在较深层数 的神经网络中,梯度信息由网络的末层逐层传向网络的首层时,传递的过程中会出现梯度 接近于 0 或梯度值非常大的现象。网络层数越深,这种现象可能会越严重。

    VGG13 网络模型结构

    2. ResNet 简介

    ResNet 的发明就是为了解决深度神经网络梯度消失和梯度爆炸的问题。既然加深网络会导致上诉问题,是否可以给神经网络“短路”,让其有退回稍微浅层结构的能力,至少不能比浅层神经网络差吧。

    这种“短路”操作,就是在若干个卷积神经层中间加一条路径,神经网络可以选择

    1. 经过这几个卷积层完成特征变换。
    2. 直接跳过这几个卷积层
    3. 将上面两者结合起来
    添加短路的 VGG13 网络结构

    �����ResNet 由华人科学家何凯明等人在2015年发明,如今已经能够将神经网络扩展到一千多层。感兴趣的读者请前往这里阅读论文原文

    3. ResNet 结构

    论文原文介绍了 18层,34层,50层,101层和152层一共五种ResNet,虽然深度不同,但是结构框架是一致的,只要掌握结构规律,可以将网络结构的层数无限扩散下去。

    这里以18层的 ResNet18 为例,介绍一下其基本结构:

    最里面的一层是ResNet 最基本的结构,我们这里叫做BasicBlock, 该 Block 由两层卷积神经网络外加一个 “短路” 联接构成。若干个 BasicBlock 又可以构建一个 ResBlock, 4个 ResBlock 外加一个预处理层和一个全连接层,一共就构成了 18 层 (2 x 2 x 4 + 2 )的 ResNet18 结构.

    image

    4. ResNet18 代码实战

    首先我们创建最基本单元 BasicBlcok

    这里需要注意以下几点:

    1. BasicBlock 类继承自 tf.keras.layers.Layer 类。
    2. num_filters 缩小图片的尺寸。
    3. identity_layer 必须与卷积神经网络的输出尺寸一致,这里使用 1x1 的filter 来调节。
    4. 涉及到 batch normaisation 的记住传入training 参数,training在验证的时候需要设置为 False .
    class BasicBlock(layers.Layer):
        def __init__(self, num_filters, stride = 1):
            super().__init__()
    
            self.conv1 = layers.Conv2D(num_filters,(3,3),strides=stride,padding='same')
            self.bn1 = layers.BatchNormalization()
            self.relu = layers.ReLU()
    
            self.conv2 = layers.Conv2D(num_filters,(3,3),strides=1,padding='same')
            self.bn2 = layers.BatchNormalization()
    
            if stride != 1:
                self.identity_layer = layers.Conv2D(num_filters,(1,1),strides=stride)
            else:
                self.identity_layer = lambda x:x
    
        def call(self, input, training = None):
            output = self.conv1(input)
            output = self.bn1(output, training=training)
            output = self.relu(output)
    
            output = self.conv2(output)
            output = self.bn2(output,training=training)
    
            identity_out = self.identity_layer(input)
    
            output = layers.add([output,identity_out])
            output = tf.nn.relu(output)
    
            return output
    

    然后,用基本单元构建 ResBlock 层,然后再由ResBlock 和 Pre_process 以及最后一个全链接层构建一个 ReNet 模型

    这里需要注意以下几点:

    1. ResNet class 模型继承 keras.Model.
    2. Layer_dim 存储 resblock 维度信息
    3. layers.GlobalAveragePooling2D() 不关心图片尺寸,可以在 h * w 纬度上平均尺化。
    4. 这里模型输出是 logits 没有加 softmax 层
    class ResNet(keras.Model):
        def __init__(self, layer_dim, class_num): #layer_dim res18:[2,2,2,2] or res34[3,4,6,3]
            super().__init__()
            self.pre_process = Sequential([layers.Conv2D(64,(3,3),strides = 1, padding = 'same'),
                                           layers.BatchNormalization(),
                                           layers.ReLU(),
                                           layers.MaxPool2D((2,2),strides= 1, padding = 'same')
                                          ])
    
            self.resblock0 = self.build_resblock(64, layer_dim[0], stride = 1)
            self.resblock1 = self.build_resblock(128, layer_dim[1], stride = 2)
            self.resblock2 = self.build_resblock(256, layer_dim[2], stride = 2)
            self.resblock3 = self.build_resblock(512, layer_dim[3], stride = 2)
    
            self.avg_pool = layers.GlobalAveragePooling2D()
            self.dense = layers.Dense(class_num)
    
        def build_resblock(self,filter_num,basic_block_num, stride):
    
            resblock = Sequential()
            for _ in range(basic_block_num):
                basic_block = BasicBlock(filter_num, stride = stride)
                resblock.add(basic_block)
    
            return resblock
    
        def call(self, input, training = None):
            output = self.pre_process(input, training = training) #[b, 64, h, w]
    
            output = self.resblock0(output, training = training) #[b, 64, h, w]
            output = self.resblock1(output, training = training) #[b, 128, h, w]
            output = self.resblock2(output, training = training) #[b, 256, h, w]
            output = self.resblock3(output, training = training) #[b, 512, h, w]
    
            output = self.avg_pool(output) #[b, 512, 1,1]
            output = self.dense(output) #[b, class_num]
    
            return output
    
    

    最后,ResNet18 中间是 [2, 2, 2, 2] 结构,既4个resblock, 每个resblock包涵两个BasicBlcok。 ResNet34 中间为[3, 4, 6, 3]结构。实例ResNet18 和 ResNet34 也非常简单,仅需要传入不同的 layer_dim 即可。

    ## ResNet 18
    model = ResNet([2,2,2,2], 100)
    
    ## ResNet 34
    model = ResNet([3,4,6,3], 100)
    

    模型训练和验证过程请参考之前文章或者查看源代码,就不在这里赘述了。


    相关文章

    Tensorflow2.0-数据加载和预处理
    Tensorflow 2.0 快速入门 —— 引入Keras 自定义模型
    Tensorflow 2.0 快速入门 —— 自动求导与线性回归
    Tensorflow 2.0 轻松实现迁移学习
    Tensorflow入门——Eager模式像原生Python一样简洁优雅
    Tensorflow 2.0 —— 与 Keras 的深度融合


    首发steemit

    欢迎扫描二维码关注我的微信公众号“tensorflow机器学习”,一起学习,共同进步

    相关文章

      网友评论

        本文标题:Tensorflow 2.0 --- ResNet 实战 CIF

        本文链接:https://www.haomeiwen.com/subject/utznactx.html