目录
跟学视频
1.原理
VGG(Visual Geometry Group)是一个深度卷积神经网络架构,广泛应用于计算机视觉领域。VGG网络的主要特点是深度较大,使用较小的卷积核(3x3)来构建深度网络结构。VGG模型在很多应用中都得到了广泛使用,尤其是在以下几个领域:
图像分类:VGG模型是图像分类任务中常用的深度神经网络之一。它可以应用于从简单物体识别到复杂的多类图像分类任务中。例如,VGG16和VGG19是标准的图像分类模型。
物体检测:VGG常作为物体检测框架的基础网络之一。例如,在基于卷积神经网络的物体检测算法中,VGG可以作为特征提取网络来提取图像的高级特征,进一步用于检测、定位物体。
图像分割:VGG也常用于图像分割任务。通过将VGG作为特征提取部分,与解码器结合,能够进行像素级的图像分割。例如,U-Net和FCN等网络结构中都可以使用VGG作为骨干网络。
迁移学习:VGG模型因其深度和性能,广泛用于迁移学习。当训练数据有限时,可以通过预训练的VGG模型进行迁移学习,应用于医学影像分析、人脸识别等任务。
人脸识别:VGG也被用于一些面向人脸识别的任务,如人脸验证和人脸表情识别,主要通过对大规模人脸数据集的训练来获取有效的特征表示。
风格迁移与图像生成:VGG也常被应用于风格迁移任务,利用VGG的特征提取能力来提取图像的内容和风格,进行图像风格转换。
尽管VGG在一些任务中表现优异,但其计算量较大,尤其是VGG16和VGG19,由于参数较多,训练和推理的效率相对较低。因此,很多现代的网络架构(如ResNet、Inception等)在某些任务中逐渐取代了VGG。不过,由于其结构简单,VGG依然在很多学术研究和实际应用中有着较高的使用率。
VGG在目标检测中使用较多,因为结构简洁,可移植性较强。
CNN——特征提取,提取特征图,再进行如分类、目标检测、实例分割、文字识别等操作。
2014年网络挑战赛,GoogLeNet冠军,VGG亚军。
相对应用来说,VGG用的较多,因为VGG可移植性较强与GoogLeNet。
在深度学习中,网络骨架(或叫网络架构骨架)指的是一个神经网络的基本结构或框架,定义了网络层的类型、层次结构和连接方式。网络骨架通常是指网络的高层设计,专注于网络的整体结构,具体的实现可以使用不同的神经网络组件和技术。
常见的网络骨架架构:
- VGG、ResNet、GoogLeNet等都是深度学习中常见的网络骨架,它们定义了如何搭建卷积神经网络(CNN)来处理视觉任务。
1.1VGG网络诞生背景
1.2VGG网络结构
VGG参数多,1.38亿
全局的kernel_size=3,使参数少,防止过拟合。
步幅越大,下采样越多。
下采样(Downsampling) 是指在数据处理中降低数据的分辨率或尺寸的过程,通常用于减少计算量、加速处理和提取高级特征。在深度学习中,下采样通常指的是通过卷积、池化等操作降低特征图的空间分辨率。
每个卷积之后都有ReLU激活函数。
后面的RestNet解决神经网络够深,但效果不好的问题。
1.3VGG总结
(1)3*3效果也不错
(2)块状结构
(3)网络更深,效果也更好,参数也更多(后续不一定越深效果越好)
2.实战
2.1model.py
import torch
from torch import nn
from torchsummary import summary
class VGG16(nn.Module):
def __init__(self):
super(VGG16, self).__init__()
self.block1 = nn.Sequential( # Sequential功能是将网络的层组合到一起
nn.Conv2d(in_channels=1, out_channels=64, kernel_size=3, padding=1),
nn.ReLU(),
nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, padding=1),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2)
)
self.block2 = nn.Sequential(
nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, padding=1),
nn.ReLU(),
nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, padding=1),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2)
)
self.block3 = nn.Sequential(
nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, padding=1),
nn.ReLU(),
nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, padding=1),
nn.ReLU(),
nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, padding=1),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2)
)
self.block4 = nn.Sequential(
nn.Conv2d(in_channels=256, out_channels=512, kernel_size=3, padding=1),
nn.ReLU(),
nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, padding=1),
nn.ReLU(),
nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, padding=1),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2)
)
self.block5 = nn.Sequential(
nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, padding=1),
nn.ReLU(),
nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, padding=1),
nn.ReLU(),
nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, padding=1),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2)
)
self.block6 = nn.Sequential(
nn.Flatten(),
nn.Linear(7*7*512, 4096),
nn.ReLU(),
nn.Linear(4096, 4096),
nn.ReLU(),
nn.Linear(4096, 10)
)
# 初始化
for m in self.modules(): # 遍历模型的所有子模块
if isinstance(m, nn.Conv2d): # 如果当前模块是二维卷积层
# 使用 Kaiming 正态分布初始化权重,适用于 ReLU 激活函数
nn.init.kaiming_normal_(m.weight, nonlinearity='relu')
if m.bias is not None: # 如果该卷积层有偏置项
# 将偏置初始化为常数 0
nn.init.constant_(m.bias, 0)
elif isinstance(m, nn.Linear): # 如果当前模块是全连接层
# 使用正态分布初始化权重,均值为 0,标准差为 0.01
nn.init.normal_(m.weight, 0, 0.01)
if m.bias is not None: # 如果该全连接层有偏置项
# 将偏置初始化为常数 0
nn.init.constant_(m.bias, 0)
def forward(self, x):
x = self.block1(x)
x = self.block2(x)
x = self.block3(x)
x = self.block4(x)
x = self.block5(x)
x = self.block6(x)
return x
if __name__ == '__main__':
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = VGG16().to(device)
print(summary(model,(1, 224, 224)))
初始化
# 初始化
for m in self.modules(): # 遍历模型的所有子模块
if isinstance(m, nn.Conv2d): # 如果当前模块是二维卷积层
# 使用 Kaiming 正态分布初始化权重,适用于 ReLU 激活函数
nn.init.kaiming_normal_(m.weight, nonlinearity='relu')
if m.bias is not None: # 如果该卷积层有偏置项
# 将偏置初始化为常数 0
nn.init.constant_(m.bias, 0)
elif isinstance(m, nn.Linear): # 如果当前模块是全连接层
# 使用正态分布初始化权重,均值为 0,标准差为 0.01
nn.init.normal_(m.weight, 0, 0.01)
if m.bias is not None: # 如果该全连接层有偏置项
# 将偏置初始化为常数 0
nn.init.constant_(m.bias, 0)
为什么这样初始化?
卷积层:
使用 Kaiming 初始化是为了避免深层网络中梯度的爆炸或消失,尤其适合 ReLU 激活函数。全连接层:
使用小范围的正态分布初始化权重,确保网络在训练初期不会产生过大的激活值。偏置初始化为 0:
偏置项为 0 是一种常见的策略,它不影响权重的梯度流,简化了模型的初始化过程。
2.2model_train.py
import copy
import time
import torch
from torchvision.datasets import FashionMNIST # torchvision.datasets提供了多种常用的数据集接口,特别是用于图像处理和计算机视觉任务的数据集。
from torchvision import transforms # torchvision.transforms提供了一般的图像转换操作类。
import torch.utils.data as Data # torch.utils.data模块提供了用于加载和处理数据的工具。
import numpy as np
import matplotlib.pyplot as plt # matplotlib.pyplot提供了类似MATLAB的绘图API。
from model import VGG16 # 从model.py中导入VGG16模型
import torch.nn as nn
import pandas as pd
def train_val_data_process():
train_data = FashionMNIST(root='./data', # 指定数据集下载或保存的路径,这里为当前目录下的 './data'
train=True, # 加载训练集。如果设置为 False,则加载测试集
transform=transforms.Compose([ # 进行数据预处理和转换
transforms.Resize(size=224),
transforms.ToTensor()]), # 将图像转换为 PyTorch 张量
download=True) # 如果指定的目录下没有数据集,下载数据集
train_data, val_data = Data.random_split(train_data, [round(0.8*len(train_data)), round(0.2*len(train_data))])
train_dataloader = Data.DataLoader(dataset=train_data,
batch_size=12,
shuffle=True,
num_workers=2)
val_dataloader = Data.DataLoader(dataset=val_data,
batch_size=12,
shuffle=True,
num_workers=2)
return train_dataloader, val_dataloader
# 模型训练
def train_model_process(model, train_dataloader, val_dataloader, num_epochs):
# 设定训练所用到的设备,有GPU则使用GPU,否则使用CPU
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
optimizer = torch.optim.Adam(model.parameters(), lr=0.001) # 使用Adam优化器,学习率为0.001
criterion = nn.CrossEntropyLoss() # 使用交叉熵损失函数(回归一般使用均方误差损失函数,分类一般使用交叉熵损失函数)
# 将模型放入到训练设备中
model = model.to(device)
# 复制当前模型的参数
best_model_wts = copy.deepcopy(model.state_dict())
# 初始化参数
# 最高准确度
best_acc = 0.0
# 训练集损失列表
train_loss_all = []
# 验证集损失列表
val_loss_all = []
# 训练集准确度列表
train_acc_all = []
# 验证集准确度列表
val_acc_all = []
# 当前时间
since = time.time()
for epoch in range(num_epochs):
print("Epoch {}/{}".format(epoch, num_epochs - 1))
print("-"*10)
# 初始化参数
# 训练集损失函数
train_loss = 0.0
# 训练集准确度
train_corrects = 0
# 验证集损失函数
val_loss = 0.0
# 验证集准确度
val_corrects = 0
# 训练集样本数量
train_num = 0
# 验证集样本数量
val_num = 0
#对每一个mini-batch训练和计算
for step, (b_x, b_y) in enumerate(train_dataloader):
# 将特征放到训练设备中
b_x = b_x.to(device)
# 将标签放到训练设备中
b_y = b_y.to(device)
# 设置模型为训练模式
model.train()
# 前向传播过程,输入为一个batch,输出为一个batch中对应的预测
output = model(b_x)
# 查找每一行中最大值对应的行标
pre_lab = torch.argmax(output, dim=1)
# 计算每一个batch的损失函数
loss = criterion(output, b_y)
# 将梯度初始化为0,每一轮都要初始化,防止梯度累积
optimizer.zero_grad()
# 反向传播,计算梯度
loss.backward()
# 根据网络反向传播的梯度信息更新网络的参数,以起到降低loss函数计算值的作用
optimizer.step()
# 对损失函数进行累加
train_loss += loss.item() * b_x.size(0)
# 如果预测正确,则准确值train_corrects加1
train_corrects += torch.sum(pre_lab == b_y.data)
# 当前用于训练的样本数量
train_num += b_x.size(0)
for step, (b_x, b_y) in enumerate(val_dataloader):
# 将特征放到验证设备中
b_x = b_x.to(device)
# 将标签放到验证设备中
b_y = b_y.to(device)
# 设置模型为评估模式
model.eval()
# 前向传播过程,输入为一个batch,输出为一个batch中对应的预测
output = model(b_x)
# 查找每一行中最大值对应的行标
pre_lab = torch.argmax(output, dim=1)
# 计算每一个batch的损失函数
loss = criterion(output, b_y)
# 对损失函数进行累加
val_loss += loss.item() * b_x.size(0)
# 如果预测正确,则准确值train_corrects加1
val_corrects += torch.sum(pre_lab == b_y.data)
# 当前用于验证的样本数量
val_num += b_x.size(0)
# 计算并保存每一次迭代的loss值和准确值
# 计算并保存训练集的loss值
train_loss_all.append(train_loss / train_num)
# 计算并保存训练集的准确率
train_acc_all.append(train_corrects.double().item() / train_num)
# 计算并保存验证集的loss值
val_loss_all.append(val_loss / val_num)
# 计算并保存验证集的准确率
val_acc_all.append(val_corrects.double().item() / val_num)
print("{} train loss:{:.4f} train acc:{:.4f}".format(epoch, train_loss_all[-1], train_acc_all[-1]))
print("{} val loss:{:.4f} val acc:{:.4f}".format(epoch, val_loss_all[-1], val_acc_all[-1]))
# 寻找最高准确度的权重
if val_acc_all[-1] > best_acc:
# 保存当前的最高准确度
best_acc = val_acc_all[-1]
# 保存当前最高准确度对应的模型参数
best_model_wts = copy.deepcopy(model.state_dict())
# 训练耗费时间
time_use = time.time() - since
print("训练和验证耗费时间:{:.0f}m {:.0f}s".format(time_use // 60, time_use % 60))
# 选择最优参数
# 加载最高准确率下的模型参数
torch.save(best_model_wts, 'D:/code/pych/pytorch_learning/VGG16/best_model.pth')
train_process = pd.DataFrame(data={"epoch": range(num_epochs),
"train_loss_all": train_loss_all,
"val_loss_all": val_loss_all,
"train_acc_all": train_acc_all,
"val_acc_all": val_acc_all})
return train_process
def matplot_acc_loss(train_process):
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(train_process["epoch"], train_process.train_loss_all, 'ro-', label="train_loss")
plt.plot(train_process["epoch"], train_process.val_loss_all, 'bs-', label="val_loss")
plt.legend()
plt.xlabel("epoch")
plt.ylabel("loss")
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 2)
plt.plot(train_process["epoch"], train_process.train_acc_all, 'ro-', label="train_acc")
plt.plot(train_process["epoch"], train_process.val_acc_all, 'bs-', label="val_acc")
plt.legend()
plt.xlabel("epoch")
plt.ylabel("acc")
plt.show()
if __name__ == "__main__":
# 实例化LeNet模型
VGG16 = VGG16()
train_dataloader, val_dataloader = train_val_data_process()
train_process = train_model_process(VGG16, train_dataloader, val_dataloader, 20)
matplot_acc_loss(train_process)
梯度消失,权重几乎不更新,一直不收敛,所以训练准确度低。
下面通过加上权重初始化,来使训练结果更加满足预期效果。
权重初始化对深度学习模型的训练至关重要。正确的初始化可以加快收敛速度,避免梯度消失或梯度爆炸问题,从而提升模型性能。在使用 VGG16 或类似的深度网络时,以下是几种常见的权重初始化方法及其代码实现。
2.3model_test.py
import torch
import torch.utils.data as Data
from torchvision import transforms
from torchvision.datasets import FashionMNIST
from model import VGG16
def test_data_process():
test_data = FashionMNIST(root='./data', # 指定数据集下载或保存的路径,这里为当前目录下的 './data'
train=False, # 加载测试集。如果设置为 False,则加载测试集
transform=transforms.Compose([ # 进行数据预处理和转换
transforms.Resize(size=224),
transforms.ToTensor()]), # 将图像转换为 PyTorch 张量
download=True)
test_dataloader = Data.DataLoader(dataset=test_data,
batch_size=1,
shuffle=True,
num_workers=0)
return test_dataloader
def test_model_process(model, test_dataloader):
# 设定训练所用到的设备,有GPU则使用GPU,否则使用CPU
device = "cuda" if torch.cuda.is_available() else "cpu"
# 将模型放入到测试设备中
model = model.to(device)
# 初始化参数
test_correct = 0.0
test_num = 0
# 只进行前向传播,不进行梯度计算,从而节省内存,加快运行速度
with torch.no_grad():
for test_data_x, test_data_y in test_dataloader:
# 将数据放入到测试设备中
test_data_x = test_data_x.to(device)
# 将标签放入到测试设备中
test_data_y = test_data_y.to(device)
# 将模型设置为评估模式
model.eval()
# 前向传播过程,输入为测试数据集,输出每个样本的预测值
output = model(test_data_x)
# 查找每一行中最大值对应的行标
pre_lab = torch.argmax(output, dim=1)
# 如果预测正确,则精确度test_correct加1
test_correct += torch.sum(pre_lab == test_data_y.data)
# 将所有的测试样本累加
test_num += test_data_x.size(0)
# 计算测试集的准确度
test_acc = test_correct.double().item() / test_num
print("测试的准确率为:", test_acc)
if __name__ == "__main__":
# 加载模型
model = VGG16()
model.load_state_dict(torch.load("best_model.pth"))
# 加载测试数据
test_dataloader = test_data_process()
# 加载模型测试的函数
#test_model_process(model, test_dataloader)
# 设定测试所用到的设备,有GPU则使用GPU,否则使用CPU
device = "cuda" if torch.cuda.is_available() else "cpu"
model = model.to(device)
classes = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']
with torch.no_grad():
for b_x, b_y in test_dataloader:
b_x = b_x.to(device)
b_y = b_y.to(device)
model.eval()
output = model(b_x)
pre_lab = torch.argmax(output, dim=1)
result = pre_lab.item()
label = b_y.item()
print("预测值:", classes[result], "-------", "真实值:", classes[label])
标签:loss,val,nn,Day4,VGG,Pytorch,train,model,data
From: https://blog.csdn.net/weixin_74106693/article/details/145171006