开发者社区> 技术小能手> 正文

深度学习第19讲:CNN经典论文研读之残差网络ResNet及其keras实现

简介:
+关注继续查看

在 VGG 网络论文研读中,我们了解到卷积神经网络也可以进行到很深层,VGG16 和 VGG19 就是证明。但卷积网络变得更深呢?当然是可以的。深度神经网络能够从提取图像各个层级的特征,使得图像识别的准确率越来越高。但在2014年和15年那会儿,将卷积网络变深且取得不错的训练效果并不是一件容易的事。

深度卷积网络一开始面临的最主要的问题是梯度消失和梯度爆炸。那什么是梯度消失和梯度爆炸呢?所谓梯度消失,就是在深层神经网络的训练过程中,计算得到的梯度越来越小,使得权值得不到更新的情形,这样算法也就失效了。而梯度爆炸则是相反的情况,是指在神经网络训练过程中梯度变得越来越大,权值得到疯狂更新的情形,这样算法得不到收敛,模型也就失效了。当然,其间通过设置 relu 和归一化激活函数层等手段使得我们很好的解决这些问题。但当我们将网络层数加到更深时却发现训练的准确率在逐渐降低。这种并不是由过拟合造成的神经网络训练数据识别准确率降低的现象我们称之为退化(degradation)。

1353079b5027a8ff52d6d14332fd5bb03b8f49fe

由上图我们可以看到 56 层的普通卷积网络不管是在训练集还是测试集上的训练误差都要高于 20 层的卷积网络。是个典型的退化现象。

这退化问题不解决,咱们的深度学习就无法 go deeper. 于是何凯明等一干大佬就发明了今天我们要研读的论文主题——残差网络 ResNet.

残差块与残差网络

要理解残差网络,就必须理解残差块(residual block)这个结构,因为残差块是残差网络的基本组成部分。回忆一下我们之前学到的各种卷积网络结构(LeNet-5/AlexNet/VGG),通常结构就是卷积池化再卷积池化,中间的卷积池化操作可以很多层。类似这样的网络结构何凯明在论文中将其称为普通网络(Plain Network),何凯明认为普通网络解决不了退化问题,我们需要在网络结构上作出创新。

何凯明给出的创新在于给网络之间添加一个捷径(shortcuts)或者也叫跳跃连接(skip connection),这使得捷径之间之间的网络能够学习一个恒等函数,使得在加深网络的情形下训练效果至少不会变差。残差块的基本结构如下:

c90e0c934ee31924f65cb40b9f124b437eab7088

以上残差块是一个两层的网络结构,输入 X 经过两层的加权和激活得到 F(X) 的输出,这是典型的普通卷积网络结构。但残差块的区别在于添加了一个从输入 X 到两层网络输出单元的 shortcut,这使得输入节点的信息单元直接获得了与输出节点的信息单元通信的能力 ,这时候在进行 relu 激活之前的输出就不再是 F(X) 了,而是 F(X)+X。当很多个具备类似结构的这样的残差块组建到一起时,残差网络就顺利形成了。残差网络能够顺利训练很深层的卷积网络,其中能够很好的解决网络的退化问题。

或许你可能会问凭什么加了一条从输入到输出的捷径网络就能防止退化训练更深层的卷积网络?或是是说残差网络为什么能有效?我们将上述残差块的两层输入输出符号改为 和 ,相应的就有:

0cd281fb213b9891173e615e8f951a7991670f43

加入的跳跃连接后就有:148fd99c8c17d608f277603ed3a367171d0621a6

在网络中加入 L2 正则化进行权值衰减或者其他情形下,l+2 层的权值 W 是很容易衰减为零的,假设偏置同样为零的情形下就有 = 。深度学习的试验表明学习这个恒等式并不困难,这就意味着,在拥有跳跃连接的普通网络即使多加几层,其效果也并不逊色于加深之前的网络效果。当然,我们的目标不是保持网络不退化,而是需要提升网络表现,当隐藏层能够学到一些有用的信息时,残差网络的效果就会提升。所以,残差网络之所以有效是在于它能够很好的学习上述那个恒等式,而普通网络学习恒等式都很困难,残差网络在两者相较中自然胜出。

由很多个残差块组成的残差网络如下图右图所示:

9030205da0f462b752ab601c7b82c97a62a9c040

残差块的 keras 实现

要实现一个残差块,关键在于实现一个跳跃连接。实际处理中跳跃连接会随着残差块输入输出大小的不同而分为两种。一种是输入输出一致情况下的 Identity Block,另一种则是输入输出不一致情形下的 Convolutional Block,顾名思义,就是跳跃连接中包含卷积操作,用来使得输入输出一致。且看二者的 keras 实现方法。

Identity Block 的图示如下:

ee8cf8de071e6a57199d8ddef9c09c86e133bf5e

编写实现代码如下:


def identity_block(X, f, filters, stage, block):
"""
Implementation of the identity block as defined in Figure 3
Arguments:
X -- input tensor of shape (m, n_H_prev, n_W_prev, n_C_prev)
f -- integer, specifying the shape of the middle CONV's window for the main path
filters -- python list of integers, defining the number of filters in the CONV layers of the main path
stage -- integer, used to name the layers, depending on their position in the network
Returns:
block -- string/character, used to name the layers, depending on their position in the network X -- output of the identity block, tensor of shape (n_H, n_W, n_C)
""" # defining name basis
conv_name_base = 'res' + str(stage) + block + '_branch'
bn_name_base = 'bn' + str(stage) + block + '_branch' # Retrieve Filters
F1, F2, F3 = filters
# Save the input value. You'll need this later to add back to the main path.
X_shortcut = X
# First component of main path
X = Conv2D(filters = F1, kernel_size = (1, 1), strides = (1,1), padding = 'valid', name = conv_name_base + '2a', kernel_initializer = glorot_uniform(seed=0))(X)
X = BatchNormalization(axis = 3, name = bn_name_base + '2a')(X)
X = Activation('relu')(X)
# Second component of main path
X = Conv2D(filters = F2, kernel_size = (f, f), strides= (1, 1), padding = 'same', name = conv_name_base + '2b', kernel_initializer = glorot_uniform(seed=0))(X)
X = BatchNormalization(axis = 3, name = bn_name_base + '2b')(X)
X = Activation('relu')(X)
# Third component of main path
X = Conv2D(filters = F3, kernel_size = (1, 1), strides = (1, 1), padding = 'valid', name = conv_name_base + '2c', kernel_initializer = glorot_uniform(seed=0))(X)
X = BatchNormalization(axis = 3, name = bn_name_base + '2c')(X)
# Final step: Add shortcut value to main path, and pass it through a RELU activation
X = Add()([X, X_shortcut])
X = Activation('relu')(X)
return X

可见残差块的实现特殊之处就在于添加一条跳跃连接。

Convolutional Block 的图示如下:

3128681d852260ee284a9712a196b3c6dfd64750

编写实现代码如下:


def convolutional_block(X, f, filters, stage, block, s = 2):
"""
Implementation of the convolutional block as defined in Figure 4
Arguments:
f -- integer, specifying the shape of the middle CONV's window for the main path
X -- input tensor of shape (m, n_H_prev, n_W_prev, n_C_prev)
filters -- python list of integers, defining the number of filters in the CONV layers of the main path
stage -- integer, used to name the layers, depending on their position in the network
X -- output of the convolutional block, tensor of shape (n_H, n_W, n_C)
block -- string/character, used to name the layers, depending on their position in the network s -- Integer, specifying the stride to be used Returns:
""" # defining name basis
conv_name_base = 'res' + str(stage) + block + '_branch'
bn_name_base = 'bn' + str(stage) + block + '_branch' # Retrieve Filters
F1, F2, F3 = filters
# Save the input value
X_shortcut = X
##### MAIN PATH ##### # First component of main path
X = Conv2D(filters = F1, kernel_size = (1, 1), strides = (s,s), padding = 'valid', name = conv_name_base + '2a', kernel_initializer = glorot_uniform(seed=0))(X)
X = BatchNormalization(axis = 3, name = bn_name_base + '2a')(X)
X = Activation('relu')(X)
# Second component of main path
X = Conv2D(filters = F2, kernel_size = (f, f), strides = (1,1), padding = 'same', name = conv_name_base + '2b', kernel_initializer = glorot_uniform(seed=0))(X)
X = BatchNormalization(axis = 3, name = bn_name_base + '2b')(X)
X = Activation('relu')(X)
# Third component of main path
X = Conv2D(filters = F3, kernel_size = (1, 1), strides = (1,1), padding = 'valid', name = conv_name_base + '2c', kernel_initializer = glorot_uniform(seed=0))(X)
X = BatchNormalization(axis = 3, name = bn_name_base + '2c')(X)
##### SHORTCUT PATH ####
X_shortcut = Conv2D(filters = F3, kernel_size = (1, 1), strides = (s, s), padding = 'valid', name = conv_name_base + '1', kernel_initializer = glorot_uniform(seed=0))(X_shortcut)
X_shortcut = BatchNormalization(axis = 3, name = bn_name_base + '1')(X_shortcut)
# Final step: Add shortcut value to main path, and pass it through a RELU activation
X = Add()([X, X_shortcut])
X = Activation('relu')(X)
return X

残差网络 resnet50 的 keras 实现

搭建好组件残差块之后就是确定网络结构,将一个个残差块组成残差网络。下面搭建一个 resnet50 的残差网络,其基本结构如下:

CONV2D -> BATCHNORM -> RELU -> MAXPOOL -> CONVBLOCK -> IDBLOCK2 -> CONVBLOCK -> IDBLOCK3 -> CONVBLOCK -> IDBLOCK5 -> CONVBLOCK -> IDBLOCK2 -> AVGPOOL -> TOPLAYER

45a852c5562e59d93e9f79f4315863ae20bf6e2e

编写实现代码如下:


def ResNet50(input_shape = (64, 64, 3), classes = 6):

# Define the input as a tensor with shape input_shape
X_input = Input(input_shape)
# Zero-Padding
X = ZeroPadding2D((3, 3))(X_input)
# Stage 1
X = Conv2D(64, (7, 7), strides = (2, 2), name = 'conv1', kernel_initializer = glorot_uniform(seed=0))(X)
X = BatchNormalization(axis = 3, name = 'bn_conv1')(X)
X = Activation('relu')(X)
X = MaxPooling2D((3, 3), strides=(2, 2))(X)
# Stage 2
X = convolutional_block(X, f = 3, filters = [64, 64, 256], stage = 2, block='a', s = 1)
X = identity_block(X, 3, [64, 64, 256], stage=2, block='b')
X = identity_block(X, 3, [64, 64, 256], stage=2, block='c')
# Stage 3
X = convolutional_block(X, f = 3, filters = [128, 128, 512], stage = 3, block='a', s = 2)
X = identity_block(X, 3, [128, 128, 512], stage=3, block='b')
X = identity_block(X, 3, [128, 128, 512], stage=3, block='c')
X = identity_block(X, 3, [128, 128, 512], stage=3, block='d')
# Stage 4
X = convolutional_block(X, f = 3, filters = [256, 256, 1024], stage = 4, block='a', s = 2)
X = identity_block(X, 3, [256, 256, 1024], stage=4, block='b')
X = identity_block(X, 3, [256, 256, 1024], stage=4, block='c')
X = identity_block(X, 3, [256, 256, 1024], stage=4, block='d')
X = identity_block(X, 3, [256, 256, 1024], stage=4, block='e')
X = identity_block(X, 3, [256, 256, 1024], stage=4, block='f')
# Stage 5
X = convolutional_block(X, f = 3, filters = [512, 512, 2048], stage = 5, block='a', s = 2)
X = identity_block(X, 3, [512, 512, 2048], stage=5, block='b')
X = identity_block(X, 3, [512, 512, 2048], stage=5, block='c')
# AVGPOOL (≈1 line). Use "X = AveragePooling2D(...)(X)"
X = AveragePooling2D((2, 2), strides=(2, 2))(X)
# output layer
X = Flatten()(X)
X = Dense(classes, activation='softmax', name='fc' + str(classes), kernel_initializer = glorot_uniform(seed=0))(X)
# Create model
model = Model(inputs = X_input, outputs = X, name='ResNet50')
return model

这样一个 resnet50 的残差网络就搭建好了,其关键还是在于搭建残差块,残差块搭建好之后只需根据网络结构构建残差网络即可。当然,其间也可以看到 keras 作为一个优秀的深度学习框架的便利之处。


原文发布时间为:2018-10-13

本文作者:louwill

本文来自云栖社区合作伙伴“Python爱好者社区”,了解相关信息可以关注“Python爱好者社区”。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
2023年2月的十篇深度学习论文推荐
本月的论文包括语言模型、扩散模型、音乐生成、多模态等主题。
82 0
学界 | Yoshua Bengio最新修改版论文:迈向生物学上可信的深度学习
深度学习和人工神经网络已经从大脑获得灵感,但大部分是在计算表现形式上的灵感(大多是生物学的,比如 spike 的存在留待考虑)。然而,如今缺少的是对生物神经元中存在的学习规则的一个可信的机器学习说明,从而能够解释一个深度神经网络有效的联合训练,也就是通过一个神经连接长链说明信任分配(credit assignment)。因此,解决信任分配难题也意味着确认神经元与权重,这二者与得到想要的输出和改变参数有关。反向传播提供了一个机器学习答案,然而就像下一段讨论的那样,它并非生物学上可信的。寻找一个生物学上可信的机器学习方法进行深度网络中的信任分配是一个主要的长期问题,也是此论文贡献的方向。
25 0
深度学习领域,你心目中 idea 最惊艳的论文是哪篇?
全卷积神经网络FCN:相信做物体识别检测分割的同学都非常熟悉这个工作,可以看作是开启和奠定了用FCN做实例和像素级别理解的一系列方法的先河,思想非常简单:直接端到端利用全卷积网络预测每个位置的标签。后续非常多的方法,包括Mask R-CNN、各种单阶段检测器、包括3D、video的诸多方法均或多或少受此简单想法的启发。
38 0
2023 年 1 月的5篇深度学习论文推荐
本文整理了 2023 年 1 月5 篇著名的 AI 论文,涵盖了计算机视觉、自然语言处理等方面的新研究。
63 0
《深度学习论文实现:空间变换网络-第一部分》电子版地址
深度学习论文实现:空间变换网络-第一部分
16 0
深度学习:VGG(Vision Geometrical Group)论文详细讲解
深度学习:VGG(Vision Geometrical Group)论文详细讲解
87 0
Alexnet论文泛读:深度学习CV领域划时代论文具有里程碑意义NeurIPS2012
Alexnet论文泛读:深度学习CV领域划时代论文具有里程碑意义NeurIPS2012
86 0
首篇《深度学习不确定性量化: 技术、应用与挑战》2020综述论文,61页pdf582篇文献
在优化和决策过程中,不确定性量化(UQ)在减少不确定性方面起着至关重要的作用。它可以用于解决科学和工程中的各种实际应用。贝叶斯逼近和集成学习技术是目前文献中使用最广泛的两种UQ方法。 在任何基于人工智能的系统中,以一种值得信赖的方式表示不确定性是非常可取的。通过有效地处理不确定性,这样的自动化系统应该能够准确地执行。不确定性因素在人工智能中扮演着重要的角色
294 0
2019年深度学习Top 5研究论文,一文Get硬核干货:XLNet、网络剪枝、StarGAN
近日,在人工智能领域拥有超过10年经验的Rubik’s Code公司评选出了2019年深度学习领域Top5论文。在2019年岁末,这些研究论文对于我们洞见深度学习的前沿进展及发展趋势具有重要的意义。
97 0
3D深度学习火了!NVIDIA NeurIPS论文:训练AI迅速将2D图像转换成3D模型
3D深度学习火了!NVIDIA NeurIPS论文:训练AI迅速将2D图像转换成3D模型
370 0
+关注
技术小能手
云栖运营小编~
文章
问答
来源圈子
更多
+ 订阅
文章排行榜
最热
最新
相关电子书
更多
深度学习框架实战-Tensorflow
立即下载
TensorRT Introduction
立即下载
端上智能-深度学习模型压缩与加速
立即下载