当前位置: 首页 > news >正文

VGG网络模型

VGG网络模型

诞生背景

VGGNet是牛津大学计算机视觉组核谷歌DeepMind一起研究出来的深度卷积神经网络。VGG是一种被广泛使用的卷积神经网络结构,其在2014年的ImageNet大规模视觉识别挑战中获得亚军。

通常所说的VGG是指VGG-16(13层卷积层+3层全连接层)。具有规律的设计,简洁可堆叠的卷积块,且在其他数据集上都有着良好的表现,从而被广泛的使用。

VGG和AlexNet相比,深度更深,参数更多(1.38亿),效果和可移植性更好。

VGG网络模型的特点,每层网络模型,是以块为单位的。

模型架构图

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

环境搭建

创建一个文件夹,作为项目的根目录,就叫做VGG16吧。

pytorch v10.1

cuda 11.3

额外库:

torchsummary == 1.5.1

sklearn == 0.0

pytorch环境安装命令

conda install pytorch==1.10.1 torchvision==0.11.2 torchaudio==0.10.1 cudatoolkit=11.3 -c pytorch

额外安装库的命令

conda install -i https://pypi.tuna.tsinghua.edu.cn/simple/ torchsummary==1.5.1 sklearn==0.0
或在激活环境的情况下:
pip install -i http://mirrors.aliyun.com/pypi/simple/ torchsummary==1.5.1 sklearn==0.0

数据集准备

然后新建一个python文件,就叫plot.py吧,往里面写入以下代码,用于下载数据集:

# FashionMNIST里面包含了许多数据集
from click.core import batch
from spacy.cli.train import train
from torchvision.datasets import FashionMNIST
from torchvision import transforms # 处理数据集,归一化
import torch.utils.data as Data
import numpy as np
import matplotlib.pyplot as plt# 下载FashionMMIST数据集
train_data = FashionMNIST(root="./data", # 指定数据集要下载的路径train=True,# 要训练集# 将数据进行归一化操作transform=transforms.Compose([transforms.Resize(size=224), # 调整数据的大小transforms.ToTensor() # 将数据转换为tensor]),download=True # 开启下载
)# 加载数据集集
train_loader = Data.DataLoader(dataset=train_data,# 要加载的数据集batch_size=64 ,# 批量数据大小shuffle=True, # 打乱数据顺序num_workers=0, # 加载数据线程数量
)# 绘制出训练集
for step,(b_x,b_y) in enumerate(train_loader):if step > 0:breakbatch_x = b_x.squeeze().numpy() # 将四维张量移除第一维,将数据转换为numpy格式batch_y = b_y.numpy() # 将张量数据转成numpy格式class_label = train_data.classes # 训练集标签
print("class_label,",class_label)# 绘图
plt.figure(figsize=(12,5))
for ii in np.arange(len(batch_y)):plt.subplot(4,16,ii+1)plt.imshow(batch_x[ii, : , :],cmap=plt.cm.gray)plt.title(class_label[batch_y[ii]],size=10)plt.axis('off')plt.subplots_adjust(wspace=0.05)plt.show()

执行上述代码后,就会开始下载所需要的数据集文件,只不过下载的速度比较慢,然后下载完成,项目的根目录会多出data文件夹,以下是data的目录结构:

--data--FashionMNIST--raw # 该文件夹下就存放数据集文件

模型搭建

创建一个model.py文件,来搭建模型

import torch
from torch import nn
from torchsummary import summaryclass VGG16(nn.Module):def __init__(self):super(VGG16,self).__init__()# 定义第一层块# Sequential是一个序列,里面包含卷积,激活函数,池化等,相当于封装self.block1 = nn.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,256), # 全连接层 输入原先是7*7*512(没变),输出原先是4096nn.ReLU(),nn.Linear(256,128), # 全连接层 输入原先是4096,输出原先是4096nn.ReLU(),nn.Linear(128,10), # 全连接层 作为输出层 输入原先是4096,输出原先是10)# ====权重初始化操作,解决模型的不收敛的问题====for m in self.modules(): # 循环遍历模型# print(m) # 打印模型的每一块,每一层if isinstance(m,nn.Conv2d): # 如果m是卷积# m.weight 模型每一层的参数,nonlinearity激活函数nn.init.kaiming_normal_(m.weight,nonlinearity='relu') # 使用 凯明初始化 方式if m.bias is not None: # 判断b参数(偏置)是否为空 约定俗成的操作nn.init.constant_(m.bias,0) # 让b的值为0elif isinstance(m,nn.Linear):# m.weight表示w参数,0表示均值,0.01表示方差nn.init.normal_(m.weight,0,0.01) # 正太分布初始化 归一化if m.bias is not None: # 判断b参数(偏置)是否为空 约定俗成的操作nn.init.constant_(m.bias,0) # 让b的值为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)))

模型不收敛的问题:

当模型的参数比较深的时候,就会出现该类问题。

先进行权重初始化(上面代码实现了),如果权重初始化设置了,模型还不收敛,那就尝试修改批次大小(batch_size) 和 尝试修改block6层的输入输出大小,这里就有点玄学的概念了,哈哈哈哈

模型训练

创建一个model_train.py文件,用作模型的训练

import copy
import timeimport torch
from torchvision.datasets import FashionMNIST
from torchvision import transforms
import torch.nn as nn
import torch.utils.data as Data
import numpy as np
import pandas as pd
import matplotlib.pyplot as pltfrom model import VGG16# 处理训练集核数据集
def train_val_data_process():# 加载数据集train_data = FashionMNIST(root="./data",# 数据集所在路径train=True, # 要训练集# 将数据进行归一化操作transform=transforms.Compose([transforms.Resize(size=224), # 修改数据的大小transforms.ToTensor() # 将数据转成Tensor格式]),download=True # 开启加载)# 随机 划分训练集 和 验证集train_data,val_data = Data.random_split(train_data, # 要划分的数据集[round(0.8*len(train_data)), # 划分80%给训练集round(0.2*len(train_data)) # 划分20%给验证集])# 加载训练集数据train_dataloader = Data.DataLoader(dataset=train_data,# 要加载的训练集batch_size=28,# 每轮的训练批次数 要调整shuffle=True,# 打乱数据顺序num_workers=2,# 加载数据线程数量)# 加载验证集数据val_dataloader = Data.DataLoader(dataset=val_data,# 要加载的验证集batch_size=28,# 每轮的训练批数 要调整shuffle=True,# 打乱数据顺序num_workers=2,# 加载数据集的线程数量)return train_dataloader,val_dataloader# 模型训练
def train_model_process(model,train_dataloader,val_dataloader,num_epochs):# model:需要训练的模型,train_dataloader:训练集数据,val_dataloader:验证集数据,num_epochs:训练轮数# 指定设备device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 定义优化器optimizer = torch.optim.Adam(model.parameters(),lr=0.001)# 定义交叉熵损失函数criterion = nn.CrossEntropyLoss()# 将模型放入到设备中进行训练model.to(device)# 复制当前模型的参数best_model_wts = copy.deepcopy(model.state_dict())# 初始化参数,记录模型的的精确度和损失值best_acc = 0.0 # 最高精确度train_loss_all = [] # 训练集的总损失train_acc_all = [] # 训练集的总精度val_loss_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_corrents = 0 # 验证集的准确度train_num = 0 # 本轮训练集的数量val_num = 0 # 本轮验证集的数量# 取出每轮中的数据集进行训练for step,(b_x,b_y) in enumerate(train_dataloader):b_x = b_x.to(device) # 将训练集数据放入到设备当中b_y = b_y.to(device) # 将标签数据放入到设备当中model.train() # 开启模型训练模式# 将每批次中的标签数据放入到模型中,进行前向传播output = model(b_x)# 查找每一行中最大值对应的行标,即预测值pre_lab = torch.argmax(output,dim=1)# 计算当前批次的损失值(模型的输出,标签)loss = criterion(output,b_y)# 每批次训练完后,将梯度初始化成0optimizer.zero_grad()# 反向传播计算loss.backward()# 更新参数optimizer.step()# 本批次损失值的累加train_loss += loss.item() * b_x.size(0)# 如果模型预测的结果正确,本批次的准确度+1train_corrects += torch.sum(pre_lab == b_y.data)# 本此次的训练数据累加train_num += b_y.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() # 设置模型为评估模式# 前向传播,输入一个批次,输出该批次的对应的预测值output = model(b_x)# 查找每一行中最大值对应的行标,即预测值pre_lab = torch.argmax(output,dim=1)# 计算本此次的损失函数loss = criterion(output,b_y)# 本批次的损失函数累加val_loss += loss.item() * b_x.size(0)# 如果预测正确,那就本批次的精度度累加val_corrents += torch.sum(pre_lab==b_y.data)# 当前用于验证的样本数累加val_num += b_x.size(0)# 计算每轮次的损失值和准确率train_loss_all.append(train_loss / train_num) # 本轮训练集的loss值train_acc_all.append(train_corrects.double().item() / train_num) # 本轮训练集的准确率val_loss_all.append(val_loss / val_num) # 本轮验证集的loss值val_acc_all.append(val_corrents.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() - sinceprint("训练耗费的时间:{:.0f}m{:.0f}s".format(time_use//60,time_use%60))# 将最佳的模型参数保存torch.save(best_model_wts,"best_model.pth") # .pth是权重文件的后缀# 保存训练好的模型参数train_process = pd.DataFrame(data={"epoch":range(num_epochs),"train_loss_all":train_loss_all,"train_acc_all":train_acc_all,"val_loss_all":val_loss_all,"val_acc_all":val_acc_all})return train_process# 定义绘图的函数,绘制loss和准确度
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_acc_all,"bs-",label="val loss")plt.legend() # 图例plt.xlabel("epoch") # x轴标签plt.ylabel("loss") # y轴标签# 绘制训练集和验证集的准确度图像plt.subplot(1,2,2) # 一行两列,第二列plt.plot(train_process['epoch'],train_process.val_loss_all,"ro-",label="train acc")plt.plot(train_process['epoch'],train_process.val_acc_all,"bs-",label="va acc")if __name__ == '__main__':# 实例化自定义模型类model = VGG16()# 加载数据集train_dataloader,val_dataloader = train_val_data_process()# 训练模型train_process = train_model_process(model,train_dataloader,val_dataloader,num_epochs=20)# 绘制图像matplot_acc_loss(train_process)

训练时间也许要四五个小时。

模型训练完毕之后,就会在本地生成best_model.pth文件,这个文件存放着模型的最佳参数,用于下面的模型测试。

模型测试

创建一个model_test.py文件,用于模型的测试

import torch
import torch.utils.data as Data
from numpy.random import shuffle
from torchvision import transforms
from torchvision.datasets import FashionMNISTfrom model import VGG16# 加载要训练的数据
def test_data_process():# 加载测试集数据test_data = FashionMNIST(root="./data",# 指定数据集要下载的路径train=False,# 不要训练集数据# 数据归一化操作transform=transforms.Compose([transforms.Resize(size=224), # 将数据转成224*224大小transforms.ToTensor(),# 将数据转成Tensor格式]),download=True # 加载数据)# 通过DataLoader加载器 来加载数据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):# 指定设备device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 将模型放入设备中model.to(device)# 初始化模型训练的每轮参数test_correct = 0.0 # 准确度test_num = 0 # 测试样本数量# 只进行前向传播with torch.no_grad(): # 将梯度设置为0for test_data_x,test_data_y in test_dataloader: # 遍历每轮次# 由于上面设置批次为1,所以这里就不需要循环批次了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)# 模型预测的结果 将pre_lab 与 标签数据 进行比较# 如果预测正确,则加1test_correct += torch.sum(pre_lab==test_data_y.data)# 测试样本数量累加test_num += test_data_y.size(0)# 计算最终测试的准确率 每轮的准确度 / 总样本数量test_acc = test_correct.double().item() / test_numprint("测试模型的准确率为:",test_acc)if __name__ == '__main__':# 加载模型model = VGG16()# 模型具体的训练过程device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 加载训练好的模型最佳参数model.load_state_dict(torch.load('best_model.pth',map_location=device))# 加载测试的数据集test_dataloader = test_data_process()# 开始训练# test_model_process(model, test_dataloader)# 模型具体的训练过程# device = torch.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')# 梯度设置为0with 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])

如果用的是cpu,而不是gpu,那么可能是pytorch的环境版本问题。

也就是cuda的版本和pytorch不适合

为0
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])
> 如果用的是cpu,而不是gpu,那么可能是pytorch的环境版本问题。
>
> 也就是cuda的版本和pytorch不适合

相关文章:

  • 云原生后端架构的挑战与应对策略
  • mysql--索引
  • 多平台输入法+助聊APP开发技术指南:从概念到实现
  • 华为云Astro轻应用利用自定义连接器调用第三方接口实际操作
  • 传奇各职业/战士/法师/道士戒指爆率及出处产出地/圣战/法神/天尊/虹魔/魔血/麻痹/超负载/求婚/隐身/传送/复活/护身/祈祷/火焰
  • GAMES202-高质量实时渲染(Assignment 2)
  • 阿里云服务器 篇五(加更):短链服务网站:添加反垃圾邮件功能
  • Unity Text打字机效果,支持富文本
  • C++ 与 Lua 联合编程
  • [预备知识]6. 优化理论(二)
  • 如何配置NGINX作为反向代理服务器来缓存后端服务的响应?
  • 微信小程序 自定义组件 标签管理
  • [SoC]AXI总线Performance验证方案
  • 【AI面试准备】Git与CI/CD及单元测试实战指南
  • [Linux]从零开始的STM32MP157 Buildroot根文件系统构建
  • mindyolo填坑
  • 如何利用dify 生成Fine‑tune 需要的Alpaca 格式数据
  • 正则表达式与文本三剑客grep、sed、awk
  • linux指令中的竖线(“|”)是干啥的?【含实例展示】
  • 数据库系统概论|第五章:数据库完整性—课程笔记1
  • 新华社评论员:在推进中国式现代化的宽广舞台上绽放青春光彩
  • 阿根廷发生5.8级地震
  • 此前显示售罄的火车票“五一”前大量放出来了?12306回应
  • 来上海喝云南咖啡!上海国际咖啡文化节助力咖啡产业破圈出海
  • 国家卫健委对近日肖某引发舆情问题开展调查
  • 比黄油年糕热量还高,这个火爆全网的甜品劝你慎吃