一文搞懂CNN经典架构-DenseNet!

推荐直接网站在线阅读:aicoting.cn

在 ResNet 提出残差连接后,深层网络的训练问题得到了极大缓解。然而,ResNet 的连接方式仍然比较稀疏:每一层的输出只与后一层相加,信息传递路径有限。

2017 年,Gao Huang 等人提出了 DenseNet(Densely Connected Convolutional Networks),通过一种更加彻底的特征复用方式,使得网络在参数更少的情况下,依然能获得更好的性能。

DenseNet 的核心思想是网络中任意一层都直接与之前所有层相连,这种密集连接方式显著提高了信息流动效率。

核心思想

在传统的卷积网络中,第 <math xmlns="http://www.w3.org/1998/Math/MathML"> l l </math>l 层的输入只来自第 <math xmlns="http://www.w3.org/1998/Math/MathML"> l − 1 l-1 </math>l−1 层。

在 ResNet 中,第 <math xmlns="http://www.w3.org/1998/Math/MathML"> l l </math>l层的输入是 <math xmlns="http://www.w3.org/1998/Math/MathML"> x l − 1 + F ( x l − 1 ) x_{l-1} + F(x_{l-1}) </math>xl−1+F(xl−1)。

而在 DenseNet 中: <math xmlns="http://www.w3.org/1998/Math/MathML"> x l = H l ( [ x 0 , x 1 , ... , x l − 1 ] ) x_l = H_l([x_0, x_1, \ldots, x_{l-1}]) </math>xl=Hl([x0,x1,...,xl−1])

这里, <math xmlns="http://www.w3.org/1998/Math/MathML"> [ x 0 , x 1 , ... , x l − 1 ] [x_0, x_1, \ldots, x_{l-1}] </math>[x0,x1,...,xl−1] 表示将所有前面层的输出在通道维度上进行拼接(concatenate)。这样,第 <math xmlns="http://www.w3.org/1998/Math/MathML"> l l </math>l 层可以直接利用所有之前层的特征。

DenseNet就是让网络中的每一层都记得所有之前的特征,而不是只依赖最后一层的结果。

网络结构

DenseNet 主要由以下几个部分组成:

  1. Dense Block(密集块):核心组件,每一层都接收前面所有层的特征,并将自己的输出传递给后续层。

  2. Transition Layer(过渡层):用于控制模型复杂度,包括 <math xmlns="http://www.w3.org/1998/Math/MathML"> 1 × 1 1×1 </math>1×1 卷积(减少通道数)和 <math xmlns="http://www.w3.org/1998/Math/MathML"> 2 × 2 2×2 </math>2×2 平均池化(缩小特征图尺寸)。

  3. Growth Rate(增长率):每一层新增加的特征通道数,用 <math xmlns="http://www.w3.org/1998/Math/MathML"> k k </math>k 表示。假设输入有 <math xmlns="http://www.w3.org/1998/Math/MathML"> m m </math>m 个通道,经过 <math xmlns="http://www.w3.org/1998/Math/MathML"> l l </math>l 层后,输出通道数为 <math xmlns="http://www.w3.org/1998/Math/MathML"> m + l × k m+l×k </math>m+l×k。

以 DenseNet-121 为例,它包含:

  • 一个初始卷积层
  • 四个 Dense Block,每个 Block 之间有 Transition Layer
  • 每个 Dense Block 内包含多个卷积层,输出通道数逐渐增加
  • 最后接全局平均池化和全连接层

DenseNet 相比 ResNet 和 VGG,有以下显著优势:

  • 高效的特征复用:避免了重复学习冗余特征,提高了参数利用率。
  • 缓解梯度消失:密集连接使得梯度可以直接流向前层,训练更稳定。
  • 参数更少:由于特征被复用,DenseNet 的参数量比 ResNet 更少,但性能更好。
  • 更强的正则化效果:信息流动更顺畅,使得模型不易过拟合。

代码示例

我们实现一个简化版 DenseNet(类似 DenseNet-121 的小型结构),并在 CIFAR-10 数据集上测试。

python 复制代码
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader

# 定义 DenseLayer
class DenseLayer(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)

    def forward(self, x):
        out = self.conv(self.relu(self.bn(x)))
        out = torch.cat([x, out], 1)  # 拼接输入和输出
        return out

# 定义 DenseBlock
class DenseBlock(nn.Module):
    def __init__(self, num_layers, in_channels, growth_rate):
        super(DenseBlock, self).__init__()
        layers = []
        for i in range(num_layers):
            layers.append(DenseLayer(in_channels + i * growth_rate, growth_rate))
        self.block = nn.Sequential(*layers)

    def forward(self, x):
        return self.block(x)

# 定义 Transition Layer
class Transition(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)

    def forward(self, x):
        out = self.conv(self.relu(self.bn(x)))
        out = self.pool(out)
        return out

# 定义 DenseNet
class DenseNet(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()
        for i, num_layers in enumerate(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_rate
            if i != len(block_layers) - 1:  # 在 Block 之间加 Transition
                out_channels = num_channels // 2
                trans = 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)

    def forward(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)
        return out

# 数据预处理
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" if torch.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)

# 简单训练循环
for epoch in range(1):  # 演示一个 epoch
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)

        outputs = model(data)
        loss = criterion(outputs, target)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        if batch_idx % 100 == 0:
            print(f"Epoch [{epoch+1}], Step [{batch_idx}], Loss: {loss.item():.4f}")

DenseNet 通过 密集连接 提供了更高效的信息流动方式,使得每一层都能直接利用所有之前层的特征。这种设计不仅减少了参数量,还提升了训练稳定性和模型性能。

DenseNet 在图像分类、目标检测、医学影像分析等任务中表现优异,成为 ResNet 之后最具影响力的 CNN 架构之一。

最新的文章都在公众号aicoting更新,别忘记关注哦!!!

📚推荐阅读

一文搞懂深度学习中的池化!

面试官:给我讲一下卷积吧!

一文搞懂卷积神经网络!

面试官:正则化都有哪些经典的方法?

面试官:你在训模型的时候经常使用的学习率策略有哪些?

面试官:深度学习中经典的优化算法都有哪些?

一文搞懂深度学习中的通用逼近定理!

一文搞懂深度学习中的表征学习理论!

一文搞懂深度学习中的信息论!

一文搞懂深度学习的反向传播与优化理论!

最新的文章都在公众号aicoting更新,别忘记关注哦!!!

作者:aicoting

分享是一种信仰,连接让成长更有温度。

我们下次不见不散!

相关推荐
花间相见2 小时前
【MySQL面试题】—— MySQL面试高频问题汇总:从原理到实战,覆盖90%考点
数据库·mysql·面试
道法自然|~2 小时前
BugCTF黄道十二宫
算法·密码学
野生技术架构师3 小时前
1000道互联网大厂Java岗面试原题解析(八股原理+场景题)
java·开发语言·面试
YuanDaima20483 小时前
[CrewAI] 第15课|构建一个多代理系统来实现自动化简历定制和面试准备
人工智能·python·面试·agent·crewai
WHS-_-20223 小时前
Python 算法题学习笔记一
python·学习·算法
沉鱼.443 小时前
第六届题目
算法
黑眼圈子3 小时前
总结一下用Java做算法的常用类和方法
java·开发语言·算法
apcipot_rain3 小时前
天梯赛练习集 时间规划 限时复盘 中档题详解(L1-6~L2-4)
算法
再卷也是菜4 小时前
第一章、线性代数(2)高斯消元法
线性代数·算法