pytorch笔记

· · 个人记录

前言

简介: PyTorch是一个基于Python的科学计算库,它主要提供了两个高级功能:一是支持张量计算,类似于NumPy,但是可以在GPU上运行;二是支持构建和训练深度神经网络。

在PyTorch中,张量(Tensor)是最基本的数据结构,类似于NumPy中的多维数组,但是可以在GPU上进行高效的计算。PyTorch支持多种张量操作,包括张量加法、减法、乘法、除法、矩阵乘法等,同时还支持各种元素级函数和聚合函数。

PyTorch还提供了一种灵活的、动态的计算图机制,称为Autograd。在PyTorch中,每个张量都可以被看作是计算图中的一个节点,每个节点都有一个对应的梯度节点,用于计算该节点对应的变量的梯度。当我们对计算图中的某个节点进行操作时,PyTorch会自动构建该节点的梯度计算图,并通过反向传播算法计算出该节点对应的变量的梯度。

PyTorch还提供了一种方便的、高层次的API,用于构建和训练深度神经网络。这个API称为torch.nn模块,它提供了各种层和损失函数的实现,可以方便地构建各种类型的深度神经网络,例如卷积神经网络、循环神经网络、自编码器等。同时,PyTorch还提供了torch.optim模块,用于实现各种优化算法,例如SGD、Adam等,以便训练深度神经网络。

总之,PyTorch是一个功能强大、易于使用的深度学习框架,适用于各种类型的深度学习任务,包括图像处理、自然语言处理、语音识别等。同时,PyTorch还具有优秀的可扩展性和灵活性,可以方便地与其他Python库和工具集成使用。

视频链接: PyTorch深度学习快速入门教程(绝对通俗易懂!)【小土堆】

1 Dataset类

from torch.utils.data import Dataset
from PIL import Image
import os

class MyData(Dataset):
    def __init__(self, root_dir, label_dir):
        self.root_dir = root_dir
        self.label_dir = label_dir
        # os.path.join():将两个路径用“\\”连接
        self.path = os.path.join(self.root_dir, self.label_dir)
        # 返回指定的文件夹包含的文件或文件夹的名字的列表
        self.img_path = os.listdir(self.path)

    # 对象索引函数
    def __getitem__(self, idx):
        img_name = self.img_path[idx]
        img_item_path = os.path.join(self.root_dir, self.label_dir, img_name)
        img = Image.open(img_item_path)
        label = self.label_dir
        return img, label

    # 返回图片数量
    def __len__(self):
        return len(self.img_path)

train_root_dir = "data/images/dataset/train"
ant_label_dir = "ants_image"
bee_label_dir = "bees_image"
ant_dataset = MyData(train_root_dir, ant_label_dir)
bee_dataset = MyData(train_root_dir, bee_label_dir)

# __getitem__() 调用方法
ant_img, ant_label = ant_dataset[0]
print(ant_dataset[0])

train_dataset = ant_dataset + bee_dataset  # 按加的顺序组合
train_img, train_label = train_dataset[124]
print(len(ant_dataset), len(bee_dataset), sep="\n")  # 124 121
train_img.show()

2 TensorBoard

from torch.utils.tensorboard import SummaryWriter

writer = SummaryWriter("logs")

# writer.add_image()

for i in range(100):
    # tag:标题;scalar_value:y轴;global_step:x轴
    writer.add_scalar(tag="y = x", scalar_value=i * i, global_step=i)

writer.close()

运行上面代码之后,在下方终端输入:

tensorboard --logdir=logs

之后打开网页:

 http://localhost:6006/ 

端口也可以自己改(6007可以随便改):

tensorboard --logdir=logs --port=6007

3 Transforms

from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms

writer = SummaryWriter("logs")
img = Image.open("data/images/SSSS.Dynazenon.jpg")

'''
toTensor:
Convert a ``PIL Image`` or ``numpy.ndarray`` to tensor
PIL Image 和 numpy.ndarray 分别对应PIL和opencv的图片读取
'''
trans_toTensor = transforms.ToTensor()
img_tensor = trans_toTensor(img)
writer.add_image("ToTensor", img_tensor)

'''
normalize:归一化
计算方法:output[channel] = (input[channel] - mean[channel]) / std[channel]
防止数据集中有过大或者过小的值,影响训练
'''
trans_norm = transforms.Normalize([5, 0.5, 0.5], [0.5, 0.5, 0.5])  # 三个通道的均值和标准差
img_norm = trans_norm(img_tensor)
writer.add_image("norm img", img_norm)

'''
resize:
Resize the input image to the given size.
resize不会改变图片的数据类型
'''
trans_resize = transforms.Resize((512, 512))
img_resize = trans_resize(img_tensor)
writer.add_image("resize img", img_resize)

'''
compose:
compose就是一个指定一个transform操作序列,定义了一条加工流水线
'''
trans_resize_2 = transforms.Resize(512)  # 将短边缩到 512
# 先 toTensor 再 resize,前一个的输出对应后一个输入
trans_compose = transforms.Compose([trans_toTensor, trans_resize_2])
img_resize_2 = trans_compose(img)
writer.add_image("resize_2 img", img_resize_2)

'''
random crop:
随机剪一个指定尺寸的新图片
'''
trans_random = transforms.RandomCrop(500)  # 指定裁剪尺寸
trans_compose_2 = transforms.Compose([trans_random, trans_toTensor])
for i in range(10):
    img_crop = trans_compose_2(img)
    writer.add_image("RandomCrop", img_crop, i)

writer.close()

4 运用Datasets以及Transform

import torchvision
from torch.utils.tensorboard import SummaryWriter

# transforms操作序列
dataset_transform = torchvision.transforms.Compose([
    torchvision.transforms.ToTensor()
])

# 训练集测试集初始化
train_set = torchvision.datasets.CIFAR10(root="./dataset", train=True, transform=dataset_transform, download=True)
test_set = torchvision.datasets.CIFAR10(root="./dataset", train=False, transform=dataset_transform, download=True)

# 将测试集前 10 张在 TensorBoard 显示
writer = SummaryWriter("logs2")
for i in range(10):
    img, target = test_set[i]
    writer.add_image("test_set", img, i)

writer.close()

5 DataLoader

import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

test = torchvision.datasets.CIFAR10("./dataset", train=False, transform=torchvision.transforms.ToTensor())

'''
batch size:loader能每次装弹4枚进入枪膛,或者理解每次抓四张牌
shuffle:每次epoch是否打乱原来的顺序,就像打完一轮牌后,洗不洗牌
num_workers:进程数
drop last:最后的打他不够一个 batch 还要不要了
'''
test_loader = DataLoader(dataset=test, batch_size=4, shuffle=True, num_workers=0, drop_last=False)

# 使用 board 可视化
writer = SummaryWriter("logs_dataloader")
step = 0
for data in test_loader:
    imgs, targets = data

    # 这里如果你用add image会报错,因为这个方法只能一次写一个图片,你必须换add images方法来写入带有批处理的图片
    # writer.add_image("test set loader", imgs, step)
    writer.add_images("test set loader", imgs, step)
    step += 1

writer.close()

6 nn_module

import torch
import torch.nn as nn

# 神经网络类的构造
class Module(nn.Module):
    def __init__(self):
        super().__init__()

    # nn.Module 中的 call 方法会调用 forward
    def forward(self, input):
        output = input + 1
        return output

mdl = Module()
x = torch.tensor(1.0)
output = mdl(x)
print(output)

7 nn_conv(卷积)

import torch
import torch.nn.functional as F

input = torch.tensor([[1, 2, 0, 3, 1],
                      [0, 1, 2, 3, 1],
                      [1, 2, 1, 0, 0],
                      [5, 2, 3, 1, 1],
                      [2, 1, 0, 1, 1]])

kernel = torch.tensor([[1, 2, 1],
                       [0, 1, 0],
                       [2, 1, 0]])

# conv2d需要输入的tensor是四维的(batch, c,h,w),但是现在的input kernel是二维
# (要变换的矩阵, (批次大小(batch size), 通道数(channel), 矩阵高度, 矩阵宽度))
input = torch.reshape(input, (1, 1, 5, 5))
kernel = torch.reshape(kernel, (1, 1, 3, 3))

# stride:步长,padding:如果步长是 1,又想保持输入输出高宽不变,就把 padding 设置 1
output1 = F.conv2d(input, kernel, stride=1)
print(output1)

# padding=1 会在输入矩阵外圈填充一圈 0,在卷积时,如果 stride=1,输入输出尺寸会不变
output2 = F.conv2d(input, kernel, stride=1, padding=1)
print(output2)

8 nn_conv2d(卷积)

import torch
import torchvision
from torch import nn
from torch.nn import Conv2d, MaxPool2d, ReLU, Sigmoid, Linear, Flatten, Sequential
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

dataset = torchvision.datasets.CIFAR10("./dataset", False, torchvision.transforms.ToTensor())
dataloader = DataLoader(dataset, batch_size=64)

class Module(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = Conv2d(in_channels=3, out_channels=6,
                            kernel_size=(3, 3), stride=(1, 1), padding=0)

    def forward(self, x):
        x = self.conv1(x)
        return x

mdl = Module()
print(mdl)

writer = SummaryWriter("logs_nn_conv2d")

step = 0
for data in dataloader:
    img, target = data
    output = mdl(img)
    # output 的 size 是 torch.Size([64, 6, 30, 30])
    writer.add_images("before conv2d", img, step)
    # output 是 6 通道,board不知道该怎么写入图片了,所以要 reshape
    output = torch.reshape(output, (-1, 3, 30, 30))  # batch size不知道可以填 -1
    writer.add_images("after conv2d", output, step)

    step += 1

writer.close()

9 nn_maxpool(最大池化)

import torch
import torchvision
from torch import nn
from torch.nn import MaxPool2d
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

# dtype:设置矩阵元素的数据类型
input = torch.tensor([[1, 2, 0, 3, 1],
                      [0, 1, 2, 3, 1],
                      [1, 2, 1, 0, 0],
                      [5, 2, 3, 1, 1],
                      [2, 1, 0, 1, 1]], dtype=torch.float)

input = torch.reshape(input, (-1, 1, 5, 5))

dataset = torchvision.datasets.CIFAR10("./dataset", train=False,
                                       transform=torchvision.transforms.ToTensor())
dataloader = DataLoader(dataset=dataset, batch_size=64)

# ceil mode:池化核走出input时还要不要里边的最大值 默认不要
class Module(nn.Module):
    def __init__(self):
        super(Module, self).__init__()
        self.max_pool1 = MaxPool2d(kernel_size=(3, 3), ceil_mode=True)

    def forward(self, input):
        output = self.max_pool1(input)
        return output

mdl = Module()
output = mdl(input)
print(output)

writer = SummaryWriter("logs_maxpool")

step = 0
for data in dataloader:
    img, target = data
    writer.add_images("input", img, step)
    output = mdl(img)
    writer.add_images("output", output, step)

    step += 1

writer.close()

10 nn_relu(非线性激活)

import torch
import torchvision
from torch import nn
from torch.nn import ReLU, Sigmoid
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

input = torch.tensor([[1, -0.5],
                     [-1, 3]])

input = torch.reshape(input, (-1, 1, 2, 2))

dataset = torchvision.datasets.CIFAR10("./dataset", False, torchvision.transforms.ToTensor())
dataloader = DataLoader(dataset, 64)

class Module(nn.Module):
    def __init__(self):
        super(Module, self).__init__()
        # inplace=True:ReLU会返回一个output,input的值不变,可以保留原始数据,一般情况inplace=False
        self.relu1 = ReLU(inplace=False)  # 默认为 False
        self.sigmoid = Sigmoid()

    def forward(self, input):
        output = self.sigmoid(input)
        return output

mdl = Module()

writer = SummaryWriter("logs_relu")
step = 0

for data in dataloader:
    img, target = data
    writer.add_images("input", img, step)
    output = mdl(img)
    writer.add_images("output", img, step)

    step += 1

writer.close()

11 nn_linear(线性层)

把图像每行首尾相接,并成一行

import torch
import torchvision
from torch import nn
from torch.nn import Linear
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

dataset = torchvision.datasets.CIFAR10("./dataset", False, torchvision.transforms.ToTensor())
dataloader = DataLoader(dataset, 64)

class Module(nn.Module):
    def __init__(self):
        super(Module, self).__init__()
        self.linear1 = Linear(196608, 10)

    def forward(self, input):
        output = self.linear1(input)
        return output

mdl = Module()

for data in dataloader:
    img, target = data
    print(img.shape)
    output = torch.reshape(img, (1, 1, 1, -1))
    print(output.shape)
    output = mdl(output)
    print(output.shape)

或者(常用):

import torch
import torchvision
from torch import nn
from torch.nn import Linear
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

dataset = torchvision.datasets.CIFAR10("./dataset", False, torchvision.transforms.ToTensor())
dataloader = DataLoader(dataset, 64)

for data in dataloader:
    img, target = data
    print(img.shape)
    output = torch.flatten(img)
    print(output.shape)

12 nn_seq(Sequential)

torch.nn.Sequential 是 PyTorch 中的一个模型容器,它允许用户按照顺序组合多个神经网络层以构建神经网络模型。通过 Sequential,可以方便地将多个层组合成一个整体,并将其作为一个单独的层或模块来使用。

import torch
import torchvision
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Sequential, Linear
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

dataset = torchvision.datasets.CIFAR10("./dataset", False, torchvision.transforms.ToTensor())
dataloader = DataLoader(dataset, 64)

class Module(nn.Module):
    def __init__(self):
        super(Module, self).__init__()
        self.seq = Sequential(Conv2d(3, 32, kernel_size=(5, 5), padding=2),
                              MaxPool2d(kernel_size=2),
                              Conv2d(32, 32, kernel_size=(5, 5), padding=2),
                              MaxPool2d(kernel_size=2),
                              Conv2d(32, 64, kernel_size=(5, 5), padding=2),
                              MaxPool2d(kernel_size=2),
                              Flatten(),
                              Linear(1024, 64),
                              Linear(64, 10)
                              )

    def forward(self, x):
        x = self.seq(x)
        return x

mdl = Module()
print(mdl)
# 用 1 填充矩阵;(批次大小(batch size), 通道数(channel), 矩阵高度, 矩阵宽度)
input = torch.ones((64, 3, 32, 32))
output = mdl(input)
print(output.shape)

13 nn_loss(损失函数)

import torch
from torch.nn import L1Loss, MSELoss, CrossEntropyLoss

inputs = torch.tensor([1, 2, 3], dtype=torch.float32)
outputs = torch.tensor([1, 2, 5], dtype=torch.float32)

inputs = torch.reshape(inputs, (1, 1, 1, 3))
outputs = torch.reshape(outputs, (1, 1, 1, 3))

loss = L1Loss(reduction="sum")
result = loss(inputs, outputs)

loss_mse = MSELoss()
result_mse = loss_mse(inputs, outputs)

print(result)
print(result_mse)

x = torch.tensor([0.1, 0.2, 0.3])
y = torch.tensor([1])
x = torch.reshape(x, (1, 3))
loss_cross = CrossEntropyLoss()
result_cross = loss_cross(x, y)
print(result_cross)

14 nn_optim(优化器)

import torch
import torchvision
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.tensorboard import SummaryWriter
from torch.optim.lr_scheduler import StepLR
from torch.utils.data import DataLoader

"""
demo1:在模型训练中加入损失函数和优化器
"""
dataset = torchvision.datasets.CIFAR10("./dataset", train=False, transform=torchvision.transforms.ToTensor(),
                                       download=True)
dataloader = DataLoader(dataset, batch_size=1)

class Module(nn.Module):
    def __init__(self):
        super(Module, self).__init__()
        self.model1 = Sequential(
            Conv2d(3, 32, 5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 32, 5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 64, 5, padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024, 64),
            Linear(64, 10)
        )

    def forward(self, x):
        x = self.model1(x)
        return x

mld = Module()
# 损失函数 优化器
loss = nn.CrossEntropyLoss()
# 调用 mld.parameters() 可以获取到 Module 中的所有可训练参数;lr是训练速率
optim = torch.optim.SGD(mld.parameters(), lr=0.01)

for i in range(20):
    running_loss = 0.0
    for data in dataloader:
        imgs, targets = data
        outputs = mld(imgs)
        result_loss = loss(outputs, targets)
        # 注意 清零--》反向传播算梯度--》更新参数
        optim.zero_grad()
        result_loss.backward()
        optim.step()
        running_loss = running_loss + result_loss
    print(running_loss)

# running_loss:
# tensor(18726.5977, grad_fn=<AddBackward0>)
# tensor(16132.8926, grad_fn=<AddBackward0>)
# tensor(15426.6357, grad_fn=<AddBackward0>)

15 model_pretrained(现有网络模型使用与修改)

import torch
import torchvision
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential

"""
demo1:加载vgg训练好的模型,并在里边加入一个线性层
"""
# ImageNet数据集太大了,100多G,还是用CIFAR10吧
# train_data = torchvision.datasets.ImageNet("../data_image_net", split='train', download=True,
#                                         transform=torchvision.transforms.ToTensor())
train_data = torchvision.datasets.CIFAR10('../data', train=True, transform=torchvision.transforms.ToTensor(),
                                          download=True)
# 加载现有的vgg模型
vgg16_not_pretrain = torchvision.models.vgg16(pretrained=False)
vgg16_pretrained = torchvision.models.vgg16(pretrained=True)

# 修改方法1:加入一个线性层,编号7
vgg16_pretrained.add_module("7", nn.Linear(1000, 10))
print(vgg16_pretrained)

# 修改方法2:修改原来的第六个线性层
vgg16_not_pretrain.classifier[6] = nn.Linear(4096, 10)
print(vgg16_not_pretrain)

16 model_save(模型的保存与加载)

import torch
import torchvision
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential

train_data = torchvision.datasets.CIFAR10('../data', train=True, transform=torchvision.transforms.ToTensor(),
                                          download=True)
# 加载现有的vgg模型
vgg16_not_pretrain = torchvision.models.vgg16(pretrained=False)
vgg16_pretrained = torchvision.models.vgg16(pretrained=True)

"""
demo2:保存/加载模型
两种保存方法 对应两种加载方法
保存模型都是用torch.save,加载模型都是用torch.load,一起保存的时候save整个模型,加载时直接torch.load加载
保存时只保存参数的,需要先向model vgg加载结构,再用model vgg.load state dict加载参数,加载参数还是要torc.load方法
保存方法1的‘陷阱’:
在使用方法1保存现有模型时,不会出错,代码更少,但是使用方法1保存自己的模型时,必须要引入这个模型的定义才可以
"""
# 保存东西需要现有东西保存
vgg16 = torchvision.models.vgg16(pretrained=False)
# 保存方式1:模型结构+参数一起保存
torch.save(vgg16, "vgg16_pretrained_save_method1.pth")
# 多保存一个vgg16_pretrained,后面 完整模型测试讨论会用到
torch.save(vgg16_pretrained, "vgg16_pretrained_save_method1.pth")
# 加载方式1
model1 = torch.load("vgg16_save_method1.pth")

# 保存方式2:只保存模型参数(推荐)
torch.save(vgg16.state_dict(), "vgg16_save_method2.pth")
# 加载方式2
# 先加载模型结构
model_vgg = torchvision.models.vgg16(pretrained=False)
# 再加载模型参数
model_vgg.load_state_dict(torch.load("vgg16_save_method2.pth"))

# 保存方法1的‘陷阱’
"""先保存tudui模型
class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()
        self.model1 = Sequential(
            Conv2d(3, 32, 5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 32, 5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 64, 5, padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024, 64),
            Linear(64, 10)
        )

    def forward(self, x):
        x = self.model1(x)
        return x

tudui = Tudui()
torch.save(tudui, "tudui_save_method1.pth")
"""

"""
直接加载tudui模型会报错
tudui = torch.load("tudui_save_method1.pth")
报错:
AttributeError: Can't get attribute 'Tudui' ...>
"""

17 完整训练流程

import torch
from torch import nn
from torch.nn import Sequential, Conv2d, ReLU, MaxPool2d, Flatten, Linear

# 创建网络模型
class Module(nn.Module):
    def __init__(self):
        super(Module, self).__init__()
        self.model1 = Sequential(
            Conv2d(3, 32, (5, 5), (1, 1), 2),
            ReLU(),
            MaxPool2d(2),
            Conv2d(32, 32, (5, 5), (1, 1), 2),
            ReLU(),
            MaxPool2d(2),
            Conv2d(32, 64, (5, 5), (1, 1), 2),
            ReLU(),
            MaxPool2d(2),
            Flatten(),
            Linear(1024, 64),
            Linear(64, 10)
        )

    def forward(self, x):
        x = self.model1(x)
        return x

if __name__ == '__main__':
    mdl = Module()
    inputs = torch.ones((64, 3, 32, 32))
    outputs = mdl(inputs)
    print(outputs.shape)
import os
import torch
import torchvision
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

from model import Module

# 定义训练的设备
# cuda:0 是独显,因为集显没有cuda
# cuda:0 可以替换成 cuda,对于单显卡的 pc 没有区别
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

train_data = torchvision.datasets.CIFAR10("./dataset", train=True, transform=torchvision.transforms.ToTensor(),
                                          download=True)
test_data = torchvision.datasets.CIFAR10("./dataset", train=False, transform=torchvision.transforms.ToTensor(),
                                         download=True)

train_data_size = len(train_data)
test_data_size = len(test_data)

print(f"训练数据集长度:{train_data_size}")
print(f"测试数据集长度:{test_data_size}")

# 使用dataloader加载数据
train_dataloader = DataLoader(train_data, batch_size=64)
test_dataloader = DataLoader(test_data, batch_size=64)

# 创建模型
mdl = Module()
mdl = mdl.to(device)

# 损失函数
loss_fn = nn.CrossEntropyLoss()
loss_fn = loss_fn.to(device)

# 优化器
learning_rate = 0.01
optimizer = torch.optim.SGD(mdl.parameters(), lr=learning_rate)

# 设置控制训练次数的参数
# 记录训练 测试次数
total_train_step = 0
total_test_step = 0
# 训练轮数
epoch = 20
# 记录最佳准确率
best_accuracy = 0
# 写入board
writer = SummaryWriter("logs_train")

for i in range(epoch):
    print(f"-------第 {i + 1} 轮训练开始-------")

    # 训练步骤开始
    mdl.train()
    for data in train_dataloader:
        imgs, targets = data
        imgs = imgs.to(device)
        targets = targets.to(device)

        outputs = mdl(imgs)
        loss = loss_fn(outputs, targets)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        total_train_step += 1
        writer.add_scalar("train loss", loss.item(), total_train_step)

        if total_train_step % 100 == 0:
            print("训练次数:{}, loss:{}".format(total_train_step, loss.item()))

    # 测试步骤开始
    mdl.eval()
    total_test_loss = 0
    total_accuracy = 0
    with torch.no_grad():
        for data in test_dataloader:
            imgs, targets = data
            imgs = imgs.to(device)
            targets = targets.to(device)
            outputs = mdl(imgs)
            loss = loss_fn(outputs, targets)
            total_test_loss += loss
            accuracy = (outputs.argmax(1) == targets).sum()
            total_accuracy += accuracy

    print("整体测试集上的Loss: {}".format(total_test_loss))
    print("整体测试集上的正确率: {}".format(total_accuracy / test_data_size))
    writer.add_scalar("test loss", total_test_loss, total_test_step)

    now_accuracy = total_accuracy / test_data_size
    writer.add_scalar("test accuracy", total_accuracy / test_data_size, total_test_step)
    total_test_step += 1

    model_folder = "./model"
    if not os.path.exists(model_folder):
        os.makedirs(model_folder)

    # 保存准确率最高的模型
    if now_accuracy > best_accuracy:
        best_accuracy = now_accuracy
        torch.save(mdl.state_dict(), f"./model/best.pth")
    # 保存最新的模型
    if i == epoch - 1:
        torch.save(mdl.state_dict(), f"./model/last.pth")

writer.close()

18 完整推理流程

import torch
import torchvision
from PIL import Image

from model import Module

# 加载数据集
dataset = torchvision.datasets.CIFAR10("./dataset", train=False, transform=torchvision.transforms.ToTensor(),
                                         download=False)
# 获取分类列表
class_list = list(dataset.class_to_idx.keys())

# 加载图片
image_path = "./data/images/dog.jpg"
image = Image.open(image_path)

'''
image = image.convert('RGB')
由于图片有png jpg的不同格式,而png图片是四通道的 多一个透明度通道,jpg是三通道的 只有三个颜色通道
这一行代码可以让png jpg都只有三个颜色通道,增强了代码的适应性
'''
image = image.convert('RGB')
transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32, 32)),
                                            torchvision.transforms.ToTensor()])
image = transform(image)
print(image.shape)

# 加载模型
model = Module()
model.load_state_dict(torch.load("./model/best.pth", map_location=torch.device('cuda')))
print(model)
image = torch.reshape(image, (1, 3, 32, 32))

# 查看模型输出
model.eval()
with torch.no_grad():
    output = model(image)
# 所有类别的概率
print(output)

# 找到最大的概率值的位置 查看数字对应类别在debug datasets.CIFAR10的class to idx
print(output.argmax(1))
# 输出预测结果
print(class_list[output.argmax(1)])