DenseNet对应的论文是Densely Connected Convolutional Networks。
DenseNet由多个dense block组成,每个dense block中的每一层都和其他所有层连接起来。对于每一层而言,它前面所有层的输出都是该层的输入,而它的输出又是后面每一层的输入。具体形式如下图(从原论文上截取下来的):
![](https://img.haomeiwen.com/i13223816/78a07befd15da585.png)
这样设计的优势在于:
- 减缓了梯度消失问题,深层梯度可以直接传回浅层;
- 加强了特征的传播,浅层特征可以直接传到深层中使用;
- 鼓励特征的reuse,每一层输出的特征都被其后面的层所使用;
- 减少了模型的参数,减少了贡献不大的中间卷积层。
Dense Net中也可以看做包含大量的short cut,但是它这种short cut和ResNet中的short cut是不同的,DenseNet中不同层的特征连接方式是concat,而ResNet中不同层的特征连接方式是sum。也即:
一个dense block中包含多个dense layer,而dense layer中包含3层:
,
和
(注意这里BN是在激活函数ReLU之前,我的理解是为了调整数据的分布,因为每一层的输入都是之前所有层的输出,不同层的输出一般其分布也不同,为了使得该层能更快更好地学习,那么先将输入信息进行规范化是很有必要的)。完整的dense layer流程是:
前3层作为一个部分是为了利用
减少特征图数量,增加计算量。不同dense block之间的连接层称为transition layer,每个transition layer包含3层:
,
和
。Transition layer的作用我认为是为了给feature map降低size作用,这里存疑。
对于每个dense layer而言,它的输入是前面所有层的输出,它的输出又是后面所有层的输入,如果不加限制,那么Dense Net中每层特征图的数量就会呈指数上升。针对这个问题,论文中提出了一个重要的参数Ground rate,这个参数指明了每个dense layer输出的channel数。所以一个包含L层的dense block中第层输入channel数是
,其中
表示dense block的输入channel数。也就是说一个dense block中每一个dense layer的输入channel数在不断增加,而输出channel数是不变的,最后dense block的输出是所有dense layer的输出的拼接,channel数为
。
为了更好地了解DenseNet的原理,我看了下PyTorch中DenseNet的实现,包括:
# dense layer的实现
def _bn_function_factory(norm, relu, conv):
def bn_function(*inputs):
# dense layer中输入的拼接和前3层是确定的,所以用一个工厂函数进行封装
# 后3层需要定制,所以就不写在工厂函数中
concated_features = torch.cat(inputs, 1) # 输入信息的拼接,inputs是一个list
bottleneck_output = conv(relu(norm(concated_features)))
return bottleneck_output
return bn_function
# dense block类中forward函数的实现
def forward(self, init_features):
features = [init_features] # 维护一个全局变量(features,以list形式)来依次保存各层的输出
for name, layer in self.named_children():
new_features = layer(*features)
features.append(new_features) # 依次向features中添加各层的输出
return torch.cat(features, 1) # dense block的输出是该block中每个dense layer的输出的拼接结果
网友评论