PyTorch实现DenseNet网络模型与实战
- 🍨 本文为🔗365天深度学习训练营 中的学习记录博客**
- 🍖 原作者:K同学啊
前言:接着前面对ResNet网络模型的学习,我们可以将DenseNet看成ResNet网络的推广,同时又在结构上做了调整和优化,进一步提高了网络训练的准确性。
目录
文章目录
一、Dense Net介绍
由前面章节对ResNet网络的学习了解到,ResNet网络模型主要是建立相邻层之间的联系,将某一层的输入与输出相加作为下一层的输入,用公式可以表示:
x 1 = H l ( x l − 1 ) + x l − 1 x_1=H_l(x_l-_1 )+x_l-_1 x1=Hl(xl−1)+xl−1
在DenseNet我们可以将其类比理解为将前面的网络都加入到后面网络的输入,通过这种方式加强了特征传播,同时有利于后向传播缓解梯度消失,用公式可以表示:
x 1 = H l ( [ x 0 , x 1 , x 2 … … ] ) x_1=H_l([x_0,x_1,x_2……]) x1=Hl([x0,x1,x2……])
DenseNet网络主要由多个Dense Block组成,相邻的Dense Block 通过Transition模块连接,Dense Block通常包括,BN,激活函数(通常为ReLU函数),PooLing层,及Conv卷积层 ,并保持具有相同的特征图,能在channel维度上连接。
- DenseBlock采用的是pre-activition(即激活在前BatchNorm在后,这一点改进其实在ResNet网络中就有应用,并且效果相比改进前有所提高。)随着后面层的输入的增大,DenseNetke可加入由BN,ReLU,1x1卷积组成的模块来减少计算参数。如:输入通道数为64,增长率为32,经过15个Bottleneck,通道数输出为64+15*32=544,若不使用1X1卷积,第16个Bottleck层参数量为3*3*544*32=156672,若采用1X1卷积,第16个Bottleneck层参数为1*1*544*128+3*3*128*32=106496,参数量大大降低了。
- Transition 主要连接两个相邻的DenseBlock,并且设定其压缩因子a来缩小特征图的大小,假设Transition左侧输入的特征图channel为m,则通过Transition层的输出为a*m,
- 整体网络框架:一共有一个初始层,4个DenseBlock层和一个分类层组成。具体结构图如下:
二、实验部分
2.1 实验介绍:
本次实验主要是使用Pytorch搭建DenseNet-101实现乳腺癌数据集识别任务
-
语言环境:Python 3.8
-
编译器 : Jupyter Lab
-
深度学习环境:Pytorch
torch==1.12.1+cu113
torchvision==0.13.1+cu113
2.2 代码部分——PyTorch 框架搭建DenseNet 实现乳腺癌识别
2.2.1导入库函数
import torch
import torch.nn as nn
import torchvision.transforms as transforms
import torchvision
from torchvision import transforms,datasets
import os,PIL,pathlib,warnings
warnings.filterwarnings("ignore")
device=torch.device("cuda"if torch.cuda.is_available()else "cpu")
device
device(type='cuda')
import os,PIL,random,pathlib
data_dir="data/"
data_dir=pathlib.Path(data_dir)
data_paths=list(data_dir.glob('*'))
classeNames=[str(path).split("/")[1] for path in data_paths]
classeNames
['1', '0']
2.2.2图片批量标准化
Transform=transforms.Compose([
transforms.Resize([224,224]),
transforms.ToTensor(),
transforms.Normalize(
mean=[0.485,0.456,0.406],
std=[0.229,0.224,0.225])
])
total_data=datasets.ImageFolder(data_dir,transform=Transform)
total_data
Dataset ImageFolder
Number of datapoints: 7037
Root location: data
StandardTransform
Transform: Compose(
Resize(size=[224, 224], interpolation=bilinear, max_size=None, antialias=True)
ToTensor()
Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
)
total_data.class_to_idx
{'0': 0, '1': 1}
2.2.3划分数据集
import torch.utils
train_size=int(0.8*len(total_data))
test_size=len(total_data)-train_size
train_dataset,test_dataset=torch.utils.data.random_split(total_data,[train_size,test_size])
train_dataset,test_dataset
(<torch.utils.data.dataset.Subset at 0x7f72c2529cd0>,
<torch.utils.data.dataset.Subset at 0x7f72c2529d90>)
from random import shuffle
batch_size=32
train_dl=torch.utils.data.DataLoader(train_dataset,
batch_size=batch_size,
shuffle=True
)
test_dl=torch.utils.data.DataLoader(test_dataset,
batch_size=batch_size,
shuffle=True)
for X ,y in test_dl:
print("Shape of X [N,C,H,W]: ", X.shape)
print("Shape of y: ",y.shape,y.dtype)
break
Shape of X [N,C,H,W]: torch.Size([32, 3, 224, 224])
Shape of y: torch.Size([32]) torch.int64
2.2.4搭建模型
from collections import OrderedDict
import torch
import torch.nn as nn
import torch.nn.functional as F
#密集层定义
class DenseLayer(nn.Sequential):
def __init__(self,in_channel,growth_rate,bn_size,drop_rate):
super(DenseLayer,self).__init__()
#self.add_module()用于向DenseNet类添加子模块,参数为这个模块的命名name,以及添加的模块名
# 添加第一个卷积块 (1x1 卷积)
self.add_module('normal',nn.BatchNorm2d(in_channel))
self.add_module('relu1',nn.ReLU(inplace=True))
self.add_module('conv1',nn.Conv2d(in_channel,
bn_size*growth_rate,
kernel_size=1,stride=1,
bias=False))
# 添加第二个卷积块 (3x3 卷积)
self.add_module('norm2',nn.BatchNorm2d(bn_size*growth_rate))
self.add_module('rule2',nn.ReLU(inplace=True))
self.add_module('conv2',nn.Conv2d(bn_size*growth_rate,
growth_rate,kernel_size=3,
stride=1,padding=1,bias=False))
self.drop_rate=drop_rate
def forward(self,x):
new_feature=super(DenseLayer,self).forward(x)
if self.drop_rate>0:
new_feature=F.dropout(new_feature,p=self.drop_rate,training=self.training)
return torch.cat([x,new_feature],1)
2.2.5DenseBlock模块定义
#每个密集块由多个密集层(DenseLayer)组成,这些层通过在通道维度上拼接来增加特征图的数量。
class DenseBlock(nn.Sequential):
def __init__(self,num_layers,in_channel,bn_size,growth_rate,drop_rate):
'''层数 (num_layers)、输入通道数 (in_channel)、瓶颈层乘数 (bn_size)、
增长率 (growth_rate) 和 dropout 比率 (drop_rate)
'''
super(DenseBlock,self).__init__()
for i