DenseNet学习笔记总结+pytorch实现
卷积神经网路的里程碑事件便是ResNet模型的出现,其核心是建立前后层之间的“捷径连接”,这有助于训练更深的CNN模型,从而得到更高的准确率。
而这篇文章要介绍的是DenseNet(Densely Connected Convolutional Networks ),顾名思义,它是建立前面所有层与后面层的“稠密连接”,也就是说,将先前所有层的输出作为输入,而该层的输出作为之后所有层的输入。这也是DenseNet与ResNet之间最大的区别。
假如有L层,在ResNet中有L个连接,而在DenseNet中,会有L(L+1)/2个连接。具体情况如下图所示:

在这篇论文中,我特别喜欢的一点是没有用很多复杂的公式,大大减轻了我阅读时的心里负担。文中只看到了两个理论公式,是用来解释DenseNet和ResNet之间的关系,分别是:
首先说下各个字母的含义:
l:层
xl:l层的输出
Hl:非线性变换
①ResNet:第l层的输出等于l-1层的输出加上l-1层输出的非线性变换。

②DenseNet:[ ]的意思是对所有层做通道的合并。Hl包括BN、ReLU和3*3卷积。

所以它们的本质区别就是一个是相加而另一个是相连接。
CNN网络一般要经过池化层或者步长大于1的卷积层来降低特征图的尺寸,但是DenseNet的稠密连接方式需要特征图大小保持一致。为了解决这个问题,DenseNet网络中使用Dense block+Transition的结构,其中Dense block是包含很多层的模块,每个层的特征图大小相同,层与层之间采用稠密连接方式。而Transition模块是连接两个相邻的Dense block,并且通过池化层使特征图大小降低。下边是DenseNet结构,它共包含3个Dense Block,各个Dense Block之间通过Transition连接在一起。上图:


密度连接:
池化层:当特征图的尺寸改变时,就无法进行连接了,然而卷积网络的一个重要组成部分是下采样层,它改变了特征图的大小。所以将dense模块分为几个模块。并对模块之间设计的过渡层进行如下:BN,1x1卷积和2x2平均池化。
增长率k: 在一个dense模块中,初始输入层的通道是k0 ,若则每层的输出通道k,Hl的输入特征图为k0+k(l−1)。k调整每层多少信息加入到全局信息。
瓶颈层: 虽然每层仅仅产生k通道的输出特征图,但是具有很多的输入。使用预激活的瓶颈层,采用如下的处理顺序:BN-ReLU-Conv(1x1)-BN-ReLU-Conv(3x3),称为DenseNet-B。在实验中,每1x1卷积降维变成4k个特征图。
压缩: 在transition层减少特征图数量,设置压缩因子。
当0<θ≤1,通过transition层后,产生了⌊θm⌋个通道的输出特征图。
当θ<1,称为DenseNet-C。在实验中,作者设为θ=0.5。在DenseNet-C上使用瓶颈层被称为DenseNet-BC。
通过在下边表格2中三个数据集C10、C100、SVHN上和其他算法加进行比较,显而易见,DenseNet-BC的网络参数和相同深度的DenseNet相比确实减少了很多,参数减少除了可以节省内存,还能减少过拟合。DenseNet-BC的结果并没有DenseNet(k=24)的效果好, 这可以解释为SVHN是一个相对容易的任务,而且深的模型可能会产生过拟合现象。

在图3中DenseNet-BC和ResNet在Imagenet数据集上的对比,左图是参数-错误率的对比,不管是相同数目的参数看错误率,还是相同错误率下的参数数目,DenseNet-BC都有着明显的优势。
右图是计算-错误率,和左图效果类似。

在图4中,左图是不同类型的DenseNet的参数-测试误差,可见是DenseNet-BC效果最好;中间是DenseNet-BC和ResNet的参数-测试误差,表明相同的测试误差下,DenseNet-BC的参数要少很多;右图是DenseNet-BC-100与ResNet-1001在相同效果下的参数数目比较,明显DenseNet-BC可以用很少的参数达到ResNet-1001的结果。

最后用pytorch来实现DenseNet:
import math
import torch
import torch.nn as nn
import torch.nn.functional as F
class Bottleneck(nn.Module):
def __init__(self, in_planes, growth_rate):
super(Bottleneck, self).__init__()
self.bn1 = nn.BatchNorm2d(in_planes)
self.relu1 = nn.ReLU(inplace=True)
self.conv1 = nn.Conv2d(in_planes, 4*growth_rate, kernel_size=1)
self.bn2 = nn.BatchNorm2d(4*growth_rate)
self.relu2 = nn.ReLU(inplace=True)
self.conv2 = nn.Conv2d(4*growth_rate, growth_rate, kernel_size=3, padding=1)
def forward(self, x):
out = self.conv1(self.relu1(self.bn1(x)))
out = self.conv2(self.relu2(self.bn2(out)))
out = torch.cat([out,x], 1)
return out
class Transition(nn.Module):
def __init__(self, in_planes, out_planes):
super(Transition, self).__init__()
self.bn = nn.BatchNorm2d(in_planes)
self.relu = nn.ReLU(inplace=True)
self.conv = nn.Conv2d(in_planes, out_planes, kernel_size=1)
def forward(self, x):
out = self.conv(self.relu(self.bn(x)))
out = F.avg_pool2d(out, 2)
return out
class DenseNet(nn.Module):
def __init__(self, nblocks, growth_rate, reduction, num_classes):
super(DenseNet, self).__init__()
self.growth_rate = growth_rate
num_planes = 2 * growth_rate
self.basic_conv = nn.Sequential(
nn.Conv2d(3, num_planes, kernel_size=7, stride=2, padding=3),
nn.BatchNorm2d(num_planes),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
)
self.dense1 = self._make_dense_layers(num_planes, nblocks[0])
num_planes += nblocks[0] * growth_rate
out_planes = int(math.floor(num_planes * reduction))
self.trans1 = Transition(num_planes, out_planes)
num_planes = out_planes
self.dense2 = self._make_dense_layers(num_planes, nblocks[1])
num_planes += nblocks[1] * growth_rate
out_planes = int(math.floor(num_planes * reduction))
self.trans2 = Transition(num_planes, out_planes)
num_planes = out_planes
self.dense3 = self._make_dense_layers(num_planes, nblocks[2])
num_planes += nblocks[2] * growth_rate
out_planes = int(math.floor(num_planes * reduction))
self.trans3 = Transition(num_planes, out_planes)
num_planes = out_planes
self.dense4 = self._make_dense_layers(num_planes, nblocks[3])
num_planes += nblocks[3] * growth_rate
self.gap = nn.AdaptiveAvgPool2d(1)
self.linear = nn.Linear(num_planes, num_classes)
def _make_dense_layers(self, in_planes, nblock):
layers = []
for i in range(nblock):
layers.append(Bottleneck(in_planes, self.growth_rate))
in_planes += self.growth_rate
return nn.Sequential(*layers)
def forward(self, x):
out = self.basic_conv(x)
out = self.trans1(self.dense1(out))
out = self.trans2(self.dense2(out))
out = self.trans3(self.dense3(out))
out = self.dense4(out)
out = self.gap(out)
out = out.view(out.size(0), -1)
out = self.linear(out)
return out
def DenseNet121():
return DenseNet([6,12,24,16], growth_rate=32, reduction=0.5, num_classes=1000)
def DenseNet169():
return DenseNet([6,12,32,32], growth_rate=32, reduction=0.5, num_classes=1000)
def DenseNet201():
return DenseNet([6,12,48,32], growth_rate=32, reduction=0.5, num_classes=1000)
def DenseNet265():
return DenseNet([6,12,64,48], growth_rate=32, reduction=0.5, num_classes=1000)
net = DenseNet121()
x = torch.randn(1,3,224,224)
y = net(x)
print(y.size())
更多推荐


所有评论(0)