【深度学习经典模型】CVPR2017的最佳论文DenseNet好在哪里??!Densely Connected Convolutional Networks!
【深度学习经典模型】CVPR2017的最佳论文DenseNet好在哪里??!Densely Connected Convolutional Networks!
文章目录
欢迎宝子们点赞、关注、收藏!欢迎宝子们批评指正!
祝所有的硕博生都能遇到好的导师!好的审稿人!好的同门!顺利毕业!
论文链接:https://arxiv.org/pdf/1608.06993
代码链接:https://github.com/liuzhuang13/DenseNet
大多数高校硕博生毕业要求需要参加学术会议,发表EI或者SCI检索的学术论文会议论文:
可访问艾思科蓝官网,浏览2024年即将召开的学术会议列表。会议入口:https://ais.cn/u/mmmiUz
1. DenseNet 的起源
DenseNet(Densely Connected Convolutional Networks)是由 Gao Huang 等人于 2017 年提出的,最早发表于论文《Densely Connected Convolutional Networks》【CVPR 2017】。
DenseNet 提出时主要针对深层神经网络中的梯度消失、信息流失和参数冗余问题,并通过改进网络的连接方式,显著减少参数量和提升网络的表示能力。
2. DenseNet 的原理
DenseNet 的核心思想是 密集连接(Dense Connection),即每一层的输出都直接连接到后续所有层。传统的卷积神经网络(CNN)层与层之间是顺序连接的,而在 DenseNet 中,第 l l l 层的输入不仅仅来自于第 l − 1 l-1 l−1 层,还来自于所有之前层的输出。数学上表示为:
x l = H l ( [ x 0 , x 1 , . . . x l − 1 ] ) x_l=H_l([x_0,x_1,...x_{l-1}]) xl=Hl([x0,x1,...xl−1])
其中, [ x 0 , x 1 , . . . x l − 1 ] [x_0,x_1,...x_{l-1}] [x0,x1,...xl−1]表示将之前各层的输出拼接起来作为输入, H l H_l Hl代表第 l l l层的非线性转换(例如卷积、ReLU 等)。
DenseNet 可以被看作是 ResNet(残差网络)的进一步改进。在 ResNet 中,通过引入残差连接,部分绕过了深层网络中的梯度消失问题,而 DenseNet 则通过密集连接,进一步加强了特征重用,从而提升了模型的表现。
3. DenseNet 的创新点
DenseNet 的主要创新点可以总结为以下几点:
- 特征重用:通过密集连接,各层之间的特征共享,避免了信息的重复学习,同时减小了过拟合的风险。
- 梯度流动性更好:由于每一层的梯度都可以通过多个路径进行反向传播,这有助于解决梯度消失问题,并提升模型训练的效率。
- 参数效率更高:尽管连接更密集,但 DenseNet 的参数量较少,因为每一层只输出较少的特征图。相比 ResNet,DenseNet
的层更薄,参数量更少,性能却更好。 - 无信息冗余:因为每一层的输入都包含了所有之前层的输出,因此减少了冗余计算,提高了效率。
4. DenseNet 的发展
DenseNet 提出后迅速受到关注,并在多个任务上取得了卓越的表现,包括图像分类、目标检测、语义分割等。在 ImageNet 数据集上,DenseNet 的表现优于其他深度网络如 ResNet 和 Inception。
DenseNet 也催生了多个变种版本和改进,例如:
- DenseNet-BC:增加了对 Bottleneck 结构和 Compression 机制的支持,以进一步减少模型参数和计算复杂度。
- DenseNet for Segmentation:DenseNet 在图像分割任务中表现优越,尤其在医学图像分割中,因其良好的梯度流动性和特征重用性,大大提升了分割的精度。
- DenseNet 与其他网络结合:DenseNet 还被应用于与其他网络架构相结合,如与 LSTM、Transformer 等结合进行时序分析等任务。
5. DenseNet 的应用
DenseNet 广泛应用于以下领域:
- 图像分类:DenseNet 的特征重用性和高效的梯度传递特性,使其在 CIFAR、ImageNet 等数据集上表现优秀。
- 医学影像分析:由于 DenseNet 在信息提取方面的优越性能,在医学影像分析(如病变检测、医学分割)中,DenseNet 作为基础架构有着广泛应用。
- 目标检测:DenseNet 在目标检测任务中,也可以用作主干网络(backbone),例如在 Faster R-CNN 中替代 ResNet。
- 语义分割:DenseNet 由于层间连接密集,在语义分割任务中能够更好地捕捉细节信息,被广泛用于 UNet、DeepLab 等模型的改进版。
6. DenseNet 的 Python 实现
接下来,我们将通过 PyTorch 实现 DenseNet,并使用其进行 CIFAR-10 数据集上的分类任务。代码包含逐行解释。
DenseNet 实现
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
# 定义DenseNet的基本块 - Dense Layer
class DenseLayer(nn.Module):
def __init__(self, in_channels, growth_rate):
super(DenseLayer, self).__init__()
# Batch Normalization -> ReLU -> 3x3 卷积
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):
# 输入 x 是之前所有层的输出
out = self.conv(self.relu(self.bn(x)))
# 将新产生的特征与输入拼接
return torch.cat([x, out], 1)
# 定义DenseBlock
class DenseBlock(nn.Module):
def __init__(self, num_layers, in_channels, growth_rate):
super(DenseBlock, self).__init__()
layers = []
# 在 Dense Block 中,多个 DenseLayer 堆叠
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 TransitionLayer(nn.Module):
def __init__(self, in_channels, out_channels):
super(TransitionLayer, 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, num_blocks, num_layers_per_block, growth_rate, num_classes=10):
super(DenseNet, self).__init__()
# 初始卷积层
self.conv1 = nn.Conv2d(3, 2 * growth_rate, kernel_size=3, padding=1, bias=False)
# 构建Dense Blocks
channels = 2 * growth_rate
self.dense_blocks = nn.ModuleList()
self.transition_layers = nn.ModuleList()
for i in range(num_blocks):
self.dense_blocks.append(DenseBlock(num_layers_per_block, channels, growth_rate))
channels += num_layers_per_block * growth_rate
if i != num_blocks - 1:
# 增加Transition层
self.transition_layers.append(TransitionLayer(channels, channels // 2))
channels = channels // 2
# 全局平均池化层和全连接层
self.bn = nn.BatchNorm2d(channels)
self.relu = nn.ReLU(inplace=True)
self.fc = nn.Linear(channels, num_classes)
def forward(self, x):
# 前向传播
out = self.conv1(x)
for i, block in enumerate(self.dense_blocks):
out = block(out)
if i != len(self.dense_blocks) - 1:
out = self.transition_layers[i](out)
out = self.relu(self.bn(out))
out = F.adaptive_avg_pool2d(out, (1, 1))
out = torch.flatten(out, 1)
out = self.fc(out)
return out
# 超参数设置
growth_rate = 12
num_blocks = 3
num_layers_per_block = 4
# 模型实例化
model = DenseNet(num_blocks, num_layers_per_block, growth_rate)
# 设定训练数据集和测试数据集
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])
train_dataset = datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
test_dataset = datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=1000, shuffle=False)
# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 训练模型
def train(model, device, train_loader, optimizer, criterion, epoch):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = criterion(output, target)
loss.backward()
optimizer.step()
# 测试模型
def test(model, device, test_loader):
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += criterion(output, target).item()
pred = output.argmax(dim=1, keepdim=True)
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /= len(test_loader.dataset)
accuracy = 100. * correct / len(test_loader.dataset)
print(f'Test set: Average loss: {test_loss:.4f}, Accuracy: {correct}/{len(test_loader.dataset)} ({accuracy:.0f}%)')
# 将模型放到GPU上训练
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = model.to(device)
# 进行若干轮的训练与测试
for epoch in range(1, 11):
train(model, device, train_loader, optimizer, criterion, epoch)
test(model, device, test_loader)
代码解释:
- DenseLayer:DenseNet 的核心组件,每一层输出都会与输入拼接。
- DenseBlock:由多个 DenseLayer 组成,用于进行特征的级联输出。
- TransitionLayer:用于减少特征图的大小和通道数,通过 1x1 卷积层和池化操作。
- DenseNet:通过多个 DenseBlock 和 TransitionLayer 组合,实现深层网络。
- 训练和测试代码:模型的训练函数 train() 和测试函数 test(),用于 CIFAR-10 数据集上的训练和评估。
这个实现展示了 DenseNet 的基本架构,适用于图像分类任务。
欢迎宝子们点赞、关注、收藏!欢迎宝子们批评指正!
祝所有的硕博生都能遇到好的导师!好的审稿人!好的同门!顺利毕业!
大多数高校硕博生毕业要求需要参加学术会议,发表EI或者SCI检索的学术论文会议论文:
可访问艾思科蓝官网,浏览2024年即将召开的学术会议列表。会议入口:https://ais.cn/u/mmmiUz