TensorFlow学习记录:ResNet卷积神经网络模型

本文介绍了ResNet残差网络的设计原理,旨在解决深度学习中梯度消失问题。通过引入“身份近路连接”,ResNet允许信息直接传递,从而避免性能退化。文中详细阐述了残差学习单元的结构,包括2层和3层单元的不同,并展示了ResNet如何通过堆叠残差学习单元实现不同深度的网络。最后,文章讨论了使用TensorFlow实现ResNet-152并应用于CIFAR-100数据集的图像分类任务。
摘要由CSDN通过智能技术生成

1.Residual Neural Network简介

自AlexNet之后,随着卷积神经网络的不断加深(例如VGGNet和Inception V1分别有19层和22层深)以及一些优化网络性能的想法不断被提出,卷积神经网络能够达到的错误率也在逐渐下降。但是,如果只是简单的将层叠加在一起,增加网路深度,并不会起到什么作用。在增加网络深度的同时,我们还要考虑到梯度消失的问题。具体来讲,因为梯度反向传播到前层,重复乘法可能使梯度无穷小,梯度消失问题也就因此而出现。梯度消失造成的结果是,随着网络层的加深,其性能(或者说准确率)趋于饱和,更严重的就是准确率发生退化(准确率不升反降)。
为了解决梯度消失的问题,ResNet引入了所谓的“身份近路连接(Identity Shortcut Connection)”的核心思想。其灵感来源:对于一个达到了饱和准确率的比较浅的网络,当在后面加上几个全等映射层(即y=x)时,误差不会因此而增加。也就是说,更深的网络不应该带来训练集上误差的上升。
加入全等映射层的做法使得ResNet允许原始输入信息直接传输到后面的层中。假设某一段神经网络的输入是x,经过这一段网络处理之后可以得到期望的输出F(x),现在将输入x传到输出作为下一段网络的初始结果,那么此时我们的输出就不再是一个简单的输出F(x),而是输出与输入的差别H(x)=F(x)+x。下图展示了这种结构(图片来源于百度)。

2.Residual Network中的残差学习单元

接下来我们看一下ResNet中的残差学习单元。残差学习单元就是ResNet中允许原始输入信息直接传输到后层的执行结构。如下图所展示的是在ResNet论文《Deep Residual Learning for Image Recognition》(论文地址:https://arxiv.org/pdf/1512.03385.pdf)中提出的两种残差学习单元,左侧是2层的残差学习单元,右侧是3层的残差学习单元。2层的残差学习单元中包含两个输出通道数一致(因为残差还要计算期望输出减去输入,这就要求输入,输出维度保持一致)的3*3卷积;而3层的残差学习单元则先使用了1*1的卷积,中间是3*3的卷积,最后也是1*1的卷积。至于为什么要这样设计卷积网络,原论文中的解释是这是为了减少训练时间,我们可以先在1*1卷积层将通道数变少再进行下面一步33的卷积。之后第三步的1*1卷积是为了还原一开始通道数的尺寸。这么一看很明显了其实很就是为了第二步骤33的卷积提速,才先将输入缩小,卷积完成之后再进行还原,所以说最后我们的通道数是没有发生任何改变的。不过论文最后还提到,使用这种3个卷积层的时间复杂度和使用2个卷积层(如下图左)的时间复杂度相近,这里我们不再深究。

在这里插入图片描述

残差学习单元的引入相当于改变了ResNet的学习目标,如果将卷积操作的结果作为没有加入残差前网络的期望输出H(x)=F(x),那么残差单元学习的将不再是H(x),而是输出和输入的差别H(x)-x,这就是“残差”的含义。下面我在实现的时候由于看了很多文章也没找到如何学习“残差”来优化这个网络,所以我自己写了个简单的梯度下降办法来优化这个网络(而且极有可能是错误的),如果有网友知道怎么实现优化这个网络,请留言,我想虚心请教一下。
如下图所示为ResNet网络将残差学习单元堆叠起来的情况。从图中可以到,ResNet有很多旁路的支线将上一残差学习单元的输入直接参与到输出,这样就能使得后面的残差学习单元可以直接对残差进行学习。在网络中,这样的连接方式也被称为Shrotut或SkipConnections。

在这里插入图片描述

将上图与之前的卷积神经网络结构图进行对比,可以发现ResNet和直连(即不存在Shortcut连接)卷积神经网络最大区别在于,ResNet直接将输入信息沿着捷径传输到输出的方式能够在一定情度上保护信息的完整性,同时,这样的方式也使得学习的目标更加简明。对于降低学习难度而言,ResNet的这些做法都是非常有帮助的。在提出ResNet的那篇论文中,其作者也分析了直连卷积神经网络和ResNet之间存在的其他异同点,如果感兴趣的话,可以获取那篇论文并仔细研究。
值得一提的是,在提出ResNet的那片论文中,作者尝试了将网络扩展到不同的深度,如下图所示。从图中可以看到,这些不同网络配置的ResNet有着相似的基础结构,那就是残差学习单元。以层数为152的ResNet中层名为“conv_3”的层为例,这个层由8个残差学习单元叠加而来(一般会将由残差学习单元叠加而成的结构称为一个残差学习模块,下面代码实现的时候也是这样安排),每个残差单元有3个卷积层,其中,“3*3,128”中的128指的就是这一层的输出深度。
引入残差学习单元结构的ResNet成功消除了之前卷积神经网络中随着层数加深而导致的测试集准确率下降的问题。随着网络层数的加深,ResNet在测试集上得到的错误率也会逐渐地减少。

3.使用TensorFlow实现ResNet-152残差网络并进行CIFAR-100图像分类

实现ResNet使用的也是先搭建网络,然后对CIFAR-100数据集进行分类。由于代码较长,所以整个实现过程被分成了三个文件read_Cifar100.py、ResNet_struct.py和ResNet_Cifar100.py,其中,read_Cifar100.py用于预处理和读取Cifar-100的图片,ResNet_struct.py用于网络的搭建,而ResNet_Cifar100.py则用于网络的评测。我们首先实现read_Cifar-100.py
import pickle   # 用于序列化和反序列化
import numpy as np  
import os  
import cv2
import matplotlib.pyplot as plt
import tensorflow as tf

# data_dir = "/content/drive/My Drive/cifar-100-python"
data_dir = "D:/Google Cloud/cifar-100-python"

'''
字典形式的数据:
cifar100 data 文件的内容结构: 
    { 
    "data" : [(R,G,B, R,G,B ,....),(R,G,B, R,G,B, ...),...]    # 50000张图片,每张: 32 * 32 * 3
    "coarse_labels":[0,...,19],                         # 0~19 super category 
    "filenames":["volcano_s_000012.png",...],   # 文件名
    "batch_label":"", 
    "fine_labels":[0,1...99]          # 0~99 category 
    } 
'''

class Cifar100DataReader():  
    def __init__(self,cifar_folder,batch_size,onehot=True):
        self.batch_size = batch_size  
        self.cifar_folder=cifar_folder  
        self.onehot=onehot  
        self.data_label_train=None            # 训练集
        self.data_label_test=None             # 测试集
        self.batch_index=0                    # 训练数据的batch块索引
        self.test_batch_index=0               # 测试数据的batch块索引
        f=os.path.join(self.cifar_folder,"train")  # 训练集有50000张图片,100个类,每个类500张
        # print ('read: %s'%f  )
        fo = open(f, 'rb')
        self.dic_train = pickle.load(fo,encoding='bytes')
        fo.close()
        self.data_label_train=list(zip(self.dic_train[b'data'],self.dic_train[b'fine_labels']) ) #label 0~99  
        np.random.shuffle(self.data_label_train)           
 
    
    def dataInfo(self):
        print (self.data_label_train[0:2] )# 每个元素为二元组,第一个是numpy数组大小为32*32*3,第二是label
        print (self.dic_train.keys())
        print (b"coarse_labels:",len(self.dic_train[b"coarse_labels"]))
        print (b"filenames:",len(self.dic_train[b"filenames"]))
        print (b"batch_label:",len(self.dic_train[b"batch_label"]))
        print (b"fine_labels:",len(self.dic_train[b"fine_labels"]))
        print (b"data_shape:",np.shape((self.dic_train[b"data"])))
        print (b"data0:",type(self.dic_train[b"data"][0]))
 
 
    # 得到下一个batch训练集
    def next_train_data(self):  
        """ 
        return list of numpy arrays [na,...,na] with specific batch_size 
                na: N dimensional numpy array  
        """            
        if self.batch_index<len(self.data_label_train)/self.batch_size:  
            # print ("batch_index:",self.batch_index  )
            datum=self.data_label_train[self.batch_index*self.batch_size:(self.batch_index+1)*self.batch_size]  
            self.batch_index+=1  
            return self._decode(datum,self.onehot)  
        else:  
            self.batch_index=0  
            np.random.shuffle(self.data_label_train)  
            datum=self.data_label_train[self.batch_index*self.batch_size:(self.batch_index
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值