第七章 Pytorch构建模型详解【构建CIFAR10模型结构】

跟着小土堆继续学,在上篇的学习中已经掌握了对Pytorch的基本使用 ,这篇构建模型练手

前置知识

container中Sequential的使用:

作用:将按各个模块按顺序搭建起来

CIFAR10网络模型结构

卷积输入输出计算公式

通过卷积的输出公式计算就可以根据输入和输出的图像形状反推出padding等值

模型

模型搭建

python 复制代码
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

测试验证模型

python 复制代码
#模型实例化
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展示

python 复制代码
writer = SummaryWriter("./logs_model")
writer.add_graph(cifar10_net, input)
writer.close()

Loss损失函数

求和平均L1Loss

python 复制代码
#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

python 复制代码
#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输入要求

python 复制代码
#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参数

python 复制代码
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中各参数的梯度grad
        optimizer.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导入)】

python 复制代码
#保存方式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)【只保存参数字典】

只保存模型相应的参数,不保存模型的网络结构(官方推荐,保存下来占用的空间小)

python 复制代码
#保存方式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包)

python 复制代码
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】

加载预训练模型

python 复制代码
VGG16_false = torchvision.models.vgg16(pretrained=False,            #仅加载网络模型(随机初始化权重)
                               progress=True)

VGG16_true = torchvision.models.vgg16(pretrained=True,              #加载网络模型并且初试参数设置为预定的权重(别人已经训练好了的参数)
                               progress=True)

修改现有网络模型结构

方式1:修改原模型

python 复制代码
#方式1:修改原模型
VGG16_false.classifier[6] = nn.Linear(4096, 10)

print(VGG16_false)

方式2:追加线性层

使用add_module()函数

python 复制代码
#追加线性层
VGG16_false.add_module("add_linear",nn.Linear(1000,10))

print(VGG16_false)

加到classifier(Sequential构造的一连串模块)中

python 复制代码
VGG16_false.classifier.add_module("add_linear",nn.Linear(1000,10))

print(VGG16_false)

模型训练套路

建立model.py存放模型结构

python 复制代码
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能否使用

python 复制代码
print('Pytorch版本:', torch.__version__)
print('显卡是否可用:', '可用' if (torch.cuda.is_available()) else '不可用')

以下是有英伟达显卡但是cuda不能使用的情况:

【情况一:Pytorch版本不对(没下载到cuda版本的Pytorch)】

解决:重新安装Pytorch

(1)进入虚拟环境中卸载cpu版本的Pytorch

bash 复制代码
activate tudui
conda uninstall pytorch

(2)去Pytorch官方下载对应cuda版本的Pytorch

查看自己的cuda版本:

win+r输入cmd回车进入命令行,输入:

bash 复制代码
nvidia-smi

在Pytorch官网中找到对应版本的下载链接Get Started

下载

【情况二:Pytorch版本与cuda版本不匹配(某些项目可能会出现版本不匹配的问题)】

在确认自己的cuda版本后,在Pytorch官方找到对应版本的Pytorch下载链接,重复上面的操作

以前的 PyTorch 版本

以上成功解决后,我们就可以用cuda把我们训练放到GPU上,加速训练

使用to(device)函数加载

python 复制代码
#定义训练设备
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")                 #cuda:0代表第一个GPU(如果你有多个GPU的话),如果没有GPU就用CPU
cifar10_net.to(device)                                                                  #将模型加载到GPU上

训练train函数设计

导入模型以及各种包

python 复制代码
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 *

数据加载

python 复制代码
#======================================================================数据加载======================================================================

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)

实例化模型

python 复制代码
#======================================================================创建模型实例对象======================================================================

#确认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)上加载

实例化损失函数

python 复制代码
#======================================================================Loss损失函数======================================================================

loss = CrossEntropyLoss()                                                               #设置交叉熵损失
loss = loss.cuda()                                                                      #放到cuda(GPU)上加载

实例化优化器

python 复制代码
#======================================================================优化器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,即两者相等,预测正确,反之错误

python 复制代码
#======================================================================训练过程======================================================================

#记录训练的步数
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中各参数的梯度grad
        optimizer.step()                                                                    #优化器optimizer根据梯度更新model中的各参数                                                       #统计这一轮的总损失
        train_step += 1
        if 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                                                                   #衡量指标:预测准确度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_step += 1
            test_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函数设计

数据加载

python 复制代码
#======================================================================数据加载======================================================================

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)                        #数据加载

实例化模型并加载预训练参数

python 复制代码
#======================================================================创建模型实例对象======================================================================

#确认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)                                                                    #打印网络结构

实例化损失函数

python 复制代码
#======================================================================Loss损失函数======================================================================

loss = CrossEntropyLoss()                                                               #设置交叉熵损失

测试过程

python 复制代码
#======================================================================测试过程======================================================================

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

python 复制代码
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

python 复制代码
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中各参数的梯度grad
        optimizer.step()                                                                    #优化器optimizer根据梯度更新model中的各参数                                                       #统计这一轮的总损失
        train_step += 1
        if 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                                                                   #衡量指标:预测准确度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_step += 1
            test_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

python 复制代码
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))
相关推荐
UQI-LIUWJ1 小时前
论文笔记:Tuning Language Models by Proxy
论文阅读·人工智能·语言模型
awonw1 小时前
[python][基础]Flask 技术栈
开发语言·python·flask
大魔王(已黑化)2 小时前
OpenCV —— 绘制图形
人工智能·opencv·计算机视觉
bright_colo2 小时前
Python-初学openCV——图像预处理(四)——滤波器
python·opencv·计算机视觉
Nandeska2 小时前
一、Python环境、Jupyter与Pycharm
python·jupyter·pycharm
开开心心_Every2 小时前
多线程语音识别工具
javascript·人工智能·ocr·excel·语音识别·symfony
机器之心2 小时前
扣子开源全家桶,Apache 2.0加持,AI Agent又一次卷到起飞
人工智能
草堂春睡足2 小时前
【Datawhale AI夏令营】科大讯飞AI大赛(大模型技术)/夏令营:让AI理解列车排期表
人工智能·笔记
余俊晖2 小时前
GRPO强化学习缓解多模态大模型OCR任务的幻觉思路及数据生成思路
人工智能
sssammmm2 小时前
AI入门学习-模型评估示例讲解
人工智能·学习