Pytorch CIFAR10图像分类 DenseNet篇
Posted Real&Love
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Pytorch CIFAR10图像分类 DenseNet篇相关的知识,希望对你有一定的参考价值。
Pytorch CIFAR10图像分类 DenseNet篇
文章目录
这里贴一下汇总篇: 汇总篇
4.定义网络(DenseNet)
之前的ResNet通过前层与后层的“短路连接”(Shortcuts),加强了前后层之间的信息流通,在一定程度上缓解了梯度消失现象,从而可以将神经网络搭建得很深。更进一步,DenseNet最大化了这种前后层信息交流,通过建立前面所有层与后面层的密集连接,实现了特征在通道维度上的复用,使其可以在参数与计算量更少的情况下实现比ResNet更优的性能。如果想详细了解并查看论文,可以看我的另一篇博客【论文泛读】 DenseNet:稠密连接的卷积网络
DenseNet 和 ResNet 不同在于 ResNet 是跨层求和,而 DenseNet 是跨层将特征在通道维度进行拼接,下面可以看看他们两者的图示
这个是最标准的卷积神经网络
这是ResNet,是跨层求和
这个就是DenseNet,是跨层将特征在通道维度进行拼接
DenseNet的网络架构如下图所示,了便于下采样的实现,我们将网络划分为多个稠密连接的dense block,网络由多个Dense Block与中间的卷积池化组成,核心就在Dense Block中。Dense Block中的黑点代表一个卷积层,其中的多条黑线代表数据的流动,每一层的输入由前面的所有卷积层的输出组成。注意这里使用了通道拼接(Concatnate)操作,而非ResNet的逐元素相加操作。
我们将每个block之间的层称为过渡层
完成卷积和池化的操作。在我们的实验中,过渡层由BN层、1x1卷积层和2x2平均池化层组成。
具体的Block实现细节如下图所示,每一个Block由若干个Bottleneck的卷积层组成,对应上面图中的黑点。Bottleneck由BN、ReLU、1×1卷积、BN、ReLU、3×3卷积的顺序构成,也被称为DenseNet-B结构。其中1x1 Conv得到 4k 个特征图它起到的作用是降低特征数量,从而提升计算效率。
关于Block,有以下4个细节需要注意:
- 每一个Bottleneck输出的特征通道数是相同的,例如这里的32。同时可以看到,经过Concatnate操作后的通道数是按32的增长量增加的,因此这个32也被称为GrowthRate。
- 这里1×1卷积的作用是固定输出通道数,达到降维的作用。当几十个Bottleneck相连接时,Concatnate后的通道数会增加到上千,如果不增加1×1的卷积来降维,后续3×3卷积所需的参数量会急剧增加。1×1卷积的通道数通常是GrowthRate的4倍。
- 上图中的特征传递方式是直接将前面所有层的特征Concatnate后传到下一层,这种方式与具体代码实现的方式是一致的。
- Block采用了激活函数在前、卷积层在后的顺序,这与一般的网络上是不同的。
DenseNet 的网络结构
在ImageNet数据集上的网络如下图所示
由于我是对CIFAR进行实验,而论文中给出的是ImageNet的网络模型,所以由于数据集的不同,模型稍微有些不同
首先我们还是得判断是否可以利用GPU,因为GPU的速度可能会比我们用CPU的速度快20-50倍左右,特别是对卷积神经网络来说,更是提升特别明显。
device = 'cuda' if torch.cuda.is_available() else 'cpu'
Bottleneck
class Bottleneck(nn.Module):
"""
Dense Block
这里的growth_rate=out_channels, 就是每个Block自己输出的通道数。
先通过1x1卷积层,将通道数缩小为4 * growth_rate,然后再通过3x3卷积层降低到growth_rate。
"""
# 通常1×1卷积的通道数为GrowthRate的4倍
expansion = 4
def __init__(self, in_channels, growth_rate):
super(Bottleneck, self).__init__()
zip_channels = self.expansion * growth_rate
self.features = nn.Sequential(
nn.BatchNorm2d(in_channels),
nn.ReLU(True),
nn.Conv2d(in_channels, zip_channels, kernel_size=1, bias=False),
nn.BatchNorm2d(zip_channels),
nn.ReLU(True),
nn.Conv2d(zip_channels, growth_rate, kernel_size=3, padding=1, bias=False)
)
def forward(self, x):
out = self.features(x)
out = torch.cat([out, x], 1)
return out
我们验证一下输出的 channel 是否正确
test_net = Bottleneck(3, 5)
test_x = torch.zeros(1, 3, 32, 32)
print('input shape: {} x {} x {}'.format(test_x.shape[1], test_x.shape[2], test_x.shape[3]))
test_y = test_net(test_x)
print('output shape: {} x {} x {}'.format(test_y.shape[1], test_y.shape[2], test_y.shape[3]))
input shape: 3 x 32 x 32 output shape: 8 x 32 x 32
Transition
class Transition(nn.Module):
"""
改变维数的Transition层 具体包括BN、ReLU、1×1卷积(Conv)、2×2平均池化操作
先通过1x1的卷积层减少channels,再通过2x2的平均池化层缩小feature-map
"""
# 1×1卷积的作用是降维,起到压缩模型的作用,而平均池化则是降低特征图的尺寸。
def __init__(self, in_channels, out_channels):
super(Transition, self).__init__()
self.features = nn.Sequential(
nn.BatchNorm2d(in_channels),
nn.ReLU(True),
nn.Conv2d(in_channels, out_channels, kernel_size=1, bias=False),
nn.AvgPool2d(2)
)
def forward(self, x):
out = self.features(x)
return out
验证一下过渡层是否正确
test_net = Transition(3, 12)
test_x = torch.zeros(1, 3, 96, 96)
print('input shape: {} x {} x {}'.format(test_x.shape[1], test_x.shape[2], test_x.shape[3]))
test_y = test_net(test_x)
print('output shape: {} x {} x {}'.format(test_y.shape[1], test_y.shape[2], test_y.shape[3]))
input shape: 3 x 96 x 96 output shape: 12 x 48 x 48
DenseNet-BC
# DesneNet-BC
# B 代表 bottleneck layer(BN-RELU-CONV(1x1)-BN-RELU-CONV(3x3))
# C 代表压缩系数(0<=theta<=1)
import math
class DenseNet(nn.Module):
"""
Dense Net
paper中growth_rate取12,维度压缩的参数θ,即reduction取0.5
且初始化方法为kaiming_normal()
num_blocks为每段网络中的DenseBlock数量
DenseNet和ResNet一样也是六段式网络(一段卷积+四段Dense+平均池化层),最后FC层。
第一段将维数从3变到2 * growth_rate
(3, 32, 32) -> [Conv2d] -> (24, 32, 32) -> [layer1] -> (48, 16, 16) -> [layer2]
->(96, 8, 8) -> [layer3] -> (192, 4, 4) -> [layer4] -> (384, 4, 4) -> [AvgPool]
->(384, 1, 1) -> [Linear] -> (10)
"""
def __init__(self, num_blocks, growth_rate=12, reduction=0.5, num_classes=10):
super(DenseNet, self).__init__()
self.growth_rate = growth_rate
self.reduction = reduction
num_channels = 2 * growth_rate
self.features = nn.Conv2d(3, num_channels, kernel_size=3, padding=1, bias=False)
self.layer1, num_channels = self._make_dense_layer(num_channels, num_blocks[0])
self.layer2, num_channels = self._make_dense_layer(num_channels, num_blocks[1])
self.layer3, num_channels = self._make_dense_layer(num_channels, num_blocks[2])
self.layer4, num_channels = self._make_dense_layer(num_channels, num_blocks[3], transition=False)
self.avg_pool = nn.Sequential(
nn.BatchNorm2d(num_channels),
nn.ReLU(True),
nn.AvgPool2d(4),
)
self.classifier = nn.Linear(num_channels, num_classes)
self._initialize_weight()
def _make_dense_layer(self, in_channels, nblock, transition=True):
layers = []
for i in range(nblock):
layers += [Bottleneck(in_channels, self.growth_rate)]
in_channels += self.growth_rate
out_channels = in_channels
if transition:
out_channels = int(math.floor(in_channels * self.reduction))
layers += [Transition(in_channels, out_channels)]
return nn.Sequential(*layers), out_channels
def _initialize_weight(self):
for m in self.modules():
if isinstance(m, nn.Conv2d):
nn.init.kaiming_normal_(m.weight.data)
if m.bias is not None:
m.bias.data.zero_()
def forward(self, x):
out = self.features(x)
out = self.layer1(out)
out = self.layer2(out)
out = self.layer3(out)
out = self.layer4(out)
out = self.avg_pool(out)
out = out.view(out.size(0), -1)
out = self.classifier(out)
return out
def DenseNet121():
return DenseNet([6,12,24,16], growth_rate=32)
def DenseNet169():
return DenseNet([6,12,32,32], growth_rate=32)
def DenseNet201():
return DenseNet([6,12,48,32], growth_rate=32)
def DenseNet161():
return DenseNet([6,12,36,24], growth_rate=48)
def densenet_cifar():
return DenseNet([6,12,24,16], growth_rate=12)
net = DenseNet121().to(device)
summary(net,(3,32,32))
---------------------------------------------------------------- Layer (type) Output Shape Param # ================================================================ Conv2d-1 [-1, 64, 32, 32] 1,728 BatchNorm2d-2 [-1, 64, 32, 32] 128 ReLU-3 [-1, 64, 32, 32] 0 Conv2d-4 [-1, 128, 32, 32] 8,192 BatchNorm2d-5 [-1, 128, 32, 32] 256 ReLU-6 [-1, 128, 32, 32] 0 Conv2d-7 [-1, 32, 32, 32] 36,864 Bottleneck-8 [-1, 96, 32, 32] 0 BatchNorm2d-9 [-1, 96, 32, 32] 192 ReLU-10 [-1, 96, 32, 32] 0 Conv2d-11 [-1, 128, 32, 32] 12,288 BatchNorm2d-12 [-1, 128, 32, 32] 256 ReLU-13 [-1, 128, 32, 32] 0 Conv2d-14 [-1, 32, 32, 32] 36,864 Bottleneck-15 [-1, 128, 32, 32] 0 BatchNorm2d-16 [-1, 128, 32, 32] 256 ReLU-17 [-1, 128, 32, 32] 0 Conv2d-18 [-1, 128, 32, 32] 16,384 BatchNorm2d-19 [-1, 128, 32, 32] 256 ReLU-20 [-1, 128, 32, 32] 0 Conv2d-21 [-1, 32, 32, 32] 36,864 Bottleneck-22 [-1, 160, 32, 32] 0 BatchNorm2d-23 [-1, 160, 32, 32] 320 ReLU-24 [-1, 160, 32, 32] 0 Conv2d-25 [-1, 128, 32, 32] 20,480 BatchNorm2d-26 [-1, 128, 32, 32] 256 ReLU-27 [-1, 128, 32, 32] 0 Conv2d-28 [-1, 32, 32, 32] 36,864 Bottleneck-29 [-1, 192, 32, 32] 0 BatchNorm2d-30 [-1, 192, 32, 32] 384 ReLU-31 [-1, 192, 32, 32] 0 Conv2d-32 [-1, 128, 32, 32] 24,576 BatchNorm2d-33 [-1, 128, 32, 32] 256 ReLU-34 [-1, 128, 32, 32] 0 Conv2d-35 [-1, 32, 32, 32] 36,864 Bottleneck-36 [-1, 224, 32, 32] 0 BatchNorm2d-37 [-1, 224, 32, 32] 448 ReLU-38 [-1, 224, 32, 32] 0 Conv2d-39 [-1, 128Pytorch CIFAR10图像分类 ResNeXt篇
Pytorch CIFAR10图像分类 EfficientNet v1篇