推荐直接网站在线阅读:https://aicoting.cn
在 ResNet 提出残差连接后,深层网络的训练问题得到了极大缓解。然而,ResNet 的连接方式仍然比较稀疏:每一层的输出只与后一层相加,信息传递路径有限。
2017 年,Gao Huang 等人提出了 DenseNet(Densely Connected Convolutional Networks),通过一种更加彻底的特征复用方式,使得网络在参数更少的情况下,依然能获得更好的性能。
DenseNet 的核心思想是网络中任意一层都直接与之前所有层相连,这种密集连接方式显著提高了信息流动效率。
核心思想
在传统的卷积网络中,第l ll层的输入只来自第l − 1 l-1l−1层。
在 ResNet 中,第l ll层的输入是x l − 1 + F ( x l − 1 ) x_{l-1} + F(x_{l-1})xl−1+F(xl−1)。
而在 DenseNet 中:x l = H l ( [ x 0 , x 1 , … , x l − 1 ] ) x_l = H_l([x_0, x_1, \ldots, x_{l-1}])xl=Hl([x0,x1,…,xl−1])
这里,[ x 0 , x 1 , … , x l − 1 ] [x_0, x_1, \ldots, x_{l-1}][x0,x1,…,xl−1]表示将所有前面层的输出在通道维度上进行拼接(concatenate)。这样,第l ll层可以直接利用所有之前层的特征。
DenseNet就是让网络中的每一层都记得所有之前的特征,而不是只依赖最后一层的结果。
网络结构
DenseNet 主要由以下几个部分组成:
Dense Block(密集块):核心组件,每一层都接收前面所有层的特征,并将自己的输出传递给后续层。
Transition Layer(过渡层):用于控制模型复杂度,包括1 × 1 1×11×1卷积(减少通道数)和2 × 2 2×22×2平均池化(缩小特征图尺寸)。
Growth Rate(增长率):每一层新增加的特征通道数,用k kk表示。假设输入有m mm个通道,经过l ll层后,输出通道数为m + l × k m+l×km+l×k。
以 DenseNet-121 为例,它包含:
- 一个初始卷积层
- 四个 Dense Block,每个 Block 之间有 Transition Layer
- 每个 Dense Block 内包含多个卷积层,输出通道数逐渐增加
- 最后接全局平均池化和全连接层
DenseNet 相比 ResNet 和 VGG,有以下显著优势:
- 高效的特征复用:避免了重复学习冗余特征,提高了参数利用率。
- 缓解梯度消失:密集连接使得梯度可以直接流向前层,训练更稳定。
- 参数更少:由于特征被复用,DenseNet 的参数量比 ResNet 更少,但性能更好。
- 更强的正则化效果:信息流动更顺畅,使得模型不易过拟合。
代码示例
我们实现一个简化版 DenseNet(类似 DenseNet-121 的小型结构),并在 CIFAR-10 数据集上测试。
importtorchimporttorch.nnasnnimporttorch.optimasoptimfromtorchvisionimportdatasets,transformsfromtorch.utils.dataimportDataLoader# 定义 DenseLayerclassDenseLayer(nn.Module):def__init__(self,in_channels,growth_rate):super(DenseLayer,self).__init__()self.bn=nn.BatchNorm2d(in_channels)self.relu=nn.ReLU(inplace=True)self.conv=nn.Conv2d(in_channels,growth_rate,kernel_size=3,padding=1,bias=False)defforward(self,x):out=self.conv(self.relu(self.bn(x)))out=torch.cat([x,out],1)# 拼接输入和输出returnout# 定义 DenseBlockclassDenseBlock(nn.Module):def__init__(self,num_layers,in_channels,growth_rate):super(DenseBlock,self).__init__()layers=[]foriinrange(num_layers):layers.append(DenseLayer(in_channels+i*growth_rate,growth_rate))self.block=nn.Sequential(*layers)defforward(self,x):returnself.block(x)# 定义 Transition LayerclassTransition(nn.Module):def__init__(self,in_channels,out_channels):super(Transition,self).__init__()self.bn=nn.BatchNorm2d(in_channels)self.relu=nn.ReLU(inplace=True)self.conv=nn.Conv2d(in_channels,out_channels,kernel_size=1,bias=False)self.pool=nn.AvgPool2d(2,stride=2)defforward(self,x):out=self.conv(self.relu(self.bn(x)))out=self.pool(out)returnout# 定义 DenseNetclassDenseNet(nn.Module):def__init__(self,growth_rate=12,block_layers=[6,12,24,16],num_classes=10):super(DenseNet,self).__init__()self.growth_rate=growth_rate num_channels=2*growth_rate# 初始通道数self.conv1=nn.Conv2d(3,num_channels,kernel_size=3,padding=1,bias=False)self.features=nn.Sequential()fori,num_layersinenumerate(block_layers):block=DenseBlock(num_layers,num_channels,growth_rate)self.features.add_module(f"denseblock{i+1}",block)num_channels=num_channels+num_layers*growth_rateifi!=len(block_layers)-1:# 在 Block 之间加 Transitionout_channels=num_channels//2trans=Transition(num_channels,out_channels)self.features.add_module(f"transition{i+1}",trans)num_channels=out_channels self.bn=nn.BatchNorm2d(num_channels)self.relu=nn.ReLU(inplace=True)self.avgpool=nn.AdaptiveAvgPool2d((1,1))self.fc=nn.Linear(num_channels,num_classes)defforward(self,x):out=self.conv1(x)out=self.features(out)out=self.relu(self.bn(out))out=self.avgpool(out)out=torch.flatten(out,1)out=self.fc(out)returnout# 数据预处理transform=transforms.Compose([transforms.Resize(224),transforms.ToTensor(),transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))])train_dataset=datasets.CIFAR10(root='./data',train=True,transform=transform,download=True)train_loader=DataLoader(train_dataset,batch_size=64,shuffle=True)# 初始化模型、损失函数和优化器device=torch.device("cuda"iftorch.cuda.is_available()else"cpu")model=DenseNet(num_classes=10).to(device)criterion=nn.CrossEntropyLoss()optimizer=optim.SGD(model.parameters(),lr=0.01,momentum=0.9)# 简单训练循环forepochinrange(1):# 演示一个 epochforbatch_idx,(data,target)inenumerate(train_loader):data,target=data.to(device),target.to(device)outputs=model(data)loss=criterion(outputs,target)optimizer.zero_grad()loss.backward()optimizer.step()ifbatch_idx%100==0:print(f"Epoch [{epoch+1}], Step [{batch_idx}], Loss:{loss.item():.4f}")DenseNet 通过 密集连接 提供了更高效的信息流动方式,使得每一层都能直接利用所有之前层的特征。这种设计不仅减少了参数量,还提升了训练稳定性和模型性能。
DenseNet 在图像分类、目标检测、医学影像分析等任务中表现优异,成为 ResNet 之后最具影响力的 CNN 架构之一。
最新的文章都在公众号aicoting更新,别忘记关注哦!!!
📚推荐阅读
一文搞懂深度学习中的池化!
面试官:给我讲一下卷积吧!
一文搞懂卷积神经网络!
面试官:正则化都有哪些经典的方法?
面试官:你在训模型的时候经常使用的学习率策略有哪些?
面试官:深度学习中经典的优化算法都有哪些?
一文搞懂深度学习中的通用逼近定理!
一文搞懂深度学习中的表征学习理论!
一文搞懂深度学习中的信息论!
一文搞懂深度学习的反向传播与优化理论!
最新的文章都在公众号aicoting更新,别忘记关注哦!!!
作者:aicoting
分享是一种信仰,连接让成长更有温度。
我们下次不见不散!