前言:
我们都知道构建一个深度网络有很多好处,比如一个deep Networks可以降低模型复杂度、可以学到更多的features等。在理论上理解,对于一般的深度网络来说,添加更多层应该更容易降低训练误差。但是实际情况是,添加很多层以后,模型的训练误差不但没有降低还上升了。
原因是一般很深的网络存在梯度消失的问题。在backward propagation的过程中,每一步都需要乘以权重,最后会导致梯度快速衰减到零。所以越靠前的层的梯度消失的越快。
而应用经典的残差网络ResNets(Residual Networks)就可以改善这些问题。
原理:
ResNets连接方式称为“shortcut”或“skip connection”,即某层的输出会跳过其后面的一层或者多层,作为后面层的输入。
一般网络如图左,ResNets网络如图右
根据输入输出的维度是否相同,我们可以将残差块(ResNets blocks)划分为两种不同的块(The identity block和The convolutional block)。
实现
一、The identity block:输入激活和输出激活的维度不同。
现在实现一个如下图所示的残差块。
main path(下面那条路径)第一部分Conv2D的滤波器个数为F1,shape(1,1),stride(1,1),padding是“valid”,命名为conv_name_base’’。BatchNorm是对通道轴规范化,为了加快训练。然后引用ReLU激活。后面的规则类似,详见代码。
def identity_block(X, f, filters, stage, block):
"""
输入参数:
X -- 输入张量,shape (m, n_H_prev, n_W_prev, n_C_prev)
f -- 整型,shape(m, n_H_prev, n_W_prev, n_C_prev)
filters -- 整型列表,定义了每个卷积层的滤波器数量
stage -- 整型,位置参数
block -- 字符串/字符,位置参数
Returns:
X -- the identity block 的输出, shape (n_H, n_W, n_C),张量
"""
# 定义名字
conv_name_base = 'res' + str(stage) + block + '_branch'
bn_name_base = 'bn' + str(stage) + block + '_branch'
# 检索滤波器
F1, F2, F3 = filters
# 保存一次输入值,后面需要用X_shortcut
X_shortcut = X
# 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)
# 第二部分
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)
# 第三部分
X = Conv2D(filters = F3, kernel_size = (1, 1), strides = (1, 1), padding = 'valid', name = conv_name_base + '2c', kernel_initializer = glorot_uniform(seed=0