0.优点
核心就两个模块:dense block ,transition。我看完这篇论文觉得和resnet的思想很想
1.由于他和renet一样都是会在训练的时候提前底层特征所以会避免梯度消失
(resnet、densenet、inception都采用一定的手段来避免梯度消失)
2. 有效的使用了feature map,他可以在数据集体量比较小的情况下有一个不错的效果
3. 使用了transition和desen block都有1*1卷积模块(desenblock内部使用bottleneck)。
4. 参数量比较小,不是一个特别深的网络但是计算量不小,所以用3的手段来压缩
1.dense block
1.1dense 结构
每一层的输入来自前面所有层的输出。x0是input,H1的输入是x0(input),H2的输入是x0和x1···连接方式是concat
conv_block就是一个DenseBlock
def conv_block(in_channels, out_channels):
blk = nn.Sequential(nn.BatchNorm2d(in_channels),
nn.ReLU(),
nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1))
return blk
这一部分是负责把浅层的特征和深层的cnoncat
class DenseBlock(nn.Module):
def __init__(self, num_convs, in_channels, out_channels):
super(DenseBlock, self).__init__()
net = []
for i in range(num_convs):
in_c = in_channels + i * out_channels
net.append(conv_block(in_c, out_channels))
self.net = nn.ModuleList(net)
self.out_channels = in_channels + num_convs * out_channels # 计算输出通道数
def forward(self, X):
for blk in self.net:
Y = blk(X)
X = torch.cat((X, Y), dim=1) # 在通道维上将输入和输出连结
return X
1.2 dense和resnet的区别
第一个公式是ResNet的。这里的l表示层,xl表示l层的输出,Hl表示一个非线性变换。所以对于ResNet而言,l层的输出是l-1层的输出加上对l-1层输出的非线性变换。
第二个公式是DenseNet的。[x0,x1,…,xl-1]表示将0到l-1层的输出feature map做concatenation。concatenation是做通道的合并,就像Inception那样。而前面resnet是做值的相加,通道数是不变的。Hl包括BN,ReLU和3*3的卷积。
下图左边是resnet右边是densenet
而传统的卷积运算可以描述为:
1.3 pre-activation
何凯明在v2的res block中测试过一种新的激活方式,就是先bn-激活再卷积
这样计算速度会慢但是理论上精度会高
1.4 transition 模块
pre-activate+1*1+avgpooling
def transition_block(in_channels, out_channels):
blk = nn.Sequential(
nn.BatchNorm2d(in_channels),
nn.ReLU(),
nn.Conv2d(in_channels, out_channels, kernel_size=1),
nn.AvgPool2d(kernel_size=2, stride=2))
return blk
2.网络结构
经过简单的处理之后就用四组desen block+transition来提取特征
net = nn.Sequential(
nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3),
nn.BatchNorm2d(64),
nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
num_channels, growth_rate = 64, 32 # num_channels为当前的通道数
num_convs_in_dense_blocks = [4, 4, 4, 4]
for i, num_convs in enumerate(num_convs_in_dense_blocks):
DB = DenseBlock(num_convs, num_channels, growth_rate)
net.add_module("DenseBlosk_%d" % i, DB)
# 上一个稠密块的输出通道数
num_channels = DB.out_channels
# 在稠密块之间加入通道数减半的过渡层
if i != len(num_convs_in_dense_blocks) - 1:
net.add_module("transition_block_%d" % i, transition_block(num_channels, num_channels // 2))
num_channels = num_channels // 2
net.add_module("BN", nn.BatchNorm2d(num_channels))
net.add_module("relu", nn.ReLU())
net.add_module("global_avg_pool", d2l.GlobalAvgPool2d()) # GlobalAvgPool2d的输出: (Batch, num_channels, 1, 1)
net.add_module("fc", nn.Sequential(d2l.FlattenLayer(), nn.Linear(num_channels, 10)))