卷积神经网络CNN-part9-DenseNet
卷积神经网络CNN-part8-ResNet-CSDN博客
摘要:本文介绍了DenseNet(稠密连接网络)的核心结构和实现方法。DenseNet通过"稠密连接"机制让每层都与前面所有层直接相连,其核心组件包括:1)稠密块(DenseBlock)实现特征重用和特征传播;2)过渡层(transition_block)使用1x1卷积和平均池化控制模型复杂度。文章详细给出了PyTorch实现代码,包括卷积块、稠密块和过渡层的构建方法,并展示了完整的DenseNet模型架构。最后通过Fashion-MNIST数据集验证了模型性能,使用0.05学习率训练10个epoch达到不错效果。相比于ResNet,DenseNet通过特征复用显著提升了参数效率。【AI】
0. 前言
稠密连接网络(Dense Convolutional Network,简称 DenseNet)是 2017 年由 Huang 等人提出的一种深层卷积神经网络,其核心创新是“稠密连接(Dense Connection)”:网络中的每个层都会与前面所有层直接连接,即第l层的输入是前l-1层的输出的拼接(而非简单相加)。【网络】
DenseNet在某种程度上师ResNet的逻辑扩展。
1.DenseNet
1.1稠密块(DenseBlock)
DenseNet使用了ResNet改良版的“批量规范层、激活层和卷积层”架构
import torch
from torch import nn
from d2l import torch as d2l#稠密块体
def conv_block(input_channels,num_channels):return nn.Sequential(nn.BatchNorm2d(input_channels),nn.ReLU(),nn.Conv2d(input_channels, num_channels, kernel_size=3, padding=1))
稠密块是由多个卷积组成,每个卷积块使用相同数量的输出通道。然而,在前向传播中,将每个卷积块的输入和输出在通道维度上连接。
class DenseBlock(nn.Module):def __init__(self,num_convs, input_channels, num_channels):super(DenseBlock, self).__init__()layer=[]for i in range(num_convs):layer.append(conv_block(num_channels*i+input_channels,num_channels))self.net=nn.Sequential(*layer)def forward(self, x):for blk in self.net:y=blk(x)x=torch.cat((x,y),dim=1)return x
我们通过定义查看下卷积块的输出通道数。卷积块中输出对输入的增长程度,也被成为增长率(growth rate)。
blk=DenseBlock(2,3,10)
x=torch.randn(4,3,8,8)
y=blk(x)
y.shape
结果:torch.Size([4, 23, 8, 8])
1.2 过渡层
由于每个稠密块都会带来通道数的增加,因此使用过多会复杂化模型,用过渡层控制模型复杂度。过渡层通过1x1卷积层来减小通道数,并使用步幅为2的平均汇聚层减半高度和宽度,进一步降低复杂度。
def transition_block(input_channels,num_channels):return nn.Sequential(nn.BatchNorm2d(input_channels),nn.ReLU(),nn.Conv2d(input_channels, num_channels, kernel_size=1),nn.AvgPool2d(kernel_size=2, stride=2))
我们查看过渡层的作用。
blk=transition_block(23,10)
blk(y).shape
结果:torch.Size([4, 10, 4, 4])
1.3 DenseNet模型
我们来构建DenseNet模型。DenseNet使用同ResNet一样的单卷积层和最大汇聚层。
b1=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))
DenseNet使用了4个稠密块,每个模块之间,DenseNet使用过渡层来减半高度和宽度,并减半通道。
#num_channels为当前的通道数
num_channels,growth_rate=64,32
num_conv_in_dense_blocks=[4,4,4,4]
blks=[]
for i,num_convs in enumerate(num_conv_in_dense_blocks):blks.append(DenseBlock(num_convs,num_channels,growth_rate))#上一个稠密块的输出通道数num_channels+=num_convs*growth_rate#在稠密块之间添加一个过渡层,使通道数减半if i!=len(num_conv_in_dense_blocks)-1:blks.append(transition_block(num_channels,num_channels//2))num_channels=num_channels//2
最后,连接全局汇聚层和全连接层来输出结果。
net = nn.Sequential(b1,*blks,nn.BatchNorm2d(num_channels),nn.ReLU(),nn.AdaptiveAvgPool2d((1,1)),nn.Flatten(),nn.Linear(num_channels,10))
2. 训练
#训练模型
lr,num_epochs,batch_size=0.05,10,256
train_iter,test_iter=d2l.load_data_fashion_mnist(batch_size, resize=96)
d2l.train_ch6(net,train_iter,test_iter,num_epochs,lr,d2l.try_gpu())