ResNet 残差学习单元(Residual Unit)

这篇笔记记录残差学习块(Residual Unit)的作用及实现。

ResNet(Residual Neural Network)是在2015年提出,其对于之前的深层网络对大特点是其结构中的残差学习块。

在网络不断加深的过程中会出现Degradation的现象,也就是说再持续增加网络的深度导致准确率下降。ResNet的灵感源于:一个比较浅的网络达到饱和准确率后,在其后加上几个y=x全等映射层,至少不会使误差增加

Residual Unit

假设有一段网络的输入是x,它回经过若干层非线性变换得到结果y,假设其期望输出为y_,同时x也作为输出的一部分加在y中。那么网络的学习目标是y_-x。ResNet相当于改变了学习的目标,它不再是完整的输出y_, 而是输出与输入的差y_-x,也就是Residual(残差)。如下图:

图中x为此残差块的输入,y_为期望输出,y为经过两个卷积层的输出。

为什么ResNet回会有效,可以这样理解:传统的卷积层或全连接层在信息传递时,或多或少存在信息丢失的问题,ResNet从一些程度上解决了这个问题,它通过直接将输入加到输出上,保护了信息的完整性。

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
def residual_block(x, output_channel):
"""residual connection implementation"""
input_channel = x.get_shape().as_list()[-1]
if input_channel * 2 == output_channel: #如果本层的输出通道数是输入通道数的2倍
increase_dim = True
strides = (2, 2)
elif input_channel == output_channel: #如果本层的输出通道数与输入通道数相同
increase_dim = False
strides = (1, 1)
else:
raise Exception("input channel can't match output channel")
conv1 = tf.layers.conv2d(x,
output_channel,
(3,3),
strides = strides,
padding = 'same',
activation = tf.nn.relu,
name = 'conv1')
conv2 = tf.layers.conv2d(conv1,
output_channel,
(3, 3),
strides = (1, 1),
padding = 'same',
activation = tf.nn.relu,
name = 'conv2')
# 如果通道数翻倍了
if increase_dim:
# [None, image_width, image_height, channel] -> [,,,channel*2]
pooled_x = tf.layers.average_pooling2d(x,
(2, 2),
(2, 2),
padding = 'valid')
# 使padded_x 与 conv2 大小相同
padded_x = tf.pad(pooled_x,
[[0,0],
[0,0],
[0,0],
[input_channel // 2, input_channel // 2]])
# 如果通道数未翻倍
else:
padded_x = x # 全等映射

output_x = conv2 + padded_x
return output_x

本笔记只是记录了残差学习块的作用及实现,完整的ResNet实现看这里