第P3周:Pytorch实现天气识别


FROM


我的环境

  • 语言环境:Python 3.8.10
  • 开发工具:Jupyter Lab
  • 深度学习环境:
    • torch==1.12.1+cu113
    • torchvision==0.13.1+cu113

1. 准备知识

1.1 检查环境

import torch  # 导入PyTorch库,用于构建深度学习模型
import torch.nn as nn  # 导入torch.nn模块,包含构建神经网络所需的类和函数
import matplotlib.pyplot as plt  # 导入matplotlib.pyplot模块,用于数据可视化
import torchvision  # 导入torchvision库,包含处理图像和视频的工具和预训练模型

# 设置硬件设备,如果有GPU则使用,没有则使用cpu
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")  # 检查系统是否有可用的GPU,如果有则使用GPU,否则使用CPU
torch.__version__
device  # 打印当前设备,以确认是使用GPU还是CPU

输出:
输出

1.2 数据导入

数据存放位置:
在这里插入图片描述
编写代码获取/data/weather_photos文件夹下的文件夹名称,即数据集的分类

# 设置数据目录的路径为当前目录下的'data/weather_photos'文件夹
data_dir = './data/weather_photos/'

# 将字符串路径转换为pathlib.Path对象,这样可以更方便地进行路径操作
data_dir = pathlib.Path(data_dir)
print(data_dir)
# 使用glob方法获取data_dir下的所有文件和文件夹的路径,并将它们存储在data_paths列表中
# '*'表示匹配所有文件和文件夹
data_paths = list(data_dir.glob('*'))
print(data_paths)
# 使用列表推导式创建一个新列表classeNames,其中包含data_paths中每个路径的第一个部分
# 这里假设路径分隔符为'/',适用于Unix/Linux系统
# 如果在Windows系统上运行,应该使用'\\'作为分隔符
# classeNames = [str(path).split("/")[1] for path in data_paths]
classeNames = [path.name for path in data_paths if path.is_dir()]
# 打印classeNames列表,这个列表包含了所有子目录的名称
classeNames

输出:
在这里插入图片描述

编写可视化代码,查看cloudy文件夹下的图片

# 导入matplotlib的pyplot模块,用于绘图
import matplotlib.pyplot as plt
# 从PIL库导入Image模块,用于图像处理
from PIL import Image

# 指定图像文件夹的路径
image_folder = './data/weather_photos/cloudy/'

# 获取文件夹中的所有图像文件,支持jpg、png和jpeg格式
# os.listdir列出目录下的所有文件和文件夹,endswith检查文件扩展名
# 列表推导式用于创建一个包含所有图像文件名的列表
image_files = [f for f in os.listdir(image_folder) if f.endswith((".jpg", ".png", ".jpeg"))]

# 创建一个3行8列的子图网格,figsize设置图像的大小
fig, axes = plt.subplots(3, 8, figsize=(16, 6))

# 使用for循环和zip函数同时迭代子图轴对象和图像文件名
# axes.flat将二维的轴数组展平成一维,方便迭代
for ax, img_file in zip(axes.flat, image_files):
    # 构造完整的图像文件路径
    img_path = os.path.join(image_folder, img_file)
    # 使用PIL的Image模块打开图像
    img = Image.open(img_path)
    # 在对应的轴上显示图像
    ax.imshow(img)
    # 关闭轴的边框,使图像显示更清晰
    ax.axis('off')

# 使用tight_layout自动调整子图参数,使之填充整个图像区域
plt.tight_layout()
# 显示图像
plt.show()

输出:
在这里插入图片描述

使用transforms库中的函数,对数据集中的图片进行预处理

# 设置数据目录的路径为当前目录下的'data/weather_photos/'文件夹
total_datadir = './data/weather_photos/'

# 提供一个链接,用于获取关于transforms.Compose的更多信息
# transforms.Compose用于串联多个图像变换操作
# 关于transforms.Compose的更多介绍可以参考:https://blog.youkuaiyun.com/qq_38251616/article/details/124878863 
train_transforms = transforms.Compose([
    transforms.Resize([224, 224]),  # 使用Resize变换将输入图片调整为统一的尺寸224x224
    transforms.ToTensor(),          # 使用ToTensor变换将PIL Image或numpy.ndarray转换为tensor,并归一化到[0,1]之间
    transforms.Normalize(           # 使用Normalize变换进行标准化处理,转换为标准正态分布(高斯分布),使模型更容易收敛
        mean=[0.485, 0.456, 0.406], # 标准化的均值,这些值通常是从数据集中计算得到的
        std=[0.229, 0.224, 0.225])  # 标准化的标准差,这些值也是从数据集中计算得到的
])

# 使用datasets.ImageFolder类加载图像数据集
# 指定数据目录和预处理变换
total_data = datasets.ImageFolder(total_datadir, transform=train_transforms)
total_data

输出:
在这里插入图片描述

1.3 划分数据集

# 计算训练集大小,设置为总数据集的80%
train_size = int(0.8 * len(total_data))

# 计算测试集大小,即总数据集大小减去训练集大小
test_size = len(total_data) - train_size

# 使用torch.utils.data.random_split函数随机划分数据集为训练集和测试集
# 这个函数接受两个参数:第一个参数是原始数据集,第二个参数是划分后各部分的大小
train_dataset, test_dataset = torch.utils.data.random_split(total_data, [train_size, test_size])

# 打印划分后的训练集和测试集,以便查看
train_dataset, test_dataset

输出:
在这里插入图片描述
train_size表示训练集大小,通过将总体数据长度的80%转换为整数得到;
test_size表示测试集大小,是总体数据长度减去训练集大小。
使用torch.utils.data.random_split()方法进行数据集划分。该方法将总体数据total_data按照指定的大小比例([train_size, test_size])随机划分为训练集和测试集,并将划分结果分别赋值给train_datasettest_dataset两个变量。
在这里插入图片描述
设置训练的基础配置

# 设置每个批次的样本数量为32
batch_size = 32

# 创建训练数据的DataLoader
# train_dataset: 训练数据集
# batch_size=batch_size: 每个批次的样本数量
# shuffle=True: 在每个epoch开始时,数据被打乱,有助于模型训练
# num_workers=1: 加载数据时使用的子进程数量,设置为1表示主进程自己加载数据
train_dl = torch.utils.data.DataLoader(train_dataset,
                                       batch_size=batch_size,
                                       shuffle=True,
                                       num_workers=1)

# 创建测试数据的DataLoader,参数设置与训练数据的DataLoader相同
test_dl = torch.utils.data.DataLoader(test_dataset,
                                      batch_size=batch_size,
                                      shuffle=True,
                                      num_workers=1)

# 迭代测试数据加载器test_dl
for X, y in test_dl:
    # 打印图像数据X的形状,其中N是批次大小,C是通道数,H是高度,W是宽度
    print("Shape of X [N, C, H, W]: ", X.shape)
    # 打印标签数据y的形状和数据类型
    print("Shape of y: ", y.shape, y.dtype)
    # 由于只需要查看一个批次的数据,所以使用break语句退出循环
    break

输出:
在这里插入图片描述

torch.utils.data.DataLoader()参数详解

torch.utils.data.DataLoaderPyTorch 中用于加载和管理数据的一个实用工具类。它允许你以小批次的方式迭代你的数据集,这对于训练神经网络和其他机器学习任务非常有用。DataLoader 构造函数接受多个参数,下面是一些常用的参数及其解释:

  1. dataset(必需参数):这是你的数据集对象,通常是 torch.utils.data.Dataset 的子类,它包含了你的数据样本。
  2. batch_size(可选参数):指定每个小批次中包含的样本数。默认值为 1。
  3. shuffle(可选参数):如果设置为 True,则在每个 epoch 开始时对数据进行洗牌,以随机打乱样本的顺序。这对于训练数据的随机性很重要,以避免模型学习到数据的顺序性。默认值为 False
  4. num_workers(可选参数):用于数据加载的子进程数量。通常,将其设置为大于 0 的值可以加快数据加载速度,特别是当数据集很大时。默认值为 0,表示在主进程中加载数据。
  5. pin_memory(可选参数):如果设置为 True,则数据加载到 GPU 时会将数据存储在 CUDA 的锁页内存中,这可以加速数据传输到 GPU。默认值为 False
  6. drop_last(可选参数):如果设置为 True,则在最后一个小批次可能包含样本数小于 batch_size 时,丢弃该小批次。这在某些情况下很有用,以确保所有小批次具有相同的大小。默认值为 False。
  7. timeout(可选参数):如果设置为正整数,它定义了每个子进程在等待数据加载器传递数据时的超时时间(以秒为单位)。这可以用于避免子进程卡住的情况。默认值为 0,表示没有超时限制。
  8. worker_init_fn(可选参数):一个可选的函数,用于初始化每个子进程的状态。这对于设置每个子进程的随机种子或其他初始化操作很有用。

2. 构建简单的CNN网络

卷积神经网络(CNN)是一种深度学习模型,广泛应用于图像识别、分类和分割等任务。下面是一些主要组件的详细说明:

  1. torch.nn.Conv2d()详解
    torch.nn.Conv2d是 PyTorch 中定义二维卷积层的类。以下是它的参数的详细解释:
torch.nn.Conv2d(
   in_channels: int, 输入数据的通道数。例如,对于RGB图像,in_channels 通常是3。
   out_channels: int, 输出数据的通道数。这通常决定了卷积层输出的特征图的数量。
   kernel_size: int or tuple, 卷积核的大小。可以是单个整数(表示卷积核的高度和宽度相同),也可以是元组 (H, W) 指定高度和宽度。
   stride: int or tuple = 1, 卷积的步长。可以是单个整数(表示高度和宽度的步长相同),也可以是元组 (H, W) 指定不同的步长。
   padding: int or tuple or str = 0, 填充的大小。可以是单个整数(表示在高度和宽度方向上添加相同数量的填充),可以是元组 (H, W) 指定不同的填充,也可以是字符串(如 'same' 或 'valid'),这取决于 padding_mode 参数。
   dilation: int or tuple = 1, 卷积的空洞率。可以是单个整数(表示高度和宽度的空洞率相同),也可以是元组 (H, W) 指定不同的空洞率。
   groups: int = 1, 分组卷积的数量。分组卷积可以减少模型的参数数量和计算量。
   bias: bool = True, 布尔值,指示卷积层是否包含偏置项。
   padding_mode: str = 'zeros', 填充模式,可以是 'zeros'、'reflect'、'replicate' 或 'circular'。
   device: torch.device = None, 指定设备对象,卷积层的参数将被初始化在该设备上。
   dtype: torch.dtype = None 指定数据类型对象,卷积层的参数将被初始化为该数据类型。
   )
  • 作用:用于提取图像的特征。通过学习图像中局部区域的特征,卷积层可以捕捉到图像的局部特征。
  1. torch.nn.Linear()详解
    torch.nn.Linear 是 PyTorch 中定义全连接层(也称为线性层或稠密层)的类。以下是它的参数的详细解释:
torch.nn.Linear(
    in_features: int, 输入数据的特征数量。这是全连接层输入的每个样本的特征数。
    out_features: int, 输出数据的特征数量。这是全连接层输出的每个样本的特征数,也称为神经元的数量。
    bias: bool = True, 布尔值,指示全连接层是否包含偏置项。如果设置为 True,则层中将包含偏置项;如果设置为 False,则不包含。
    device: torch.device = None, 指定设备对象,全连接层的参数将被初始化在该设备上。如果为 None,则参数将被初始化在 CPU 上。
    dtype: torch.dtype = None 指定数据类型对象,全连接层的参数将被初始化为该数据类型。如果为 None,则参数将被初始化为默认的数据类型(通常是 torch.float32)。
)
  • 作用:在CNN中,全连接层通常用于最终的分类或回归任务。在特征提取的最后阶段,全连接层将学习到的高级特征映射到最终的输出类别。
  1. torch.nn.MaxPool2d()详解
    torch.nn.MaxPool2d 是 PyTorch 中定义二维最大池化层的类。最大池化层用于对输入特征图进行下采样,通过滑动窗口中的最大值来降低特征图的空间维度,同时保留最重要的信息。以下是它的参数的详细解释:
torch.nn.MaxPool2d(
    kernel_size: int or tuple, 池化窗口的大小。可以是单个整数(表示高度和宽度的窗口大小相同),也可以是元组 (H, W) 指定高度和宽度的窗口大小。
    stride: int or tuple = None, 池化的步长。可以是单个整数(表示高度和宽度的步长相同),也可以是元组 (H, W) 指定不同的步长。如果设置为 None,则默认等于 kernel_size。
    padding: int or tuple = 0, 填充的大小。可以是单个整数(表示在高度和宽度方向上添加相同数量的填充),也可以是元组 (H, W) 指定不同的填充。
    dilation: int = 1, 池化窗口的空洞率。可以是单个整数(表示高度和宽度的空洞率相同),也可以是元组 (H, W) 指定不同的空洞率。空洞池化可以增加池化窗口的有效感受野而不增加计算量。
    return_indices: bool = False, 布尔值,指示是否返回最大值的索引。如果设置为 True,则返回每个输出元素的最大值的索引。
    ceil_mode: bool = False 布尔值,指示是否使用天花板函数来计算输出大小。如果设置为 True,则输出大小将向上取整。
)
  • 作用:最大池化层是卷积神经网络中常用的结构,它有助于减少特征图的维度,从而减少模型的参数数量和计算量,同时能够保持特征的重要信息。
    [图片]
    计算网络shape变化:
    在这里插入图片描述
# 导入PyTorch中的功能函数模块
import torch.nn.functional as F

# 定义一个继承自nn.Module的类Network_bn,表示一个卷积神经网络
class Network_bn(nn.Module):
    # 初始化方法
    def __init__(self):
        # 调用父类nn.Module的初始化方法
        super(Network_bn, self).__init__()
        
        # 定义第一个卷积层conv1,输入通道数为3(RGB图像),输出通道数为12,卷积核大小为5x5
        self.conv1 = nn.Conv2d(in_channels=3, out_channels=12, kernel_size=5, stride=1, padding=0)
        # 定义第一个批量归一化层bn1,与conv1的输出通道数相同
        self.bn1 = nn.BatchNorm2d(12)
        
        # 定义第二个卷积层conv2,输入和输出通道数均为12
        self.conv2 = nn.Conv2d(in_channels=12, out_channels=12, kernel_size=5, stride=1, padding=0)
        # 定义第二个批量归一化层bn2
        self.bn2 = nn.BatchNorm2d(12)
        
        # 定义第一个最大池化层pool1,池化窗口大小为2x2,步长为2
        self.pool1 = nn.MaxPool2d(2,2)
        
        # 定义第三个卷积层conv4,输入通道数为12,输出通道数为24
        self.conv4 = nn.Conv2d(in_channels=12, out_channels=24, kernel_size=5, stride=1, padding=0)
        # 定义第三个批量归一化层bn4
        self.bn4 = nn.BatchNorm2d(24)
        
        # 定义第四个卷积层conv5,输入和输出通道数均为24
        self.conv5 = nn.Conv2d(in_channels=24, out_channels=24, kernel_size=5, stride=1, padding=0)
        # 定义第四个批量归一化层bn5
        self.bn5 = nn.BatchNorm2d(24)
        
        # 定义第二个最大池化层pool2
        self.pool2 = nn.MaxPool2d(2,2)
        
        # 定义一个全连接层fc1,输入特征数为24*50*50(假设池化后的特征图大小为50x50),输出特征数为类别数len(classeNames)
        self.fc1 = nn.Linear(24*50*50, len(classeNames))

    # 前向传播方法
    def forward(self, x):
        # 通过卷积层、批量归一化层和ReLU激活函数
        x = F.relu(self.bn1(self.conv1(x)))      
        x = F.relu(self.bn2(self.conv2(x)))     
        # 通过最大池化层
        x = self.pool1(x)                        
        x = F.relu(self.bn4(self.conv4(x)))     
        x = F.relu(self.bn5(self.conv5(x)))  
        # 再次通过最大池化层
        x = self.pool2(x)                        
        # 调整张量形状以匹配全连接层的输入要求
        x = x.view(-1, 24*50*50)
        # 通过全连接层
        x = self.fc1(x)

        # 返回网络的输出
        return x

# 检测是否有可用的GPU,如果有则使用GPU,否则使用CPU
device = "cuda" if torch.cuda.is_available() else "cpu"
# 打印使用的设备
print("Using {} device".format(device))

# 实例化网络并将它发送到指定的设备(GPU或CPU)
model = Network_bn().to(device)
# 打印模型
model

输出:
在这里插入图片描述


3. 模型训练

3.1 设置超参数

# 导入PyTorch的nn模块
import torch.nn as nn
import torch.optim as optim

# 创建一个交叉熵损失函数实例
# 交叉熵损失函数是多类分类问题中常用的损失函数
loss_fn = nn.CrossEntropyLoss()

# 设置学习率为0.0001
learn_rate = 1e-4

# 创建随机梯度下降(SGD)优化器实例
# 优化器用于更新模型的权重以最小化损失函数
# model.parameters()提供了模型中所有需要优化的参数
# lr=learn_rate设置了优化器的学习率
opt = torch.optim.SGD(model.parameters(), lr=learn_rate)
  • nn.CrossEntropyLoss() 是PyTorch中的交叉熵损失函数,它通常用于多分类问题。这个损失函数结合了nn.LogSoftmax()nn.NLLLoss(),即它首先应用对数softmax函数,然后计算负对数似然损失(Negative Log Likelihood Loss)。
  • 学习率是优化算法中的一个重要参数,它决定了模型在训练过程中参数更新的幅度。如果学习率设置得过大,可能会导致训练过程中的梯度爆炸,从而无法收敛;如果学习率设置得过小,可能会导致训练过程缓慢,甚至陷入局部最优。
  • torch.optim.SGD 是PyTorch中的随机梯度下降(Stochastic Gradient Descent)优化器。
  • model.parameters() 是一个生成器,它返回模型中所有可训练的参数。
  • lr=learn_rate 设置优化器的学习率为之前定义的learn_rate

3.2 训练函数

  1. optimizer.zero_grad()
  • 这个函数用于清零(重置)模型所有参数的梯度。在PyTorch中,梯度是累加的,因此每次进行参数更新前,都需要将梯度清零,以避免将上次迭代的梯度累积到本次迭代中。
  • 这一步通常在每次迭代的开始进行,以确保每次计算的梯度都是当前批次数据的梯度,而不是累积的梯度。
  1. loss.backward()
  • 这个函数用于进行反向传播。在PyTorch中,当您对一个Tensor调用.backward()方法时,会自动计算这个Tensor的梯度,并且沿着计算图反向传播,直到所有的叶子节点(即模型的参数)。
  • loss.backward()会将损失函数关于模型参数的梯度计算出来,并将这些梯度保存到对应参数的.grad属性中。
  • 需要注意的是,只有设置了requires_grad=True的Tensor才会在反向传播中计算梯度。
  1. optimizer.step()
  • 这个函数用于根据计算出的梯度更新模型的参数。在调用optimizer.step()之前,需要确保已经调用了loss.backward()来计算梯度。
  • optimizer.step()会根据优化器的算法(例如SGD、Adam等)和学习率来更新参数的值,实现模型的学习。
    optimizer只负责通过梯度下降进行优化,而不负责产生梯度,梯度是tensor.backward()方法产生的。
# 定义训练函数
def train(dataloader, model, loss_fn, optimizer):
    # 获取训练数据集的大小
    size = len(dataloader.dataset)
    # 获取批次的数量
    num_batches = len(dataloader)

    # 初始化训练损失和准确率
    train_loss, train_acc = 0, 0
    
    # 遍历数据加载器中的所有批次
    for X, y in dataloader:
        # 将图片和标签数据传输到指定的设备(GPU或CPU)
        X, y = X.to(device), y.to(device)
        
        # 计算模型的预测结果
        pred = model(X)
        # 计算预测结果和真实标签之间的损失
        loss = loss_fn(pred, y)
        
        # 执行反向传播前,先清除之前的梯度信息
        optimizer.zero_grad()
        # 反向传播,计算梯度
        loss.backward()
        # 根据梯度更新模型的权重
        optimizer.step()
        
        # 计算准确率,并将结果累加
        train_acc += (pred.argmax(1) == y).type(torch.float).sum().item()
        # 累加损失值
        train_loss += loss.item()
            
    # 计算平均准确率和损失
    train_acc /= size
    train_loss /= num_batches

    # 返回训练过程中的平均准确率和损失
    return train_acc, train_loss

3.3 测试函数

  • 这个 test函数接收三个参数:dataloadermodelloss_fn
  • 它遍历 dataloader 中的所有批次,对每个批次执行前向传播和损失计算,但不进行反向传播或参数更新,因为测试阶段的目的是评估模型性能,而不是训练模型。
  • 在每个批次中,它计算模型的预测值 target_pred,然后使用损失函数 loss_fn 计算预测值和真实标签 target 之间的损失 loss
  • 函数还记录了整个测试集上的平均准确率和平均损失。
# 定义测试函数
def test(dataloader, model, loss_fn):
    # 获取测试数据集的大小
    size = len(dataloader.dataset)
    # 获取批次的数量
    num_batches = len(dataloader)
    
    # 初始化测试损失和准确率
    test_loss, test_acc = 0, 0
    
    # 使用torch.no_grad()上下文管理器来停止梯度计算,节省内存和计算资源
    with torch.no_grad():
        # 遍历数据加载器中的所有批次
        for imgs, target in dataloader:
            # 将图片和标签数据传输到指定的设备(GPU或CPU)
            imgs, target = imgs.to(device), target.to(device)
            
            # 计算模型的预测结果
            target_pred = model(imgs)
            # 计算预测结果和真实标签之间的损失
            loss = loss_fn(target_pred, target)
            
            # 累加损失值
            test_loss += loss.item()
            # 计算准确率,并将结果累加
            test_acc += (target_pred.argmax(1) == target).type(torch.float).sum().item()

    # 计算平均准确率和损失
    test_acc /= size
    test_loss /= num_batches

    # 返回测试过程中的平均准确率和损失
    return test_acc, test_loss

3.4 训练

  1. model.train()
    model.train()的作用是启用 Batch NormalizationDropout
    model.train()PyTorch 中用于将模型设置为训练模式,这主要影响模型中特定层的行为,如 Batch Normalization(批量归一化)和 Dropout。以下是这些层在训练模式下的行为解释:
  • Batch Normalization(批量归一化):
    在训练模式下,Batch Normalization 层会计算并存储每个批次数据的均值和方差,然后使用这些统计数据来归一化该批次的数据。这样做的目的是为了减少内部协变量偏移(Internal Covariate Shift),即确保网络的每一层输入数据分布保持相对稳定,从而加快训练速度并提高模型性能。
  • Dropout
    Dropout 是一种正则化技术,用于防止模型过拟合。在训练模式下,Dropout 层会随机地将一部分网络连接“丢弃”(即设置为零),这样每次训练时只有一部分网络被更新。这种方法可以减少神经元之间复杂的共适应关系,增强模型的泛化能力。

当调用 model.train() 时,模型中的所有层都会知道模型处于训练模式。对于 Batch Normalization 层,这意味着它们会计算并更新均值和方差的估计值;对于 Dropout 层,这意味着它们会随机丢弃一些神经元。
在测试或验证模型时,通常使用 model.eval() 来将模型设置为评估模式。在评估模式下,Batch Normalization 层会使用训练期间计算的均值和方差来归一化数据,而 Dropout 层则不会丢弃任何神经元,即它们会保持所有连接的激活状态。
2. model.eval()

model.eval()的作用是不启用 Batch NormalizationDropout

  • Batch Normalization(批量归一化):
    在评估模式下,Batch Normalization 层会使用训练期间计算并存储的全局均值和方差来归一化数据,而不是使用当前批次的统计数据。这样做是为了确保模型在训练和评估时表现的一致性,因为训练时使用的是每个批次的动态统计数据,而评估时则使用整个训练集的静态统计数据。
  • Dropout
    在评估模式下,Dropout 层不会随机丢弃任何神经元,即不会应用任何丢弃概率。这意味着在评估或测试时,所有的神经元都会参与前向传播,确保模型使用其全部能力来处理输入数据。

使用 model.eval() 是为了确保在模型评估或测试时,Batch NormalizationDropout 层的行为与训练时不同,以反映模型在实际部署时的预期行为。这对于获得准确的性能指标至关重要。

# 设置训练周期的数量
epochs = 20

# 初始化用于记录训练损失、训练准确率、测试损失和测试准确率的列表
train_loss = []
train_acc = []
test_loss = []
test_acc = []

# 开始训练循环
for epoch in range(epochs):
    # 将模型设置为训练模式
    model.train()
    # 调用train函数进行训练,并获取该周期的训练准确率和损失
    epoch_train_acc, epoch_train_loss = train(train_dl, model, loss_fn, opt)
    
    # 将模型设置为评估模式
    model.eval()
    # 调用test函数进行测试,并获取该周期的测试准确率和损失
    epoch_test_acc, epoch_test_loss = test(test_dl, model, loss_fn)
    
    # 将当前周期的训练和测试结果添加到对应的列表中
    train_acc.append(epoch_train_acc)
    train_loss.append(epoch_train_loss)
    test_acc.append(epoch_test_acc)
    test_loss.append(epoch_test_loss)
    
    # 打印当前周期的性能指标
    # 使用格式化字符串来清晰地展示信息
    template = ('Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%, Test_loss:{:.3f}')
    print(template.format(epoch+1, epoch_train_acc*100, epoch_train_loss, epoch_test_acc*100, epoch_test_loss))

# 训练结束后打印完成信息
print('Done')

输出:
在这里插入图片描述


4. 结果可视化

# 导入matplotlib的pyplot模块,用于绘图
import matplotlib.pyplot as plt

# 导入warnings模块,用于控制警告信息的显示
import warnings
# 忽略警告信息,以保持输出的清洁
warnings.filterwarnings("ignore")               # 忽略警告信息
# 设置matplotlib的参数,以正常显示中文标签
# plt.rcParams['font.sans-serif'] = ['SimHei']
# 设置matplotlib的参数,以正常显示负号
plt.rcParams['axes.unicode_minus'] = False
# 设置图像的分辨率
plt.rcParams['figure.dpi'] = 100

# 创建一个表示训练周期范围的迭代器
epochs_range = range(epochs)

# 创建一个宽12英寸、高3英寸的图像
plt.figure(figsize=(12, 3))
# 创建一个1行2列的子图布局
plt.subplot(1, 2, 1)

# 在左侧子图中绘制训练和测试准确率曲线
plt.plot(epochs_range, train_acc, label='Training Accuracy')
plt.plot(epochs_range, test_acc, label='Test Accuracy')
# 显示图例,位置在右下角
plt.legend(loc='lower right')
# 设置子图标题
plt.title('Training and Validation Accuracy')

# 创建右侧子图
plt.subplot(1, 2, 2)
# 在右侧子图中绘制训练和测试损失曲线
plt.plot(epochs_range, train_loss, label='Training Loss')
plt.plot(epochs_range, test_loss, label='Test Loss')
# 显示图例,位置在右上角
plt.legend(loc='upper right')
# 设置子图标题
plt.title('Training and Validation Loss')

# 显示绘制的图像
plt.show()

输出:
在这里插入图片描述

5. 课后

5.1 要求

  • 本地读取并加载数据。

在这里插入图片描述

  • 测试集accuracy到达93%
  1. 调整学习率,测试集短暂地达到了93%的accuracy
    在这里插入图片描述在这里插入图片描述
  2. 将SDG优化器替换为Adam优化器
    在这里插入图片描述在这里插入图片描述

5.2 拔高

  • 测试集accuracy到达95%
    在已经训练过20个Epoch的模型上继续训练5个Epoch,效果较为稳定
    在这里插入图片描述
  • 调用模型识别一张本地图片
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值