第七章 Pytorch构建模型详解【构建CIFAR10模型结构】
跟着小土堆继续学,在上篇的学习中已经掌握了对Pytorch的基本使用 ,这篇构建模型练手
前置知识
container中Sequential的使用:
作用:将按各个模块按顺序搭建起来
CIFAR10网络模型结构
卷积输入输出计算公式
通过卷积的输出公式计算就可以根据输入和输出的图像形状反推出padding等值
模型
模型搭建
class CIFAR10_Net(nn.Module):def __init__(self):super(CIFAR10_Net, self).__init__()self.model = nn.Sequential( #Sequential将按各个模块按顺序搭建起来#卷积输入输出计算:32 = (32 + 2 * padding - 1 * (5-1) - 1) + 1得到padding为2【Hin = 32,stride = 1,dilation = 1,kernel_size = 5,Hout = 32】nn.Conv2d(in_channels=3, out_channels=32, kernel_size=5, stride=1, padding=2), #第一步是5*5的卷积【将(3*32*32)的图片转为(32*32*32)的数据】nn.MaxPool2d(kernel_size=2, stride=2), #第二步是2*2最大池化【将(32*32*32)的数据转为(32*16*16)的数据】#卷积输入输出计算:16 = (16 + 2 * padding - 1 * (5-1) - 1) + 1得到padding为2【Hin = 16,stride = 1,dilation = 1,kernel_size = 5,Hout = 16】nn.Conv2d(in_channels=32, out_channels=32, kernel_size=5, stride=1, padding=2), #第三步还是5*5的卷积【将(32*16*16)的数据转为(32*16*16)的数据】nn.MaxPool2d(kernel_size=2, stride=2), #第四步还是2*2最大池化【将(32*16*16)的数据转为(32*8*8)的数据】# 卷积输入输出计算:8 = (8 + 2 * padding - 1 * (5-1) - 1) + 1得到padding为2【Hin = 8,stride = 1,dilation = 1,kernel_size = 5,Hout = 8】nn.Conv2d(in_channels=32, out_channels=64, kernel_size=5, stride=1, padding=2), #第五步还是5*5的卷积【将(32*8*8)的数据转为(64*8*8)的数据】nn.MaxPool2d(kernel_size=2, stride=2), #第六步还是2*2最大池化【将(64*8*8)的数据转为(64*4*4)的数据】nn.Flatten(), #第七步展平数据【将(64*4*4)的数据转为64*4*4的数据】#全连接层#上面经过model已经得到数据,接下来要将数据转为输出结构nn.Linear(64*4*4,64), #第八步线性连接进入隐藏层【将64*4*4的数据输入计算出64个隐藏层结果】nn.Linear(64,10) #第九步线性连接得出预测【将64的数据输入计算出10个预测结果的分数】)def forward(self, x):x = self.model(x)x = self.fc1(x)x = self.fc2(x)return x
测试验证模型
#模型实例化
cifar10_net = CIFAR10_Net()
print(cifar10_net)#测试数据【ones代表全为1,random即随机】
input = torch.ones(64, 3, 32, 32)
output = cifar10_net(input)print(output.shape)
Tensorboard展示
writer = SummaryWriter("./logs_model")
writer.add_graph(cifar10_net, input)
writer.close()
Loss损失函数
求和平均L1Loss
#L1Loss
inputs = torch.tensor([1,2,3],dtype=torch.float32)
targets = torch.tensor([1,2,5],dtype=torch.float32)inputs = torch.reshape(inputs, (1,1,1,3))
targets = torch.reshape(targets, (1,1,1,3))loss = L1Loss() #默认为mean——求和平均,如果是reduction="sum"就是统计总数result = loss(inputs, targets)
print(result)
平方差MSELoss
#MSELoss
inputs = torch.tensor([1,2,3],dtype=torch.float32)
targets = torch.tensor([1,2,5],dtype=torch.float32)inputs = torch.reshape(inputs, (1,1,1,3))
targets = torch.reshape(targets, (1,1,1,3))loss = MSELoss() #默认为mean——求和平均,如果是reduction="sum"就是统计总数result = loss(inputs, targets)
print(result)
交叉熵CrossEntropyLoss
CrossEntropyLoss输入要求
#CrossEntropyLoss
inputs = torch.tensor([0.1,0.2,0.3],dtype=torch.float32)
targets = torch.tensor([1]) #标签inputs = torch.reshape(inputs, (1,3)) #CrossEntropyLoss要求的输入形状是(N,C) =》(batch_size,number of class)loss = CrossEntropyLoss()result = loss(inputs, targets)
print(result)
损失的梯度计算
梯度计算即是反向传播,通过利用backward函数进行计算,计算好的结果会存放到model中
给result_loss.backward()这一步打上断点,执行调试【右上角的虫子,在运行旁边】
执行result_loss.backward()这一步后,梯度结果就会存放在model中【我们输入的output数据类型是tensor,而tensor中会保存计算的前向路径,因此backward就可以自动追踪路径定位到相应model,往其中存放梯度grad】
优化器optimizer
通过损失函数我们已经计算出了模型中各参数的梯度grad,优化器optimizer的作用就是根据梯度进行参数更新
使用示例
【optimizer.step()的作用就是根据梯度gard更新参数,而optimizer.zero_grad()是清空model中的梯度,以防止上次的梯度影响这次优化器优化,接着重新利用loss.back()重新计算梯度,循环往复】
【1】梯度清零
【2】反向传播求梯度
【3】根据梯度,优化器更新参数
更新前
更新后
【4】循环往复(重复前面的步骤)
以上遍历完整个数据集(训练集)就相当于一轮学习,我们训练时往往要经过多轮学习,不断优化model参数
for epoch in range(20):running_loss = 0.0 #统计这一轮的总损失for i, (images, labels) in enumerate(train_loader): #训练optimizer.zero_grad() #梯度清空【清空上一轮计算出的梯度(将model中的grad都设为None)】outputs = cifar10_net(images) #模型输出结果【预测标签】result_loss = loss(outputs, labels) #损失函数计算result_loss.backward() #根据反向传播计算出model中各参数的梯度gradoptimizer.step() #优化器optimizer根据梯度更新model中的各参数running_loss += result_loss #统计这一轮的总损失print('epoch:{}, running_loss:{}'.format(epoch, running_loss)) #输出这一轮的总损失
网络模型读取和保存
模型保存
保存方式1:save(model,save_path)【完全保存】
保存网络模型的同时也保存相应的参数,“.pth”的后缀其实取什么都可以,但是默认是.pth
【注意:该方式加载预训练模型时要求文件中含有model的类(import导入或直接定义,一边都是import导入)】
#保存方式1
#保存模型
torch.save(cifar10_net, "cifar10_net.pth") #保存网络模型的同时也保存相应的参数,“.pth”的后缀其实取什么都可以,但是默认是.pth#加载模型
load_net = torch.load("cifar10_net.pth")
print(load_net)
保存方式2:save(model.state_dict,save_path)【只保存参数字典】
只保存模型相应的参数,不保存模型的网络结构(官方推荐,保存下来占用的空间小)
#保存方式2(官方推荐,保存下来占用的空间小)
#保存模型
torch.save(cifar10_net.state_dict(), "cifar10_dict.pth") #用字典保存模型相应的参数,不保存模型的网络结构#加载模型
load_net = CIFAR10_Net() #实例化模型
load_dict = torch.load("cifar10_dict.pth") #加载参数字典
load_net.load_state_dict(load_dict) #将参数字典加载到模型结构汇总
print(load_net)
现有网络模型(Pytorch提供)的使用
VGG的使用
VGG对应的数据集ImageNet
数据集下载(需在虚拟环境中提前安装scipy包)
Train_Dataset = torchvision.datasets.ImageNet(root='./ImageNet_data',split='train',transform=torchvision.transforms.ToTensor())Test_Dataset = torchvision.datasets.ImageNet(root='./ImageNet_data',split='val',transform=torchvision.transforms.ToTensor())train_loader = torch.utils.data.DataLoader(dataset=Train_Dataset,batch_size=64)
test_loader = torch.utils.data.DataLoader(dataset=Test_Dataset,batch_size=64)
上网下载【不做验证了,数据集太大了,百来个G】
加载预训练模型
VGG16_false = torchvision.models.vgg16(pretrained=False, #仅加载网络模型(随机初始化权重)progress=True)VGG16_true = torchvision.models.vgg16(pretrained=True, #加载网络模型并且初试参数设置为预定的权重(别人已经训练好了的参数)progress=True)
修改现有网络模型结构
方式1:修改原模型
#方式1:修改原模型
VGG16_false.classifier[6] = nn.Linear(4096, 10)print(VGG16_false)
方式2:追加线性层
使用add_module()函数
#追加线性层
VGG16_false.add_module("add_linear",nn.Linear(1000,10))print(VGG16_false)
加到classifier(Sequential构造的一连串模块)中
VGG16_false.classifier.add_module("add_linear",nn.Linear(1000,10))print(VGG16_false)
模型训练套路
建立model.py存放模型结构
import torch
from torch import nn#======================================================================模型搭建======================================================================class CIFAR10_Net(nn.Module):def __init__(self):super(CIFAR10_Net, self).__init__()self.model = nn.Sequential( #Sequential将按各个模块按顺序搭建起来#卷积输入输出计算:32 = (32 + 2 * padding - 1 * (5-1) - 1) + 1得到padding为2【Hin = 32,stride = 1,dilation = 1,kernel_size = 5,Hout = 32】nn.Conv2d(in_channels=3, out_channels=32, kernel_size=5, stride=1, padding=2), #第一步是5*5的卷积【将(3*32*32)的图片转为(32*32*32)的数据】nn.MaxPool2d(kernel_size=2, stride=2), #第二步是2*2最大池化【将(32*32*32)的数据转为(32*16*16)的数据】#卷积输入输出计算:16 = (16 + 2 * padding - 1 * (5-1) - 1) + 1得到padding为2【Hin = 16,stride = 1,dilation = 1,kernel_size = 5,Hout = 16】nn.Conv2d(in_channels=32, out_channels=32, kernel_size=5, stride=1, padding=2), #第三步还是5*5的卷积【将(32*16*16)的数据转为(32*16*16)的数据】nn.MaxPool2d(kernel_size=2, stride=2), #第四步还是2*2最大池化【将(32*16*16)的数据转为(32*8*8)的数据】# 卷积输入输出计算:8 = (8 + 2 * padding - 1 * (5-1) - 1) + 1得到padding为2【Hin = 8,stride = 1,dilation = 1,kernel_size = 5,Hout = 8】nn.Conv2d(in_channels=32, out_channels=64, kernel_size=5, stride=1, padding=2), #第五步还是5*5的卷积【将(32*8*8)的数据转为(64*8*8)的数据】nn.MaxPool2d(kernel_size=2, stride=2), #第六步还是2*2最大池化【将(64*8*8)的数据转为(64*4*4)的数据】nn.Flatten(), #第七步展平数据【将(64*4*4)的数据转为64*4*4的数据】#全连接层#上面经过model已经得到数据,接下来要将数据转为输出结构nn.Linear(64*4*4,64), #第八步线性连接进入隐藏层【将64*4*4的数据输入计算出64个隐藏层结果】nn.Linear(64,10) #第九步线性连接得出预测【将64的数据输入计算出10个预测结果的分数】)def forward(self, x):x = self.model(x)return x#======================================================================测试验证模型======================================================================if __name__ == '__main__': #main函数==》只在执行本文件时运行,当作为类使用时,这部分不会被引入【目的在于测试该文件,有更好的规范性】cifar10_net = CIFAR10_Net() #模型实例化print(cifar10_net)input = torch.ones(64, 3, 32, 32) #测试数据【ones代表全为1,random即随机】output = cifar10_net(input)print(output.shape)
cuda(GPU)加速模型训练
环境配置
检查cuda能否使用
print('Pytorch版本:', torch.__version__)
print('显卡是否可用:', '可用' if (torch.cuda.is_available()) else '不可用')
以下是有英伟达显卡但是cuda不能使用的情况:
【情况一:Pytorch版本不对(没下载到cuda版本的Pytorch)】
解决:重新安装Pytorch
(1)进入虚拟环境中卸载cpu版本的Pytorch
activate tudui
conda uninstall pytorch
(2)去Pytorch官方下载对应cuda版本的Pytorch
查看自己的cuda版本:
win+r输入cmd回车进入命令行,输入:
nvidia-smi
在Pytorch官网中找到对应版本的下载链接Get Started
下载
【情况二:Pytorch版本与cuda版本不匹配(某些项目可能会出现版本不匹配的问题)】
在确认自己的cuda版本后,在Pytorch官方找到对应版本的Pytorch下载链接,重复上面的操作
以前的 PyTorch 版本
以上成功解决后,我们就可以用cuda把我们训练放到GPU上,加速训练
使用to(device)函数加载
#定义训练设备
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") #cuda:0代表第一个GPU(如果你有多个GPU的话),如果没有GPU就用CPU
cifar10_net.to(device) #将模型加载到GPU上
训练train函数设计
导入模型以及各种包
from typing import OrderedDict
import torch
import torchvision
from torch import nn
from torch.nn import L1Loss, MSELoss, CrossEntropyLoss
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms#引入model
from model import *
数据加载
#======================================================================数据加载======================================================================train_dataset = torchvision.datasets.CIFAR10(root='./dataset', #训练集train=True,download=True,transform=transforms.ToTensor())test_dataset = torchvision.datasets.CIFAR10(root='./dataset', #测试集train=False,download=True,transform=transforms.ToTensor())#查询数据集长度
train_len = len(train_dataset)
test_len = len(test_dataset)print('Train dataset length:', train_len)
print('Test dataset length:', test_len)train_loader = torch.utils.data.DataLoader(train_dataset,64) #数据加载
test_loader = torch.utils.data.DataLoader(test_dataset, 64)
实例化模型
#======================================================================创建模型实例对象======================================================================#确认cuda可用
print('CUDA版本:', torch.version.cuda)
print('Pytorch版本:', torch.__version__)
print('显卡是否可用:', '可用' if (torch.cuda.is_available()) else '不可用')cifar10_net = CIFAR10_Net() #模型实例化
cifar10_net = cifar10_net.cuda() #放到cuda(GPU)上加载
实例化损失函数
#======================================================================Loss损失函数======================================================================loss = CrossEntropyLoss() #设置交叉熵损失
loss = loss.cuda() #放到cuda(GPU)上加载
实例化优化器
#======================================================================优化器optimizer======================================================================optimizer = torch.optim.SGD(cifar10_net.parameters(), lr=1e-2, momentum=0.9) #优化器设置(SGD是随机梯度下降,1e-2即是1*(10)**2 = 0.01)
模型训练以及测试与保存
模型测试中模型效果衡量标准为acc(分类任务中常用指标),即对比模型输出结果和标签查看是否一致,一致则说明我们的预测正确,对比完整个测试集后,acc = 预测正确数/测试总数
得到output后,我们使用argmax(dim = 1)进行分析结果,其中对output来说就相当于一个二维数组(在本案例中),而dim的作用是指定相应的维度,dim = 1即是第二维(第一维是dim = 0),拿其中一个输出i举例:
dim = 1相对应的就是第i个输出output[i][]的一行(设第一维为列,第二维为行),argmax的作用就是选出其中最大元素所在的下标(代表最大概率的分类),如果一个output为[0.1,0.2,0.4,0.3] —> argmax返回的就是下标3,此时查看target的标记是多少,如果target为3,即两者相等,预测正确,反之错误
#======================================================================训练过程======================================================================#记录训练的步数
train_step = 0
#记录测试的步数
test_step = 0
#训练轮数
epochs = 10
#加载到Tensorboard中
writer = SummaryWriter("./logs_model_loss")for epoch in range(epochs):for i, (images, labels) in enumerate(train_loader): #训练images = images.to('cuda') #放到cuda(GPU)上加载labels = labels.to('cuda') #放到cuda(GPU)上加载optimizer.zero_grad() #梯度清空【清空上一轮计算出的梯度(将model中的grad都设为None)】outputs = cifar10_net(images) #模型输出结果【预测标签】result_loss = loss(outputs, labels) #损失函数计算result_loss.backward() #根据反向传播计算出model中各参数的梯度gradoptimizer.step() #优化器optimizer根据梯度更新model中的各参数 #统计这一轮的总损失train_step += 1if train_step % 100 == 0: #每训练一百次打印一次结果print("Train_Step [{}/{}], Loss: {:.4f}".format(train_step, train_len,result_loss))writer.add_scalar('Loss/train', result_loss.item(), train_step) #将损失使用坐标轴显示出来#======================================================================测试过程======================================================================#每一轮epoch训练完后都经过测试with torch.no_grad(): #with torch.no_grad()———以下部分的梯度全部清空,保证模型不会因为梯度影响测试结果test_loss = 0 #测试的总损失test_accuracy = 0 #衡量指标:预测准确度accfor i, (images, labels) in enumerate(test_loader):images = images.to('cuda') #放到cuda(GPU)上加载labels = labels.to('cuda') #放到cuda(GPU)上加载outputs = cifar10_net(images) #模型的测试输出result_loss = loss(outputs, labels) #测试误差test_loss += result_loss #测试总误差统计test_step += 1test_accuracy += (outputs.argmax(dim=1) == labels).sum() #统计预测正确的个数writer.add_scalar('Loss/test', test_loss.item(), test_step) # 将损失使用坐标轴显示出来writer.add_scalar('Accuracy/test', test_accuracy/test_len, test_step)print('===========================epoch:{},acc = {},Test_loss = {}=============================='.format(epoch+1, test_accuracy/test_len, epochs,test_loss))#======================================================================模型保存======================================================================#每一轮epoch结束,保存一次模型# 保存方式2(官方推荐,保存下来占用的空间小)torch.save(cifar10_net.state_dict(), "cifar10_dict_epcoh{}.pth".format(epoch)) # 用字典保存模型相应的参数,不保存模型的网络结构print("model saved")
writer.close()
将我们的输出结果放到Tensorboard中
测试test函数设计
数据加载
#======================================================================数据加载======================================================================test_dataset = torchvision.datasets.CIFAR10(root='./dataset', #测试集train=False,download=True,transform=transforms.ToTensor())#查询数据集长度
test_len = len(test_dataset)
print('Test dataset length:', test_len)test_loader = torch.utils.data.DataLoader(test_dataset, 64) #数据加载
实例化模型并加载预训练参数
#======================================================================创建模型实例对象======================================================================#确认cuda可用
print('CUDA版本:', torch.version.cuda)
print('Pytorch版本:', torch.__version__)
print('显卡是否可用:', '可用' if (torch.cuda.is_available()) else '不可用')cifar10_net = CIFAR10_Net() #模型实例化
cifar10_net = cifar10_net.cuda() #放到cuda(GPU)上加载#加载模型
load_dict = torch.load("cifar10_dict_epcoh9.pth") #加载参数字典
cifar10_net.load_state_dict(load_dict) #将参数字典加载到模型结构汇总
print(cifar10_net) #打印网络结构
实例化损失函数
#======================================================================Loss损失函数======================================================================loss = CrossEntropyLoss() #设置交叉熵损失
测试过程
#======================================================================测试过程======================================================================test_loss = 0 #测试的总损失
test_accuracy = 0 #衡量指标:预测准确度acc
for i, (images, labels) in enumerate(test_loader):images = images.to('cuda') #放到cuda(GPU)上加载labels = labels.to('cuda') #放到cuda(GPU)上加载outputs = cifar10_net(images) #模型的测试输出result_loss = loss(outputs, labels) #测试误差test_loss += result_loss #测试总误差统计test_accuracy += (outputs.argmax(dim=1) == labels).sum() #统计预测正确的个数print('===========================acc = {},Test_loss = {}=============================='.format(test_accuracy/test_len, test_loss))
补充
下面两个函数其实要不要都不影响训练,但是针对特定层的处理往往要用到
train()函数
eval()函数
具体影响的层
Dropout 层
- 训练时:随机丢弃部分神经元(如 50%),防止过拟合。
- 评估时:关闭丢弃机制,保留所有神经元进行计算。
Batch Normalization (BN)
- 训练时:使用当前批次数据的均值和方差进行归一化,并更新全局统计量(running_mean/running_var)。
- 评估时:使用训练阶段积累的全局统计量,而非当前批次数据,确保结果稳定。
代码文件
model.py
import torch
from torch import nn#======================================================================模型搭建======================================================================class CIFAR10_Net(nn.Module):def __init__(self):super(CIFAR10_Net, self).__init__()self.model = nn.Sequential( #Sequential将按各个模块按顺序搭建起来#卷积输入输出计算:32 = (32 + 2 * padding - 1 * (5-1) - 1) + 1得到padding为2【Hin = 32,stride = 1,dilation = 1,kernel_size = 5,Hout = 32】nn.Conv2d(in_channels=3, out_channels=32, kernel_size=5, stride=1, padding=2), #第一步是5*5的卷积【将(3*32*32)的图片转为(32*32*32)的数据】nn.MaxPool2d(kernel_size=2, stride=2), #第二步是2*2最大池化【将(32*32*32)的数据转为(32*16*16)的数据】#卷积输入输出计算:16 = (16 + 2 * padding - 1 * (5-1) - 1) + 1得到padding为2【Hin = 16,stride = 1,dilation = 1,kernel_size = 5,Hout = 16】nn.Conv2d(in_channels=32, out_channels=32, kernel_size=5, stride=1, padding=2), #第三步还是5*5的卷积【将(32*16*16)的数据转为(32*16*16)的数据】nn.MaxPool2d(kernel_size=2, stride=2), #第四步还是2*2最大池化【将(32*16*16)的数据转为(32*8*8)的数据】# 卷积输入输出计算:8 = (8 + 2 * padding - 1 * (5-1) - 1) + 1得到padding为2【Hin = 8,stride = 1,dilation = 1,kernel_size = 5,Hout = 8】nn.Conv2d(in_channels=32, out_channels=64, kernel_size=5, stride=1, padding=2), #第五步还是5*5的卷积【将(32*8*8)的数据转为(64*8*8)的数据】nn.MaxPool2d(kernel_size=2, stride=2), #第六步还是2*2最大池化【将(64*8*8)的数据转为(64*4*4)的数据】nn.Flatten(), #第七步展平数据【将(64*4*4)的数据转为64*4*4的数据】#全连接层#上面经过model已经得到数据,接下来要将数据转为输出结构nn.Linear(64*4*4,64), #第八步线性连接进入隐藏层【将64*4*4的数据输入计算出64个隐藏层结果】nn.Linear(64,10) #第九步线性连接得出预测【将64的数据输入计算出10个预测结果的分数】)def forward(self, x):x = self.model(x)return x#======================================================================测试验证模型======================================================================if __name__ == '__main__': #main函数==》只在执行本文件时运行,当作为类使用时,这部分不会被引入【目的在于测试该文件,有更好的规范性】cifar10_net = CIFAR10_Net() #模型实例化print(cifar10_net)input = torch.ones(64, 3, 32, 32) #测试数据【ones代表全为1,random即随机】output = cifar10_net(input)print(output.shape)
train_model.py
from typing import OrderedDict
import torch
import torchvision
from torch import nn
from torch.nn import L1Loss, MSELoss, CrossEntropyLoss
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms#引入model
from model import *#======================================================================数据加载======================================================================train_dataset = torchvision.datasets.CIFAR10(root='./dataset', #训练集train=True,download=True,transform=transforms.ToTensor())test_dataset = torchvision.datasets.CIFAR10(root='./dataset', #测试集train=False,download=True,transform=transforms.ToTensor())#查询数据集长度
train_len = len(train_dataset)
test_len = len(test_dataset)print('Train dataset length:', train_len)
print('Test dataset length:', test_len)train_loader = torch.utils.data.DataLoader(train_dataset,64) #数据加载
test_loader = torch.utils.data.DataLoader(test_dataset, 64)#======================================================================创建模型实例对象======================================================================#确认cuda可用
print('CUDA版本:', torch.version.cuda)
print('Pytorch版本:', torch.__version__)
print('显卡是否可用:', '可用' if (torch.cuda.is_available()) else '不可用')cifar10_net = CIFAR10_Net() #模型实例化
cifar10_net = cifar10_net.cuda() #放到cuda(GPU)上加载#======================================================================Loss损失函数======================================================================loss = CrossEntropyLoss() #设置交叉熵损失#======================================================================优化器optimizer======================================================================optimizer = torch.optim.SGD(cifar10_net.parameters(), lr=1e-2, momentum=0.9) #优化器设置(SGD是随机梯度下降,1e-2即是1*(10)**2 = 0.01)#======================================================================训练过程======================================================================#记录训练的步数
train_step = 0
#记录测试的步数
test_step = 0
#训练轮数
epochs = 10
#加载到Tensorboard中
writer = SummaryWriter("./logs_model_loss")for epoch in range(epochs):for i, (images, labels) in enumerate(train_loader): #训练images = images.to('cuda') #放到cuda(GPU)上加载labels = labels.to('cuda') #放到cuda(GPU)上加载optimizer.zero_grad() #梯度清空【清空上一轮计算出的梯度(将model中的grad都设为None)】outputs = cifar10_net(images) #模型输出结果【预测标签】result_loss = loss(outputs, labels) #损失函数计算result_loss.backward() #根据反向传播计算出model中各参数的梯度gradoptimizer.step() #优化器optimizer根据梯度更新model中的各参数 #统计这一轮的总损失train_step += 1if train_step % 100 == 0: #每训练一百次打印一次结果print("Train_Step [{}/{}], Loss: {:.4f}".format(train_step, train_len,result_loss))writer.add_scalar('Loss/train', result_loss.item(), train_step) #将损失使用坐标轴显示出来#======================================================================测试过程======================================================================#每一轮epoch训练完后都经过测试with torch.no_grad(): #with torch.no_grad()———以下部分的梯度全部清空,保证模型不会因为梯度影响测试结果test_loss = 0 #测试的总损失test_accuracy = 0 #衡量指标:预测准确度accfor i, (images, labels) in enumerate(test_loader):images = images.to('cuda') #放到cuda(GPU)上加载labels = labels.to('cuda') #放到cuda(GPU)上加载outputs = cifar10_net(images) #模型的测试输出result_loss = loss(outputs, labels) #测试误差test_loss += result_loss #测试总误差统计test_step += 1test_accuracy += (outputs.argmax(dim=1) == labels).sum() #统计预测正确的个数writer.add_scalar('Loss/test', test_loss.item(), test_step) # 将损失使用坐标轴显示出来writer.add_scalar('Accuracy/test', test_accuracy/test_len, test_step)print('===========================epoch:{},acc = {},Test_loss = {}=============================='.format(epoch+1, test_accuracy/test_len, epochs,test_loss))#======================================================================模型保存======================================================================#每一轮epoch结束,保存一次模型# 保存方式2(官方推荐,保存下来占用的空间小)torch.save(cifar10_net.state_dict(), "cifar10_dict_epcoh{}.pth".format(epoch)) #用字典保存模型相应的参数,不保存模型的网络结构print("model saved")
writer.close()
test_model.py
from typing import OrderedDict
import torch
import torchvision
from torch import nn
from torch.nn import L1Loss, MSELoss, CrossEntropyLoss
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms#引入model
from model import *#======================================================================数据加载======================================================================test_dataset = torchvision.datasets.CIFAR10(root='./dataset', #测试集train=False,download=True,transform=transforms.ToTensor())#查询数据集长度
test_len = len(test_dataset)
print('Test dataset length:', test_len)test_loader = torch.utils.data.DataLoader(test_dataset, 64) #数据加载#======================================================================创建模型实例对象======================================================================#确认cuda可用
print('CUDA版本:', torch.version.cuda)
print('Pytorch版本:', torch.__version__)
print('显卡是否可用:', '可用' if (torch.cuda.is_available()) else '不可用')cifar10_net = CIFAR10_Net() #模型实例化#加载模型
load_dict = torch.load("cifar10_dict_epcoh9.pth") #加载参数字典
cifar10_net.load_state_dict(load_dict) #将参数字典加载到模型结构汇总
print(cifar10_net) #打印网络结构#定义训练设备
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") #cuda:0代表第一个GPU(如果你有多个GPU的话),如果没有GPU就用CPU
cifar10_net.to(device) #将模型加载到GPU上#======================================================================Loss损失函数======================================================================loss = CrossEntropyLoss() #设置交叉熵损失
loss.to(device) #放到cuda(GPU)上加载#======================================================================测试过程======================================================================test_loss = 0 #测试的总损失
test_accuracy = 0 #衡量指标:预测准确度acc
for i, (images, labels) in enumerate(test_loader):images = images.to(device) #放到cuda(GPU)上加载labels = labels.to(device) #放到cuda(GPU)上加载outputs = cifar10_net(images) #模型的测试输出result_loss = loss(outputs, labels) #测试误差test_loss += result_loss #测试总误差统计test_accuracy += (outputs.argmax(dim=1) == labels).sum() #统计预测正确的个数print('===========================acc = {},Test_loss = {}=============================='.format(test_accuracy/test_len, test_loss))