- 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
- 🍖 原作者:K同学啊
目标
具体实现
(一)环境
语言环境:Python 3.10
编 译 器: PyCharm
框 架: Pytorch
(二)具体步骤
1. DenseNet121.py
import torch
import torch.nn as nn
import torch.nn.functional as F
import math
# 实现DenseLayer(密集连接层)
class DenseLayer(nn.Module):
def __init__(self, num_input_features, growth_rate, bn_size, drop_rate):
super(DenseLayer, self).__init__()
# BN -> ReLU -> Conv(1x1) -> BN -> ReLU -> Conv(3x3)
self.norm1 = nn.BatchNorm2d(num_input_features) # 第一个批归一化层
self.relu1 = nn.ReLU(inplace=True) # 第一个ReLU激活函数
self.conv1 = nn.Conv2d(num_input_features, bn_size * growth_rate,
kernel_size=1, stride=1, bias=False) # 1x1卷积层
self.norm2 = nn.BatchNorm2d(bn_size * growth_rate) # 第二个批归一化层
self.relu2 = nn.ReLU(inplace=True) # 第二个ReLU激活函数
self.conv2 = nn.Conv2d(bn_size * growth_rate, growth_rate,
kernel_size=3, stride=1, padding=1, bias=False) # 3x3卷积层
self.drop_rate = drop_rate # Dropout率
def forward(self, x):
# 保存输入特征,用于后续的密集连接
new_features = self.norm1(x)
new_features = self.relu1(new_features)
new_features = self.conv1(new_features)
new_features = self.norm2(new_features)
new_features = self.relu2(new_features)
new_features = self.conv2(new_features)
# 如果设置了dropout,则应用dropout
if self.drop_rate > 0:
new_features = F.dropout(new_features, p=self.drop_rate, training=self.training)
# 将新特征与输入特征进行拼接,实现密集连接
return torch.cat([x, new_features], 1)
# 实现DenseBlock(密集块)
class DenseBlock(nn.Module):
def __init__(self, num_layers, num_input_features, bn_size, growth_rate, drop_rate):
super(DenseBlock, self).__init__()
# 创建指定数量的DenseLayer,每一层的输入特征数量都会增加
self.layers = nn.ModuleList()
for i in range(num_layers):
layer = DenseLayer(
num_input_features + i * growth_rate,
growth_rate=growth_rate,
bn_size=bn_size,
drop_rate=drop_rate
)
self.layers.append(layer)
def forward(self, x):
# 依次通过所有的DenseLayer
features = x
for layer in self.layers:
features = layer(features)
return features
# 实现TransitionLayer(过渡层)
class TransitionLayer(nn.Module):
def __init__(self, num_input_features, num_output_features):
super(TransitionLayer, self).__init__()
# BN -> Conv(1x1) -> AvgPool(2x2)
self.norm = nn.BatchNorm2d(num_input_features) # 批归一化层
self.relu = nn.ReLU(inplace=True) # ReLU激活函数
self.conv = nn.Conv2d(num_input_features, num_output_features,
kernel_size=1, stride=1, bias=False) # 1x1卷积层
self.pool = nn.AvgPool2d(kernel_size=2, stride=2) # 平均池化层
def forward(self, x):
x = self.norm(x)
x = self.relu(x)
x = self.conv(x)
x = self.pool(x)
return x
# 实现完整的DenseNet121模型
class DenseNet121(nn.Module):
def __init__(self, growth_rate=32, block_config=(6, 12, 24, 16),
num_init_features=64, bn_size=4, drop_rate=0, num_classes=1000):
super(DenseNet121, self).__init__()
# 首先是一个7x7的卷积层,步长为2
self.features = nn.Sequential()
self.features.add_module('conv0',
nn.Conv2d(3, num_init_features, kernel_size=7, stride=2, padding=3, bias=False)) # 初始卷积层
self.features.add_module('norm0', nn.BatchNorm2d(num_init_features)) # 批归一化层
self.features.add_module('relu0', nn.ReLU(inplace=True)) # ReLU激活函数
self.features.add_module('pool0', nn.MaxPool2d(kernel_size=3, stride=2, padding=1)) # 最大池化层
# 依次添加DenseBlock和TransitionLayer
num_features = num_init_features
for i, num_layers in enumerate(block_config):
# 添加DenseBlock
block = DenseBlock(
num_layers=num_layers,
num_input_features=num_features,
bn_size=bn_size,
growth_rate=growth_rate,
drop_rate=drop_rate
)
self.features.add_module(f'denseblock{i + 1}', block)
num_features = num_features + num_layers * growth_rate
# 如果不是最后一个block,则添加TransitionLayer
if i != len(block_config) - 1:
# 过渡层将特征图数量减半
trans = TransitionLayer(
num_input_features=num_features,
num_output_features=num_features // 2
)
self.features.add_module(f'transition{i + 1}', trans)
num_features = num_features // 2
# 最后添加一个BatchNorm
self.features.add_module('norm5', nn.BatchNorm2d(num_features)) # 最终的批归一化层
# 全局平均池化和分类器
self.classifier = nn.Linear(num_features, num_classes) # 全连接分类器
# 初始化权重
for m in self.modules():
if isinstance(m, nn.Conv2d):
nn.init.kaiming_normal_(m.weight) # 使用Kaiming初始化卷积层权重
elif isinstance(m, nn.BatchNorm2d):
nn.init.constant_(m.weight, 1) # 初始化批归一化层的权重为1
nn.init.constant_(m.bias, 0) # 初始化批归一化层的偏置为0
elif isinstance(m, nn.Linear):
nn.init.constant_(m.bias, 0) # 初始化全连接层的偏置为0
def forward(self, x):
features = self.features(x) # 提取特征
out = F.relu(features, inplace=True) # 应用ReLU激活函数
out = F.adaptive_avg_pool2d(out, (1, 1)) # 全局平均池化
out = torch.flatten(out, 1) # 展平特征
out = self.classifier(out) # 分类
return out
# 创建DenseNet121模型实例
def create_densenet121(num_classes=1000, pretrained=False):
model = DenseNet121(num_classes=num_classes)
return model
# 使用示例
if __name__ == "__main__":
# 创建模型
model = create_densenet121()
print(model)
# 创建随机输入张量 (batch_size, channels, height, width) x = torch.randn(1, 3, 224, 224)
# 前向传播
output = model(x)
print(f"Input shape: {x.shape}")
print(f"Output shape: {output.shape}")