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

实战2. 利用Pytorch解决 CIFAR 数据集中的图像分类为 10 类的问题——提高精度

实战2. 利用Pytorch解决 CIFAR 数据集中的图像分类为 10 类的问题——提高精度

  • 前期准备
  • 加载数据
  • 建立模型
  • 模型训练
  • 质量指标

让我们回到图像分类问题 CIFAR。

你的主要任务:实现整个模型训练流程,并在测试样本上获得良好的准确度指标值。 任务积分:

  • 0,如果测试样本的准确度<0.5;
  • 如果测试样本的准确度 >0.5 且 <0.6,则为 0.5;
  • 1,如果测试样本的准确度>0.6;

本任务中用于训练模型的代码已完整实现。您需要做的就是为神经网络类编写代码并试验参数以获得良好的质量。除此之外,你要保证模型不超过300M的内存使用!

前期准备

import numpy as np

import torch
from torch import nn
from torch.nn import functional as F

import torchvision
from torchvision import datasets, transforms

from matplotlib import pyplot as plt
from IPython.display import clear_output
import numpy as np
np.random.seed(42)


class LinearRegression:
    def init(self, **kwargs):
        self.coef_ = None
        pass

    def fit(self, x: np.array, y: np.array):
        # 添加一列全为1的偏置项
        x = np.concatenate([np.ones((x.shape[0], 1)), x], axis=1)
        # 使用最小二乘法计算系数
        self.coef_ = np.linalg.inv(x.T @ x) @ x.T @ y
        return self

    def predict(self, x: np.array):
        # 添加一列全为1的偏置项
        x = np.concatenate([np.ones((x.shape[0], 1)), x], axis=1)
        # 根据计算出的系数进行预测
        y_pred = x @ self.coef_
        return y_pred

加载数据

数据加载代码与我们之前课程中的相同。没有必要改变任何东西。

# 从 torchvision 加载数据集
train_data = datasets.CIFAR10(root="./cifar10_data", train=True, download=True, transform=transforms.ToTensor())
test_data = datasets.CIFAR10(root="./cifar10_data", train=False, download=True, transform=transforms.ToTensor())

# 将训练部分分为train和val

# 我们将把 80% 的图片纳入训练样本
train_size = int(len(train_data) * 0.8)
# 进行验证 - 剩余 20%
val_size = len(train_data) - train_size

train_data, val_data = torch.utils.data.random_split(train_data, [train_size, val_size])

# 我们启动将生成批次的数据加载器
train_loader = torch.utils.data.DataLoader(train_data, batch_size=64, shuffle=True)
val_loader = torch.utils.data.DataLoader(val_data, batch_size=64, shuffle=False)
test_loader = torch.utils.data.DataLoader(test_data, batch_size=64, shuffle=False)

输出:
100%|██████████| 170M/170M [00:04<00:00, 40.6MB/s]

让我们看一下数据集中的一些图像:

# 绘制图像的函数
def show_images(images, labels):
    f, axes= plt.subplots(1, 10, figsize=(30,5))

    for i, axis in enumerate(axes):
        # 将图像从张量转换为numpy
        img = images[i].numpy()
        # 将图像转换为尺寸(长度、宽度、颜色通道)
        img = np.transpose(img, (1, 2, 0))

        axes[i].imshow(img)
        axes[i].set_title(labels[i].numpy())

    plt.show()

# 获取一批图像
for batch in train_loader:
    images, labels = batch
    break

show_images(images, labels)

在这里插入图片描述

建立模型

下面是用于构建模型的单元格。您不应该立即制作具有大量层的大型复杂模型:这样的网络将需要很长时间来训练,并且很可能会过度训练。

您的主要任务是训练模型并在延迟(测试样本)上获得至少 60% 准确度的质量。

注意:你的模型必须由“模型”变量表示。

您可以尝试以下方法来改善网络结果:

  • 尝试不同数量的卷积层和全连接层;
  • 在卷积层中尝试不同数量的过滤器;
  • 在隐藏的全连接层中尝试不同数量的神经元;
  • 尝试在完全连接层和卷积层之后添加 BatchNorm。请注意,nn.BatchNorm2d 用于卷积层。 num_features 参数等于卷积层的过滤器(out_channels)的数量;
  • 尝试添加/删除 max_pooling;
  • 改变学习率;
  • 对网络进行更多次的训练。

如果您的模型过度拟合(验证指标开始变得更糟),请尝试减少模型参数的数量。如果模型没有过拟合,但是效果不佳,请尝试增加模型参数的数量。

模板为:

# 此处输入您的代码
# 声明一个卷积神经网络类

class ConvNet(nn.Module):
    def __init__(self):
        super().__init__()

        # 此处输入您的代码
        # 定义网络的层
        ...


    def forward(self, x):
        # 维度 x ~ [64, 3, 32, 32]

        # 此处输入您的代码
        # 实现前向传递网络 
        ...

我们按照要求进行解决

class ConvNet(nn.Module):
    def __init__(self):
        super().__init__()
        # 第一个卷积层,输入通道 3(RGB 图像),输出通道 16,卷积核大小 3
        self.conv1 = nn.Conv2d(3, 16, kernel_size=3, padding=1)
        # 第一个 BatchNorm 层,对应卷积层的输出通道数 16
        self.bn1 = nn.BatchNorm2d(16)
        # 最大池化层,池化核大小 2
        self.pool1 = nn.MaxPool2d(2)
        # 第二个卷积层,输入通道 16,输出通道 32,卷积核大小 3
        self.conv2 = nn.Conv2d(16, 32, kernel_size=3, padding=1)
        # 第二个 BatchNorm 层,对应卷积层的输出通道数 32
        self.bn2 = nn.BatchNorm2d(32)
        # 最大池化层,池化核大小 2
        self.pool2 = nn.MaxPool2d(2)
        # 展平层
        self.flatten = nn.Flatten()
        # 第一个全连接层,输入维度根据前面卷积和池化层输出计算得到(8 * 8 * 32),输出维度 64
        self.fc1 = nn.Linear(8 * 8 * 32, 64)
        # 第一个全连接层的 BatchNorm 层
        self.bn_fc1 = nn.BatchNorm1d(64)
        # 第二个全连接层,输入维度 64,输出维度 10(CIFAR - 10 有 10 个类别)
        self.fc2 = nn.Linear(64, 10)

    def forward(self, x):
        # 第一个卷积层 -> BatchNorm -> ReLU 激活 -> 最大池化
        x = F.relu(self.bn1(self.conv1(x)))
        x = self.pool1(x)
        # 第二个卷积层 -> BatchNorm -> ReLU 激活 -> 最大池化
        x = F.relu(self.bn2(self.conv2(x)))
        x = self.pool2(x)
        # 展平
        x = self.flatten(x)
        # 第一个全连接层 -> BatchNorm -> ReLU 激活
        x = F.relu(self.bn_fc1(self.fc1(x)))
        # 第二个全连接层得到最终输出
        x = self.fc2(x)
        return x
model = ConvNet()

下面的单元检查 GPU 是否可用,如果有,则将神经网络传输到 GPU。

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)

模型训练

网络训练功能(无需更改)。

该函数每50次训练迭代输出一次训练样本上的损失和准确率的当前值。此外,每次迭代之后都会计算并显示验证样本的损失和准确度。这些值让你了解你的模型学习得如何。

def evaluate(model, dataloader, loss_fn):

    losses = []

    num_correct = 0
    num_elements = 0

    for i, batch in enumerate(dataloader):

        # 这就是我们获取当前批次的方法
        X_batch, y_batch = batch
        num_elements += len(y_batch)

        with torch.no_grad():
            logits = model(X_batch.to(device))

            loss = loss_fn(logits, y_batch.to(device))
            losses.append(loss.item())

            y_pred = torch.argmax(logits, dim=1)

            num_correct += torch.sum(y_pred.cpu() == y_batch)

    accuracy = num_correct / num_elements

    return accuracy.numpy(), np.mean(losses)

def train(model, loss_fn, optimizer, n_epoch=3):

    # 网络训练周期
    for epoch in range(n_epoch):

        print("Epoch:", epoch+1)

        model.train(True)

        running_losses = []
        running_accuracies = []
        for i, batch in enumerate(train_loader):
            # 这就是我们获取当前批次的方法
            X_batch, y_batch = batch

            # 前向传递(获取对一批图像的响应)
            logits = model(X_batch.to(device))

            # 计算网络给出的答案和批次的正确答案的损失
            loss = loss_fn(logits, y_batch.to(device))
            running_losses.append(loss.item())

            loss.backward() # backpropagation (梯度计算)
            optimizer.step() # 更新网络权重
            optimizer.zero_grad() # 重置权重

            # 计算当前训练批次的准确率
            model_answers = torch.argmax(logits, dim=1)
            train_accuracy = torch.sum(y_batch == model_answers.cpu()) / len(y_batch)
            running_accuracies.append(train_accuracy)

            # 记录结果
            if (i+1) % 100 == 0:
                print("Average train loss and accuracy over the last 50 iterations:",
                      np.mean(running_losses), np.mean(running_accuracies), end='\n')

        # 每个时期之后,我们都会得到验证样本的质量指标
        model.train(False)

        val_accuracy, val_loss = evaluate(model, val_loader, loss_fn=loss_fn)
        print("Epoch {}/{}: val loss and accuracy:".format(epoch+1, n_epoch,),
                      val_loss, val_accuracy, end='\n')

    return model

我们正在开始训练

# 再次声明模型
model = ConvNet()
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)

# 选择损失函数
loss_fn = torch.nn.CrossEntropyLoss()

# 选择优化算法和学习率。
# 你可以尝试不同的 learning_rate 值
learning_rate = 1e-3
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
# 让我们开始训练模型
# 参数 n_epoch 可以变化
model = train(model, loss_fn, optimizer, n_epoch=3)

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

质量指标

获取测试样本的质量指标

test_accuracy, _ = evaluate(model, test_loader, loss_fn)
print('Accuracy on the test', test_accuracy)

输出:
Accuracy on the test 0.6913

检查是否满足所需的阈值:

if test_accuracy <= 0.5:
    print("测试质量低于0.5,0分")
elif test_accuracy < 0.6:
    print("测试质量在 0.5 至 0.6 之间,得 0.5 分")
elif test_accuracy >= 0.6:
    print("测试质量高于 0.6,得 1 分")

输出:
测试质量高于 0.6,得 1 分

下面的单元格包含使用训练过的网络获取文件的代码,获取pth文件

model.eval()
x = torch.randn((1, 3, 32, 32))
torch.jit.save(torch.jit.trace(model.cpu(), (x)), "model.pth")
http://www.dtcms.com/a/73565.html

相关文章:

  • 施磊老师c++(八)
  • 唤起“栈”的回忆
  • 【数据结构】栈与队列:基础 + 竞赛高频算法实操(含代码实现)
  • Web测试
  • 神聖的綫性代數速成例題7. 逆矩陣的性質、逆矩陣的求法
  • 深度学习-yolo实战项目【分类、目标检测、实例分割】?如何创建自己的数据集?如何对数据进行标注?没有GPU怎么办呢?
  • 计算机网络基础:网络配置与管理
  • ImGui 学习笔记(五) —— 字体文件加载问题
  • Redis集群扩容实战指南:从原理到生产环境最佳实践
  • DICOM医学影像数据加密技术应用的重要性及其实现方法详解
  • 优选算法的匠心之艺:二分查找专题(二)
  • 双模型协作机制的deepseek图片识别
  • Linux错误(2)程序触发SIGBUS信号分析
  • CTF类题目复现总结-真的很杂 1
  • Spring Boot 集成 Lua 脚本:实现高效业务逻辑处理
  • 【小项目】四连杆机构的Python运动学求解和MATLAB图形仿真
  • Elasticsearch:为推理端点配置分块设置
  • 【微服务】SpringBoot整合LangChain4j 操作AI大模型实战详解
  • Qt SQL-1
  • 基于MapReduce的气候数据分析
  • [JAVASE] 反射
  • USB转多路串口项目资料汇总
  • 第九讲 排序(上)
  • (链表)面试题 02.07. 链表相交
  • 【vue2 + Cesium】相机视角移动+添加模型、模型点击事件
  • 鸿蒙开发:什么是ArkTs?
  • Vue学习笔记集--props组件
  • 快速进行数据验证的优雅实现-注解
  • DeepSeek + 药物研发:解决药物研发周期长、成本高-降低80%、失败率高-减少40%
  • Flink 初体验:从 Hello World 到实时数据流处理