🍨 本文为🔗365天深度学习训练营365天深度学习训练营 中的学习记录博客
🍦 参考文章:365天深度学习训练营-第J2周:ResNet50V2算法实战与解析
🍖 原作者:K同学啊 | 接辅导、项目定制
🚀 文章来源:K同学的学习圈子
我的环境
语言环境:python:3.7
编译器:jupytoer notebook
深度学习环境:Pytorch
torch == 2.0.1+cu117
学习目标:
使用pytorch搭建DensNet网络
一、前言
在计算机视觉领域,卷积神经网络(CNN)已经成为最主流的方法,比如GoogLenet,VGG-16,Incepetion等模型。CNN史上的一个里程碑事件是ResNet模型的出现,ResNet可以训练出更深的CNN模型,从而实现更高的准确度。ResNet模型的核心是通过建立前面层与后面层之间的“短路连接”(shortcuts,skip connection),进而训练出更深的CNN网络。
今天我们要介绍的是DenseNet模型,它的基本思路与ResNet一致,但是它建立的是前面所有层与后面层的密集连接(dense connection),它的名称也是由此而来。DenseNet的另一大特色是通过特征在channel上的连接来实现特征重用(feature reuse)。这些特点让DenseNet在参数和计算成本更少的情形下实现比ResNet更优的性能,DenseNet也因此斩获CVPR 2017的最佳论文奖。
二、设计理念
相比ResNet,DenseNet提出了一个更激进的密集连接机制:即互相连接所有的层,具体来说就是每个层都会接受其前面所有层作为其额外的输入。
图1为ResNet网络的残差连接机制,作为对比,图2为DenseNet的密集连接机制。可以看到,ResNet是每个层与前面的某层(一般是2~4层)短路连接在一起,连接方式是通过元素相加。而在DenseNet中,每个层都会与前面所有层在channel维度上连接(concat)在一起(即元素叠加),并作为下一层的输入。
对于一个L层的网络,DenseNet共包含 L(L+1)/2个连接,相比ResNet,这是一种密集连接。而且DenseNet是直接concat来自不同层的特征图,这可以实现特征重用,提升效率,这一特点是DenseNet与ResNet最主要的区别。
三、DenseNet121结构图
import tensorflow as tf
import torch
import torchvision
import torchvision.transforms as transforms
from torchvision import transforms,datasets
import os,pathlib,PIL
device = 'cuda' if torch.cuda.is_available() else 'cpu'
device
data_dir = './第8天/bird_photos/'
data_dir = pathlib.Path(data_dir)
data_paths = list(data_dir.glob('*'))
classeNames = [str(path).split('\\')[2] for path in data_paths]
transforms = 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('./第8天/bird_photos/',transform=transforms)
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])
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)
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('norm1', 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))
self.add_module('norm2', nn.BatchNorm2d(bn_size*growth_rate))
self.add_module('relu2', 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)
class DenseBlock(nn.Sequential):
def __init__(self, num_layers, in_channel, bn_size, growth_rate, drop_rate):
super(DenseBlock, self).__init__()
for i in range(num_layers):
layer = DenseLayer(in_channel+i*growth_rate, growth_rate, bn_size, drop_rate)
self.add_module('denselayer%d'%(i+1,), layer)
# DenseBlock
class Transition(nn.Sequential):
def __init__(self, in_channel, out_channel):
super(Transition, self).__init__()
self.add_module('norm', nn.BatchNorm2d(in_channel))
self.add_module('relu', nn.ReLU(inplace=True))
self.add_module('conv', nn.Conv2d(in_channel, out_channel,
kernel_size=1, stride=1, bias=False))
self.add_module('pool', nn.AvgPool2d(2, stride=2))
class DenseNet(nn.Module):
def __init__(self, growth_rate=32, block_config=(6,12,24,16), init_channel=64,
bn_size=4, compression_rate=0.5, drop_rate=0, num_classes=1000):
'''
:param growth_rate: (int) number of filters used in DenseLayer, `k` in the paper
:param block_config: (list of 4 ints) number of layers in eatch DenseBlock
:param init_channel: (int) number of filters in the first Conv2d
:param bn_size: (int) the factor using in the bottleneck layer
:param compression_rate: (float) the compression rate used in Transition Layer
:param drop_rate: (float) the drop rate after each DenseLayer
:param num_classes: (int) 待分类的类别数
'''
super(DenseNet, self).__init__()
# first Conv2d
self.features = nn.Sequential(OrderedDict([
('conv0', nn.Conv2d(3, init_channel, kernel_size=7, stride=2, padding=3, bias=False)),
('norm0', nn.BatchNorm2d(init_channel)),
('relu0', nn.ReLU(inplace=True)),
('pool0', nn.MaxPool2d(3, stride=2, padding=1))
]))
# DenseBlock
num_features = init_channel
for i, num_layers in enumerate(block_config):
block = DenseBlock(num_layers, num_features, bn_size, growth_rate, drop_rate)
self.features.add_module('denseblock%d'%(i+1), block)
num_features += num_layers*growth_rate
if i != len(block_config)-1:
transition = Transition(num_features, int(num_features*compression_rate))
self.features.add_module('transition%d'%(i+1), transition)
num_features = int(num_features*compression_rate)
# final BN+ReLU
self.features.add_module('norm5', nn.BatchNorm2d(num_features))
self.features.add_module('relu5', nn.ReLU(inplace=True))
# 分类层
self.classifier = nn.Linear(num_features, num_classes)
# 参数初始化
for m in self.modules():
if isinstance(m, nn.Conv2d):
nn.init.kaiming_normal_(m.weight)
elif isinstance(m, nn.BatchNorm2d):
nn.init.constant_(m.bias, 0)
nn.init.constant_(m.weight, 1)
elif isinstance(m, nn.Linear):
nn.init.constant_(m.bias, 0)
def forward(self, x):
x = self.features(x)
x = F.avg_pool2d(x, 7, stride=1).view(x.size(0), -1)
x = self.classifier(x)
return x
densenet121 = DenseNet(init_channel=64,
growth_rate=32,
block_config=(6,12,24,16),
num_classes=len(classeNames))
model = densenet121.to(device)
model
‘# 初试化列表来存储训练和验证的准确率和损失值
train_acc = []
val_acc=[]
train_loss=[]
val_loss = []
for epoch in range(epochs):
# 训练阶段
model.train()
running_loss = 0.0
acc = 0
for data,label in train_dl:
data, label = data.to(device), label.to(device)
optimizer.zero_grad()
pred_out = model(data)
print(pred_out)
print(label)
loss = loss_fun(pred_out, label)
loss.backward()
optimizer.step()
running_loss += loss.item()
_,predicted = torch.max(pred_out.data,1)
acc += (predicted == label).sum().item()
epoch_loss = running_loss / len(train_dl)
epoch_acc = acc/len(train_dl.dataset)
train_loss.append(epoch_loss)
train_acc.append(epoch_acc)
# 验证阶段
model.eval()
running_loss = 0.0
acc = 0
with torch.no_grad():
for data, label in test_dl:
data, label = data.to(device), label.to(device)
output = model(data)
loss = loss_fun(output,label)
running_loss += loss.item()
_,predicted = torch.max(output.data,1)
acc += (predicted == label).sum().item()
epoch_loss = running_loss/len(test_dl)
epoch_acc = acc/len(test_dl.dataset)
val_acc.append(epoch_acc)
val_loss.append(epoch_loss)
print('Epoch{}/{},Train_loss:{:.4f}, Train_acc:{:.2f}, Val_loss:{:.4f}, Val_acc:{:.2f}'
.format(epoch+1,epochs,train_loss[-1],train_acc[-1]*100, val_loss[-1],val_acc[-1]*100))
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] =False
plt.rcParams['figure.dpi'] = 100
plt.figure(figsize = (12,3))
plt.subplot(1,2,1)
plt.plot(range(epochs), train_acc, label='Training Accuracy')
plt.plot(range(epochs), val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')
plt.subplot(1,2,2)
plt.plot(range(epochs), train_loss, label='Training Loss')
plt.plot(range(epochs), val_loss, label='Validation loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()
总结:模型的迭代速度很快,但是模型在cpu上训练了很久,训练效果可以。
参考链接:https://blog.csdn.net/qq_42286481/article/details/136950385