深度残差学习在图像识别中的应用综述

第1节:要点

1.1Shallow Vs Deep Models

人工神经网络性能的关键在于网络的深度。神经网络同时作为特征提取器和分类器。神经网络充当自动特征提取器的能力大大提高了它们推广新问题的能力,例如对未见的图像进行分类。通过手动特征工程,在图像识别等领域非常困难,需要手工制作分类新的未见图像所需的特征。浅层网络完全能够自动提取特征,但由于表示深度较低,因此无法提取细粒度特征,最终使模型得到正确推广。另一方面,更深的模型能够提取低级,中级和高级功能。因此,它们作为比浅层模型更优异的特征提取器,这使得它们在浅层模型的分类方面表现得更好。事实证明,深度学习各个领域的所有领先模型都利用深度概念。

然而,深度问题的一个关键问题是随着网络的深入,信息的丢失。这背后的一个简单直觉就是将决策视为事件历史的一个函数。事件顺序发生,过去的事件影响未来的事件,从这个角度考虑; 一个决策者只能看到最后的过去事件。这样一个决策者做出了一个宏观假设的决定,即过去的事件已经编码了我们需要了解的所有先前事件的全部内容。对于非常短的事件历史,这种假设可以保持正确,因为在紧密相继的事件之间经常存在强关联,但是,当历史很长时,在每个时间步,关于过去如何影响未来的信息逐渐丢失,我们深入未来,最终,我们变得非常短视,

随着图层变得非常深,深度问题就会暴露出来。作者在CIFAR10数据集上训练了一个20层网络和一个56层网络。令人惊讶的是,20层网络胜过了56层网络。因此,很明显,简单地堆叠更多层不足以优化深度神经网络。56层网络也比20层网络有更高的训练错误,这清楚地表明它不是一个过度拟合的问题,因此众所周知的正常化技术如丢失不能用于解决问题。

1.2Residual Functions

为了解决与超深网络相关的梯度消失问题,作者引入了残差连接到网络中。残差连接只是下一层和层之间的连接。

这个想法在下面的图表中清楚地说明:

深度残差学习在图像识别中的应用综述

在上图中,普通网络简单地将信息从一层发送到下一层,关于图像的过去状态的信息高度受限,并且所有激活必须基于新特征,另一方面,残差连接采用将来自图层t的未来映射添加到层t + 2的输出中。

这相当于学习残差函数y = f(x)+ x

在无残差的连接直接前馈网络中,层Ť仅在层依赖于数据笔- 1与层T -1编码所有的前面的层的结果,在另一方面,残差的连接看更远的过去,将考虑来自T - 2层的信息。

这个非常简单但强大的想法使作者能够越来越精确地训练超过100层网络。

值得注意的是,虽然作者最初认为残差连接对深度非常重要,但未来的工作已经证明,残差网络可以提高浅层和深层神经网络的性能。这与我们通过提供有关数据原始状态的充足数据来提高残差函数的说明相一致。

第2节:相关工作

添加来自以前时间步骤的特征已被用于涉及多层完全连接网络的各种任务以及卷积神经网络。其中最值得注意的是Srivastava等人提出的Highway networks。Highway networks具有残差连接,但与resnet不同,它们的残差连接被门控。因此,过去的信息流由门控机制允许通过的数据量决定。这个想法主要受LSTMs中的门控机制的启发。

而残留网络有形式

Y = f(x )+ x

Highway networks具有这种形式

Y = f(x ). sigmoid(Wx + b) + x. (1 —sigmoid (Wx + b))

请注意,在Highway networks公式中,sigmoid函数采用一般形式1 /(1 + e ^ -x), sigmoid函数始终输出0-1范围内的值,参数W和b是学习权重,而控制sigmoid函数输出的偏置。一个非残差网络可以看作是一个特殊的Highway network,sigmoid门的输出为1。

给定sigmoid(Wx + b)= 1

y = f(x).+ x. (1–1) = f(x)

当S形门的输出为0时,Highway network成为身份识别功能

给定sigmoid(Wx + b)= 0

y = f(x).0+ x. (1–0) = x

Highway networks使信息能够从过去流入,但由于门控功能,信息流动仍然受阻。因此,一个有19层的Highway networks比32层的Highway networks要好。

第3节:网络结构

Resnets具有非常一致的结构,它们在结构上与Simonyan等人的VGG相似。它们由多层残差模块组成,而这些模块又被分组为残差块。

3.1:Resnet模块

Resnet模块有两种变体; 第一个由两层3×3卷积组成,另一个更受欢迎称为瓶颈层,因为它由1×1卷积组成,可以将通道数减少4倍,然后是3×3的卷积,最后展开的层返回到1×1卷积ç。瓶颈块的动机是降低网络的计算成本,因为1×1卷积比3×3卷积要便宜9倍,所以它们用于最小化进入3×3卷积的通道数量。

瓶颈模块可以在keras中构建如下:

def resnet_module(x,filters,pool=False):

res = x

stride = 1

if pool:

stride = 2

res = Conv2D(filters,kernel_size=1,strides=2,padding="same")(res)

x = Conv2D(int(filters/4),kernel_size=1,strides=stride,padding="same")(x)

x = BatchNormalization()(x)

x = Activation("relu")(x)

x = Conv2D(int(filters/4), kernel_size=3, strides=1, padding="same")(x)

x = BatchNormalization()(x)

x = Activation("relu")(x)

x = Conv2D(filters, kernel_size=1, strides=1, padding="same")(x)

x = BatchNormalization()(x)

x = add([x,res])

x = Activation("relu")(x)

return x

要清楚地了解上述代码,请考虑我们仔细绘制的这张图片。

深度残差学习在图像识别中的应用综述

在上图中,输入x进入模块,C代表输出通道的数量,我们将输入传递到通道数等于C / 4的1 x 1 conv,然后进行批量归一化和relu。对于3 x 3次转换重复该设置,最后,我们将3 x 3转换的输出通过具有C通道的1 x 1转换,然后仅进行批量归一化。

此外,在开始时我们让残差等于输入,但是如果我们要汇集,这通常涉及到信道数量的两倍,那么残差是带有等于C的信道的跨越1×1 conv的结果。如果这没有完成,残差和输出的尺寸不匹配。

最后,我们将残差与最后1 x 1 Conv - BN层的输出相加。然后,我们将relu应用于添加的结果。

一个resnet块是瓶颈层的堆栈。

#A resnet block consisting of N number of resnet modules, first layer has is pooled.

def resnet_block(x,filters,num_layers,pool_first_layer=True):

for i in range(num_layers):

pool = False

if i == 0 and pool_first_layer: pool = True

x = resnet_module(x,filters=filters,pool=pool)

return x

注意在上面,我们只在每个块的第一层中设置了pool = True,这在上面的代码中有明确的定义。

最后我们定义完整的resnet(Python实现)

import keras

from keras.layers import *

from keras.models import Model

#A single resnet module consisting of 1 x 1 conv - 3 x 3 conv and 1 x 1 conv

def resnet_module(x,filters,pool=False):

res = x

stride = 1

if pool:

stride = 2

res = Conv2D(filters,kernel_size=1,strides=2,padding="same")(res)

x = Conv2D(int(filters/4),kernel_size=1,strides=stride,padding="same")(x)

x = BatchNormalization()(x)

x = Activation("relu")(x)

x = Conv2D(int(filters/4), kernel_size=3, strides=1, padding="same")(x)

x = BatchNormalization()(x)

x = Activation("relu")(x)

x = Conv2D(filters, kernel_size=1, strides=1, padding="same")(x)

x = BatchNormalization()(x)

x = add([x,res])

x = Activation("relu")(x)

return x

def resnet_first_module(x,filters):

res = x

stride = 1

res = Conv2D(filters,kernel_size=1,strides=1,padding="same")(res)

x = Conv2D(int(filters/4),kernel_size=1,strides=stride,padding="same")(x)

x = BatchNormalization()(x)

x = Activation("relu")(x)

x = Conv2D(int(filters/4), kernel_size=3, strides=1, padding="same")(x)

x = BatchNormalization()(x)

x = Activation("relu")(x)

x = Conv2D(filters, kernel_size=1, strides=1, padding="same")(x)

x = BatchNormalization()(x)

x = add([x,res])

x = Activation("relu")(x)

return x

#A resnet block consisting of N number of resnet modules, first layer has is pooled.

def resnet_block(x,filters,num_layers,pool_first_layer=True):

for i in range(num_layers):

pool = False

if i == 0 and pool_first_layer: pool = True

x = resnet_module(x,filters=filters,pool=pool)

return x

#The Resnet model consisting of Conv - block1 - block2 - block3 - block 4 - FC with Softmax

def Resnet(input_shape,num_layers=50,num_classes=10):

if num_layers not in [50,101,152]:

raise ValueError("Num Layers must be either 50, 101 or 152")

block_layers = {50: [3, 4, 6, 3],

101: [3, 4, 23, 3],

152: [3,8,36,3]

}

block_filters = {50: [256, 512, 1024, 2048],

101: [256, 512, 1024, 2048],

152: [256, 512, 1024, 2048]

}

layers = block_layers[num_layers]

filters = block_filters[num_layers]

input = Input(input_shape)

x = Conv2D(64,kernel_size=7,strides=2,padding="same")(input)

x = BatchNormalization()(x)

x = Activation("relu")(x)

x = MaxPooling2D(pool_size=(3,3),strides=(2,2))(x)

x = resnet_first_module(x,filters[0])

for i in range(4):

num_filters = filters[i]

num_layers = layers[i]

pool_first = True

if i == 0:

pool_first = False

num_layers = num_layers - 1

x = resnet_block(x,filters=num_filters,num_layers=num_layers,pool_first_layer=pool_first)

x = GlobalAveragePooling2D()(x)

x = Dense(num_classes)(x)

x = Activation("softmax")(x)

model = Model(inputs=input,outputs=x,name="Resnet{}".format(num_layers))

return model

上面的代码是高度模块化的,可以扩展到数千层。这里仅支持50,101和152层,如果提供了其他值,则会引发值错误,并且可以修改代码以扩展到超深度网络。

Resnet由4个块组成。每个块的层数和过滤器数量由block_layers字典确定。

block_layers = {50:[3,4,6,3],

101

:[3,4,23,3] ,152:[3,8,36,3]

所述block_layers定义层的总数目映射到每个块的模块的数量的字典。对于50层网络,第一个模块中有3个模块,第二个模块中有4个模块,第三个模块中有6个模块,第四个模块中有3个。

请注意,假设每个模块由3个卷积层组成,如果计算每个配置的总层数,您会意识到它等于num_layers - 2。因此,对于50层,我们将有(3 + 4 + 6 + 3)* 3 = 48。

对于101层,我们会有99层,而对于152层网络我们会有150层。

原因是,第一个块之前有一个卷积层,最后是一个将特征映射映射到类预测的完全连接层。这两层被添加到块中的层数,以根据所需的层数制作50,101和152层。

所述block_filters字典确定过滤器对每个块

}

block_filters = {50:[256,512,1024,2048],

101:[ 256,512,1024,2048 ],

152:[256,512,1024,2048]

}

对于所有不同的配置,第一个块对于每个块中的所有模块具有256个过滤器,对于第二个块具有512个过滤器,对于第三个块具有1024个过滤器,对于第四个块为2048。

最后,GlobalAveragePooling2D应用于输出特征映射,这只是一个标准的AveragePooling,其池大小等于特征映射的宽度和高度,因此,结果将是1 x 1 x个过滤器,因为每个过滤器将变为1 x 1功能地图。

这个输出被传递到softmax的完全连接层。

结论

神经网络的深度对于获得更好的性能是非常重要的。当神经网络层非常深时,由于信息丢失导致消失梯度,他们会表现出更高的训练和验证损失。为了保证更深的网络总是比浅层网络产生更好的精度,Resnet的作者提出使用残差块,从过去引入信息来弥补信息损失。这项技术可以训练非常深的网络,从而在标准基准测试中达到最新的精度。

相关推荐