文章背景
最近的工作显示如果在靠近输入输出的层数之间加入短连接(short connections)能大幅度的提升训练的深度、准确度与效率。基于这种思想,这篇论文提出了一种全新的网络结构Dense Convolutional Network(DenseNet):将每一层与出层之外的所有层连接起来。也就是说对于有L层的传统卷积网络而言,有L个连接,但是对于DenseNet而言有L(L+1)/2个连接。
思想
全文有两个公式第一个公式是关于ResNet的,其中
l
\ {l}
l表示层数,
x
l
\ {x_l}
xl表示第
l
\ {l}
l的输出,
H
\ {H}
H表示非线性变换。
x
l
=
H
l
(
x
l
−
1
)
+
x
l
−
1
\ {x_l= H_l(x_{l-1}) + x_{l-1}}
xl=Hl(xl−1)+xl−1、
第二个公式是关于DenseNet的
x
l
=
H
l
(
[
x
0
,
x
1
,
x
2
,
.
.
.
,
x
l
−
1
]
)
\ x_l =H_l([x_0,x_1,x_2,...,x_{l-1}])
xl=Hl([x0,x1,x2,...,xl−1])
从这两个公式可以很清楚的看出两种网络思路上的区别,resnet是通过将这层的输出与前一层的输出相加;densenet是将前面所有的输出全部concatenat。
网络结构
这是densenet中的一个dense block
这是densenet网络的结构由多个denseblock组成
代码实现
class bottleneck_layer(layers.layer):
def __init__(self, x, filters, filters= “自定义”, stride=1):
super(bottleneck_layer, self).__init__()
#有denseblock的结构图可以看出bn+relu+conv为一个单元
self.bn = layers.BatchNormalization()
self.relu = layers.Activation('relu')
self.conv1 = layers.Con2D(x, use_bias=False, filters = 4*filters, kernel_size=[1,1], strides=stride, padding='same')
self.dp = layers.Dropout(0.2)
self.conv2 = layers.Con2D(x, use_bias=False, filters = filters, kernel_size=[3,3], strides=stride, padding='same')
def call(self, input, training = None):
out = self.bn(input)
out = self.relu(out)
out = self.conv1(out)
out = self.dp(out)
out = self.bn(input)
out = self.relu(out)
out = self.conv2(out)
out = self.dp(out)
return out
class transition_layer(layers.layer):
def __init__(self, x, filters, stride=1):
super(transition_layer, self).__init__()
self.bn = layers.BatchNormalization()
self.relu = layers.Activation('relu')
in_channel = x.shape[-1]
self.conv1 = layers.Con2D(x, use_bias=False, filters = 0.5*in_channel, kernel_size=[1,1], strides=stride, padding='same')
self.dp = layers.Dropout(0.2)
self.avgpool=layers.GlobalAveragePooling2D()
def call(self, input, training = None):
out = self.bn(input)
out = self.relu(out)
out = self.conv(out)
out = self.dp(out)
out = self.avgpool(out)
return out
def dense_block(input, num_blocks):
layers_concat = list()
layers_concat.append(input)
x = bottleneck_layer(input)
layers_concat.append(x)
for i in range(nb_layers - 1):
x = Concatenation(layers_concat)
x = self.bottleneck_layer(x)
layers_concat.append(x)
x = Concatenation(layers_concat)
return x
class Dense_net(keras.Model):
def __init__(self, input, filters="自定义", stride=1):
super(Dense_net, self).__init__()
self.conv1 = layers.Con2D(x, use_bias=False, filters = 2*filters, kernel_size=[7,7], strides=2, padding='same')
self.block1 = dense_block(input, nb_layers=6)
self.trans1 = transition_layer(input)
self.block2 = dense_block(input, nb_layers=12)
self.trans2 = transition_layer(input)
self.block3 = dense_block(input, nb_layers=48)
self.trans3 = transition_layer(input)
self.block4 = dense_block(input, nb_layers=32)
self.bn = layers.BatchNormalization()
self.relu = layers.Activation('relu')
self.avgpool=layers.GlobalAveragePooling2D()
self.dense = layers.Dense(input, units = "自定义", name = linear)
def call(self, input, training = None):
out = self.conv1(input)
out = self.block1(out)
out = self.trans1(out)
out = self.block2(out)
out = self.trans2(out)
out = self.block3(out)
out = self.trans3(out)
out = self.block4(out)
out = self.bn(out)
out = self.relu(out)
out = self.avgpool(out)
out = flatten(out)
out = self.dense(out)
return out
```