简介
DenseNet的核心思想是改进网络中特征的传递方式,通过在每个层之间建立直接的连接(即稠密连接),来提高信息流动和梯度传播的效率,从而解决深度神经网络中的梯度消失和梯度爆炸问题。
稠密连接:
在DenseNet中,每一层都与前面的所有层相连。具体来说,层的输入包括前面所有层的输出。这意味着网络中的每个层都接收到来自前面所有层的特征图(feature maps)。由于稠密连接,后面的层可以重用前面层学到的特征,这有助于减少参数数量,同时提高特征的利用效率。
import torch
import torch.nn as nn
from torch.nn import functional as F
def conv_block(input_channels, num_channels):
'''
这个函数创建了一个卷积块,
包含批归一化层(BatchNorm2d)、
ReLU激活函数和3x3卷积层。这个块用于每个稠密块(DenseBlock)中的卷积层。
:param input_channels: 输入通道
:param num_channels: 输出通道
:return: 卷积块
'''
return nn.Sequential(
nn.BatchNorm2d(input_channels), nn.ReLU(),
nn.Conv2d(input_channels, num_channels, kernel_size=3, padding=1))
def transition_block(input_channels, num_channels):
'''
这个函数创建了一个转换块,包含批归一化层、ReLU激活函数和1x1卷积层,
以及平均池化层。转换块用于在稠密块之间减少通道数,同时降低特征图的空间维度。
:param input_channels: 输入通道
:param num_channels: 输出通道
:return:转换块
'''
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))
class DenseBlock(nn.Module):
'''
这个类定义了DenseNet中的稠密块。
'''
def __init__(self, num_convs, input_channels, num_channels):
'''
在构造函数中,循环创建指定数量的卷积块,并将它们添加到一个序列中。
:param num_convs: 卷积块的数量
:param input_channels: 当前的通道数
:param 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):
'''
forward 方法定义了稠密块的前向传播过程,将每个卷积块的输出与输入在通道维度上连接起来。
:param X:输入
:return:
'''
for blk in self.net:
Y = blk(X)
# 连接通道维度上每个块的输入和输出
X = torch.cat((X, Y), dim=1)
return X
'''
这是一个序列,包含网络的第一个卷积块,包括一个7x7的卷积层、批归一化层、ReLU激活函数和最大池化层。
这个序列用于网络的初始特征提取。
'''
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))
'''
稠密块和转换层的构建
'''
num_channels, growth_rate = 64, 32 # num_channels为当前的通道数
num_convs_in_dense_blocks = [4, 4, 4, 4]
blks = []
'''
代码中使用了一个循环来构建一系列稠密块和转换层。
每个稠密块由DenseBlock类创建,转换层由transition_block函数创建。
'''
for i, num_convs in enumerate(num_convs_in_dense_blocks):
# 建立num_convs个卷积网络,他的通道数数num_channels,增长的通道倍数growth_rate
blks.append(DenseBlock(num_convs, num_channels, growth_rate))
# 上一个稠密块的输出通道数
num_channels += num_convs * growth_rate
# 在稠密块之间添加一个转换层,使通道数量减半
if i != len(num_convs_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))
print(net)
def forward(self, X):
里面写了每一层都与前面的所有层相连。