TyranitarX Connect.

残差网络(Residual Network)

Word count: 690Reading time: 3 min
2019/07/09 Share

ResNet(Residual Neural Network)由微软研究院的Kaiming He等四名华人提出,通过使用ResNet Unit成功训练出了152层的神经网络,并在ILSVRC2015比赛中取得冠军,在top5上的错误率为3.57%,同时参数量比VGGNet低,效果非常突出。ResNet的结构可以极快的加速神经网络的训练,模型的准确率也有比较大的提升。同时ResNet的推广性非常好,甚至可以直接用到InceptionNet网络中。

ResNet的主要思想是在网络中增加了直连通道,即Highway Network的思想。此前的网络结构是性能输入做一个非线性变换,而Highway Network则允许保留之前网络层的一定比例的输出。ResNet的思想和Highway Network的思想也非常类似,允许原始输入信息直接传到后面的层中,如下图所示。

1.提出原因

VGG-NET将网络加深到一定的层次,提高了学习的准确率。但是加深到更深层次后,反而准确率下降。究其原因是深层网络更难优化,而非深层网络学习不到东西。(比如说增加Y=X的网络,实际上深层和浅层学习到的东西是相同的)
因此有了如下的残差网络结构:

  • identity部分为恒等变换
  • F(x)是残差学习

2.模型结构

  • 先用一个普通的卷积层,stride = 2
  • 再经过一个3*3的max_polling
  • 再经过残差结构
  • 没有中间的全连接层,直接到输出

残差结构使得网络需要学习的知识变少,容易学习。
残差结构使得每一层的数据分布接近,容易学习。

3.代码实现

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
def redidual_block(x, output_channel):
"""redidual connection implementation"""
input_channel = x.get_shape().as_list()[-1]
if input_channel * 2 == output_channel:
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:
pooled_x = tf.layers.average_pooling2d(
x,
(2,2),
(2,2),
padding = 'valid'
)
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

def res_net(x,num_residual_blocks,num_filter_base,class_num):
'''
x: 输入数据
num_residual_blocks: 残差连接块数 eg:[3, 4, 6, 3]
num_filter_base: 最初通道数
class_num: 泛化不同数据集
'''
# 降采样数
num_subsampling = len(num_residual_blocks)
layers = []
# x : [None, width, height, channel] ->[width, height, channel]
# input_size = x.getshape().as_list()[1:]
with tf.variable_scope('conv0'):
conv0 = tf.layers.conv2d(
x,
num_filter_base,
(3,3),
strides = (1,1),
activation = tf.nn.relu,
padding = 'same',
name = 'conv0'
)
layers.append(conv0)
for sample_id in range(num_subsampling):
for i in range(num_residual_blocks[sample_id]):
with tf.variable_scope('conv%d_%d' % (sample_id, i)):
conv = redidual_block(
layers[-1],
num_filter_base * (2 ** sample_id)
)
layers.append(conv)
with tf.variable_scope('fc'):
global_pool = tf.reduce_mean(layers[-1], [1,2])
logits = tf.layers.dense(global_pool, class_num)
layers.append(logits)

return layers[-1]
CATALOG
  1. 1. 1.提出原因
  2. 2. 2.模型结构
  3. 3. 3.代码实现