pytorch实现卷积神经网络_pytorch项目

2022-11-10 13:50:44 浏览数 (2)

大家好,又见面了,我是你们的朋友全栈君。

论文链接:https://arxiv.org/pdf/1608.06993.pdf 顾名思义,DenseNet采用了高密度的跳连结构,对于每一层,使用先前所有层的输出作为输入,该层的输出将作为之后所有层的输入的一部分。因此对于一个dense模块,假设有 L L L层,那么存在 L ( L 1 ) 2 frac{L(L 1)}{2} 2L(L 1)​直接的连接。

dense模块之后会连接一个transition层,由1×1卷积和平均池化构成。 作者认为有如下优点: (1)由于存在很多跳连,减轻了空梯度问题,加强了梯度和信息流动,更容易训练。 (2)加强了特征的重用。 (3)DenseNet层的filter数量比较少,使得层比较狭窄。每层只生成了很小的特征图,成为共同知识中的一员。这样网络的参数更少,且更有效率。

DenseNet结构分析

定义混合函数 H l ( ⋅ ) H_{l}(cdot) Hl​(⋅),由三个连接操作组成:批标准化(BN),ReLU和3×3卷积。 ResNet比较 :恒等函数和混合函数的加和为输出,这样的做法可能会阻止网络中的信息流动。 密度连接: 对于某层,前 l − 1 l-1 l−1层的输出 x 0 , ⋯   , x l − 1 bm{x}_{0},cdots,bm{x}_{l-1} x0​,⋯,xl−1​进行channel水平的连接,生成一个单一的特征图: x l = H l ( [ x 0 , ⋯   , x l − 1 ] ) bm{x}_{l}=H_{l}([bm{x}_{0},cdots,bm{x}_{l-1}]) xl​=Hl​([x0​,⋯,xl−1​]) 池化层:当特征图的尺寸改变时,就无法进行连接了,然后在设计网络时必须要求特征图的尺寸在往后时减少,所以将dense模块分为几个模块。在模块之间设计transition层进行如下:BN,1×1卷积和2×2平均池化。 增长率 k k k: 在一个dense模块中,初始输入的特征图模块是 k 0 k_{0} k0​,若则每层的输出通道 k k k, H l H_{l} Hl​的输入为 k 0 k ( l − 1 ) k_{0} k(l-1) k0​ k(l−1)。 k k k调整每层多少信息加入到全局信息。 瓶颈层: 虽然每层仅仅产生 k k k通道的输出特征图,但是具有很多的输入。使用预激活的瓶颈层,采用如下的处理顺序:BN-ReLU-Conv(1×1)-BN-ReLU-Conv(3×3),称为DenseNet-B。在实验中,每1×1卷积降维变成 4 k 4k 4k通道特征图。 压缩: 在transition层减少特征图数量,设置压缩因子 0 < θ ≤ 1 0<theta leq 1 0<θ≤1,通过transition层后,产生了 ⌊ θ m ⌋ left lfloor theta mright rfloor ⌊θm⌋个通道的输出特征图。当 θ < 1 theta<1 θ<1,称为DenseNet-C。在实验中,作者设为 θ = 0.5 theta=0.5 θ=0.5。在DenseNet-C上使用瓶颈层被称为DenseNet-BC。

特征重用分析

DenseNet中由于使用该密度的跳连结构使得能够复用前面的feature-map,使用heap-map来分析前面层不同feature-map对当前层的重要程度。

上图展示了卷积层filter权重绝对值的平均值。坐标(s,l)表示在一个dense块中,由第s层传到l层的feature-map,l层权重的平均L1-norm(即权重绝对值的平均值)。我们知道,权重绝对值越大,表示对应的特征越重要,因此也反映了l层对s层的依赖性。红色部分表示强大的使用了s层的feature-map。 从图中作者得到如下4点结论: 1.在相同的块中,所有层的对于输入都具有权重。特征提取在早期层,直接使用在深度层。 2.transition层对于输入都具有权重。DenseNet从第一层到最后一层的信息流动仅通过很少的间接连接。 3.第二层和第三层的dense块一致的对transition层的输出赋予了很小的权重(第一层除外),表明了transition层的输出对第一层之后的层产生了冗余的特征。 4.最终的分类层更加关注最终的feature-maps,表明有一些高水平的特征产生在后面。

Pytorch实现DenseNet-BC

在论文中,作者公开了ImageNet的DenseNet结构。对于CIFAR,denseNet采用3个dense模块,每个dense模块采用相同数量的bottleneck。例如在DenseNet-BC(k=12) 100中,每个dense模块有16个bottleneck层,3 * (16 *2)=96层,剩下的4层分别为第一层卷积,中间的2层transition层,和最后的全连接层。

代码语言:javascript复制
import torch
import torch.nn as nn
from collections import OrderedDict
class _DenseLayer(nn.Sequential):
def __init__(self, in_channels, growth_rate, bn_size):
super(_DenseLayer, self).__init__()
self.add_module('norm1', nn.BatchNorm2d(in_channels))
self.add_module('relu1', nn.ReLU(inplace=True))
self.add_module('conv1', nn.Conv2d(in_channels, bn_size * growth_rate,
kernel_size=1,
stride=1, bias=False))
self.add_module('norm2', nn.BatchNorm2d(bn_size*growth_rate))
self.add_module('relu2', nn.ReLU(inplace=True))
self.add_module('conv2', nn.Conv2d(bn_size*growth_rate, growth_rate,
kernel_size=3,
stride=1, padding=1, bias=False))
# 重载forward函数
def forward(self, x):
new_features = super(_DenseLayer, self).forward(x)
return torch.cat([x, new_features], 1)
class _DenseBlock(nn.Sequential):
def __init__(self, num_layers, in_channels, bn_size, growth_rate):
super(_DenseBlock, self).__init__()
for i in range(num_layers):
self.add_module('denselayer%d' % (i 1),
_DenseLayer(in_channels growth_rate*i,
growth_rate, bn_size))
class _Transition(nn.Sequential):
def __init__(self, in_channels, out_channels):
super(_Transition, self).__init__()
self.add_module('norm', nn.BatchNorm2d(in_channels))
self.add_module('relu', nn.ReLU(inplace=True))
self.add_module('conv', nn.Conv2d(in_channels, out_channels,
kernel_size=1,
stride=1, bias=False))
self.add_module('pool', nn.AvgPool2d(kernel_size=2, stride=2))
class DenseNet_BC(nn.Module):
def __init__(self, growth_rate=12, block_config=(6,12,24,16),
bn_size=4, theta=0.5, num_classes=10):
super(DenseNet_BC, self).__init__()
# 初始的卷积为filter:2倍的growth_rate
num_init_feature = 2 * growth_rate
# 表示cifar-10
if num_classes == 10:
self.features = nn.Sequential(OrderedDict([
('conv0', nn.Conv2d(3, num_init_feature,
kernel_size=3, stride=1,
padding=1, bias=False)),
]))
else:
self.features = nn.Sequential(OrderedDict([
('conv0', nn.Conv2d(3, num_init_feature,
kernel_size=7, stride=2,
padding=3, bias=False)),
('norm0', nn.BatchNorm2d(num_init_feature)),
('relu0', nn.ReLU(inplace=True)),
('pool0', nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
]))
num_feature = num_init_feature
for i, num_layers in enumerate(block_config):
self.features.add_module('denseblock%d' % (i 1),
_DenseBlock(num_layers, num_feature,
bn_size, growth_rate))
num_feature = num_feature   growth_rate * num_layers
if i != len(block_config)-1:
self.features.add_module('transition%d' % (i   1),
_Transition(num_feature,
int(num_feature * theta)))
num_feature = int(num_feature * theta)
self.features.add_module('norm5', nn.BatchNorm2d(num_feature))
self.features.add_module('relu5', nn.ReLU(inplace=True))
self.features.add_module('avg_pool', nn.AdaptiveAvgPool2d((1, 1)))
self.classifier = nn.Linear(num_feature, num_classes)
for m in self.modules():
if isinstance(m, nn.Conv2d):
nn.init.kaiming_normal_(m.weight)
elif isinstance(m, nn.BatchNorm2d):
nn.init.constant_(m.weight, 1)
nn.init.constant_(m.bias, 0)
elif isinstance(m, nn.Linear):
nn.init.constant_(m.bias, 0)
def forward(self, x):
features = self.features(x)
out = features.view(features.size(0), -1)
out = self.classifier(out)
return out
# DenseNet_BC for ImageNet
def DenseNet121():
return DenseNet_BC(growth_rate=32, block_config=(6, 12, 24, 16), num_classes=1000)
def DenseNet169():
return DenseNet_BC(growth_rate=32, block_config=(6, 12, 32, 32), num_classes=1000)
def DenseNet201():
return DenseNet_BC(growth_rate=32, block_config=(6, 12, 48, 32), num_classes=1000)
def DenseNet161():
return DenseNet_BC(growth_rate=48, block_config=(6, 12, 36, 24), num_classes=1000,)
# DenseNet_BC for cifar
def densenet_BC_100():
return DenseNet_BC(growth_rate=12, block_config=(16, 16, 16))

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

0 人点赞