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

用pytorch实现一个简单的图片预测类别

前言:

        在阅读本文之前,你需要了解Python,Pytorch,神经网络的一些基础知识,比如什么是数据集,什么是张量,什么是神经网络,如何简单使用tensorboard,DataLoader。

        本次模型训练使用的是cpu。

目录

创建python文件:

model_train.py文件

1、准备数据集

2、打印看看该数据集的大小

3、加载数据集

4、创建网络模型

model.py文件

5、定义损失函数

6、定义优化器

7、设置一些训练网络所需参数

8、可视化训练过程

9、训练过程

model_vertification文件


创建python文件:

        model.py 自定义神经网络模型。

        model_train.py 训练 CIFAR - 10 数据集上的自定义模型并保存参数。

        model_vertification.py 用一张图片验证网络模型进行预测。

下面从各个文件讲解。

model_train.py文件

完整的模型训练步骤如下:

1、准备数据集

       这里选用 CIFAR10 数据集,这个数据集是 torchvision 里面自带的,一个十分类问题的数据集,该数据集较小(160MB左右),使用torchvision.datasets模块加载 CIFAR10 数据集。

# 下载并加载 CIFAR-10 训练数据集
# root 指定数据集存储的根目录;train=True 表示加载训练集;
# transform 将数据转换为 Tensor 类型;download=True 表示如果数据集不存在则进行下载
train_data = torchvision.datasets.CIFAR10(root= r'D:\Desktop\数据集', train=True, transform=torchvision.transforms.ToTensor(), download=True)
# 下载并加载 CIFAR-10 测试数据集
test_data = torchvision.datasets.CIFAR10(root= r'D:\Desktop\数据集', train=False, transform=torchvision.transforms.ToTensor(), download=True)

2、打印看看该数据集的大小

# 计算训练集和测试集的长度
train_data_size = len(train_data)
test_data_size = len(test_data)
print(f"训练数据集的长度为: {train_data_size}")
print(f"测试数据集的长度为: {test_data_size}")

        可以看到该数据集有50000张训练图片,10000张测试图片。 

3、加载数据集

        使用DataLoader分别加载训练和测试数据集。

# 使用 DataLoader 对训练数据进行批量加载,batch_size=64 表示每个批次包含 64 个样本
train_dataloader = DataLoader(train_data, batch_size=64)
# 对测试数据进行批量加载
test_dataloader = DataLoader(test_data, batch_size=64)

4、创建网络模型

        将自定义的网络模型放在model.py文件,在train.py中导入使用。

        根据此图片的神经网络模型来自定义一个网络模型。(其中卷积层Conv2d中的参数stride和padding需要经过如下的公式计算得到,该计算并不复杂)。

        计算公式 (pytorch官网torch.nn中Conv2d中查看)

model.py文件

import torch
from torch import nn


class zzy(nn.Module):
    def __init__(self):
        super(zzy, self).__init__()
        self.model = nn.Sequential(
            nn.Conv2d(3, 32, 5, 1, 2),
            nn.MaxPool2d(2),
            nn.Conv2d(32, 32, 5, 1, 2),
            nn.MaxPool2d(2),
            nn.Conv2d(32, 64, 5, 1, 2),
            nn.MaxPool2d(2),
            nn.Flatten(),
            nn.Linear(64 * 4 * 4, 64),
            nn.Linear(64,10)
        )

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


if __name__ == '__main__':
    zzy1 = zzy()
    data = torch.ones((64,3,32,32))
    output = zzy1(data)
    print(output.shape)

        运行此文件验证该网络模型是否能得到预想的结果 ,如下。

         在train.py中导入model.py后实例化网络模型。

# 创建网络模型
# 实例化自定义的网络模型 zzy
zzy1 = zzy()

5、定义损失函数

# 定义损失函数
# 使用交叉熵损失函数,常用于多分类问题
loss_fn = nn.CrossEntropyLoss()

6、定义优化器

# 优化器创建
# 学习率设置为 0.01
learning_rate = 1e-2
# 使用随机梯度下降(SGD)优化器,对 zzy1 模型的参数进行优化
optimier = torch.optim.SGD(zzy1.parameters(), lr=learning_rate)

7、设置一些训练网络所需参数

# 设置训练网络参数
# 记录总的训练步数
total_train_step = 0
# 记录总的测试步数
total_test_step = 0
# 训练的轮数
epoch = 10

8、可视化训练过程

        为了可视化整个训练过程, 添加 TensorBoard 用于可视化训练过程。

# 在 '../logs_train' 目录下创建 SummaryWriter 对象
writer = SummaryWriter('../logs_train')

9、训练过程

        首先设置我们的训练轮次,这是外层循环,内层循环里分别进行训练数据集和测试数据集的训练。

        在训练集中,每次取出一批数据,即前面定义的64张图片,送入我们定义的网络模型得到输出,计算输出和真实值之间的损失,再进行反向传播更新模型参数进行优化,训练步数加一,再取出下一批数据,重复上面的过程。

        在测试集中,计算正确率。

# 开始训练循环,共训练 epoch 轮
for i in range(epoch):
    print(f'--------第{i + 1}次训练--------')
    # 遍历训练数据加载器中的每个批次
    for data in train_dataloader:
        # 从数据批次中解包图像和对应的标签
        imgs, target = data
        # 将图像输入到模型中进行前向传播,得到模型的输出
        outputs = zzy1(imgs)
        # 计算模型输出与真实标签之间的损失
        loss = loss_fn(outputs, target)

        # 梯度清零,防止梯度累积
        optimier.zero_grad()
        # 反向传播,计算梯度
        loss.backward()
        # 根据计算得到的梯度更新模型的参数
        optimier.step()

        # 训练步数加 1
        total_train_step += 1
        # 每训练 100 步,打印一次训练信息并将训练损失写入 TensorBoard
        if total_train_step % 100 == 0:
            print(f'训练次数:{total_train_step},Loss:{loss.item()}')
            # 将训练损失添加到 TensorBoard 中,用于后续可视化
            writer.add_scalar('train_loss', loss.item(), total_train_step)

    zzy.eval()
    # 测试步骤开始
    # 初始化总的测试损失为 0
    total_test_loss = 0
    # 初始化正确率为 0
    total_accuracy = 0
    # 上下文管理器,在测试过程中不进行梯度计算,减少内存消耗
    with torch.no_grad():
        # 遍历测试数据加载器中的每个批次
        for data in test_dataloader:
            # 从数据批次中解包图像和对应的标签
            imgs, targets = data
            # 将图像输入到模型中进行前向传播,得到模型的输出
            outputs = zzy1(imgs)
            # 计算模型输出与真实标签之间的损失
            loss = loss_fn(outputs, targets)
            # 计算正确率,outputs.argmax(1)表示横向看
            accuracy = (outputs.argmax(1) == targets).sum()
            # 累加测试损失
            total_test_loss += loss.item()
            total_accuracy += accuracy
    print(f'整体的测试损失: {total_test_loss}')
    print(f'整体的正确率: {total_accuracy/test_data_size}')
    # 将测试损失添加到 TensorBoard 中,用于后续可视化
    writer.add_scalar('test_loss', total_test_loss, total_test_step)
    writer.add_scalar('total_accuracy',total_accuracy/test_data_size,total_test_step)
    # 测试步数加 1
    total_test_step += 1
    torch.save(zzy,'zzy_{}.pth'.format(i))
    # 官方推荐的保存方式
    # torch.save(zzy.state_dict(),'zzy_{}.pth'.forma(i))
    print("模型已保存")
# 关闭 SummaryWriter,释放资源
writer.close()

完整的model_train.py代码如下:

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

# 下载并加载 CIFAR-10 训练数据集
train_data = torchvision.datasets.CIFAR10(root=r'D:\Desktop\数据集', train=True, transform=torchvision.transforms.ToTensor(), download=True)
# 下载并加载 CIFAR-10 测试数据集
test_data = torchvision.datasets.CIFAR10(root=r'D:\Desktop\数据集', 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}")

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

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

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

# 实例化自定义的网络模型 zzy
zzy1 = zzy()

# 定义损失函数
loss_fn = nn.CrossEntropyLoss()

# 优化器创建
learning_rate = 1e-2
optimier = torch.optim.SGD(zzy1.parameters(), lr=learning_rate)

# 设置训练网络参数
total_train_step = 0
total_test_step = 0
epoch = 10

# 添加 TensorBoard 用于可视化训练过程
writer = SummaryWriter('../logs_train')

# 开始训练循环,共训练 epoch 轮
for i in range(epoch):
    print(f'--------第{i + 1}次训练--------')
    # 遍历训练数据加载器中的每个批次
    for data in train_dataloader:
        # 从数据批次中解包图像和对应的标签
        imgs, target = data
        # 将图像输入到模型中进行前向传播,得到模型的输出
        outputs = zzy1(imgs)
        # 计算模型输出与真实标签之间的损失
        loss = loss_fn(outputs, target)

        # 梯度清零,防止梯度累积
        optimier.zero_grad()
        # 反向传播,计算梯度
        loss.backward()
        # 根据计算得到的梯度更新模型的参数
        optimier.step()

        # 训练步数加 1
        total_train_step += 1
        # 每训练 100 步,打印一次训练信息并将训练损失写入 TensorBoard
        if total_train_step % 100 == 0:
            print(f'训练次数:{total_train_step},Loss:{loss.item()}')
            # 将训练损失添加到 TensorBoard 中,用于后续可视化
            writer.add_scalar('train_loss', loss.item(), total_train_step)

    # 设置模型为评估模式
    zzy1.eval()
    # 测试步骤开始
    total_test_loss = 0
    total_accuracy = 0
    with torch.no_grad():
        for data in test_dataloader:
            imgs, targets = data
            outputs = zzy1(imgs)
            loss = loss_fn(outputs, targets)
            accuracy = (outputs.argmax(1) == targets).sum()
            total_test_loss += loss.item()
            total_accuracy += accuracy

    print(f'整体的测试损失: {total_test_loss}')
    print(f'整体的正确率: {total_accuracy / test_data_size}')
    # 将测试损失添加到 TensorBoard 中,用于后续可视化
    writer.add_scalar('test_loss', total_test_loss, total_test_step)
    writer.add_scalar('total_accuracy', total_accuracy / test_data_size, total_test_step)
    # 测试步数加 1
    total_test_step += 1

    # 保存模型状态字典
    torch.save(zzy1.state_dict(), f'zzy_{i}.pth')
    print("模型已保存")

# 关闭 SummaryWriter,释放资源
writer.close()

运行结果:

        从下图可以看到,最后训练出来的模型预测正确率为0.54左右,不算好,如果想继续优化,加大训练轮次,或者调整学习率。 

        打开tensorboard观察到整个训练过程的变化,图中深色线是经过平滑处(Smoothed)的训练损失值,能更清晰呈现损失总体变化趋势,减少波动干扰;浅色线代表原始的训练损失值,反映每个训练步骤上即时的损失情况,波动相对较大。

 

后续补充:

        想查看整个训练所用时间,可以导入time模块,设置一下开始训练时间和结束训练时间求差。

        当我把训练次数加大10倍(100次)后,模型预测的正确率为0.63左右,相对0.53没有提高很多,而且训练轮次较多时或者数据量较大时用cpu计算的时间花费就比较多了 (不要参考下面的时间,中途暂停过较长时间)。

        到中期训练了40轮次后,从正确率的变化可以看出,模型效果不佳 。

        当然可能有很多原因导致,数据量不足,网络模型结构不合理,优化器选择不合理等等,这里不过多赘述。

model_vertification文件 

        网上随便找了一个狗狗的图片保存为image.png,我们想要验证该网络模型的预测效果。

        注意:

        1、 模型训练时,模型保存方式和加载该模型的方式要对应。

        2、 需要将图片更改为网络模型能够处理的shape。

完整代码:

import torch
import torchvision
from PIL import Image
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch.utils.tensorboard import SummaryWriter
from model import  *
# 打开图片,使用绝对路径
image_path = r'D:\Desktop\deep_learning\pytorch入门\images\image.png'
image = Image.open(image_path)
print(image.size)

# 保留颜色通道
image = image.convert('RGB')

# 定义图像变换
# 首先转化尺寸,再转化为tensor类型
transform = torchvision.transforms.Compose([
    torchvision.transforms.Resize((32, 32)),
    torchvision.transforms.ToTensor()
])

# 应用变换
image = transform(image)
print(f'变换后的{image.shape}')
# 增加批量维度,以匹配模型输入要求
# 图像数据,常见的输入形状是 (batch_size, channels, height, width)
# 示例说明:假设 image 是一个形状为 (3, 32, 32) 的张量,代表一张 3 通道、高度为 32、宽度为 32 的图像。
# 当调用 image.unsqueeze(0) 后,得到的新张量形状将变为 (1, 3, 32, 32),
# 这里的 1 就是新插入的批量维度,表示这个批量中只有一张图像。
# unsqueeze 只能插入一个大小为 1 的维度
image = image.unsqueeze(0)
# 或者使用这种方式来更改图像shape
# image = torch.reshape(image, (1, 3, 32, 32))

print(image.shape)



# 实例化模型
model = zzy()

# 加载模型的状态字典
model_path = r'D:\Desktop\deep_learning\model_train\zzy_9.pth'  # 确保路径正确
model.load_state_dict(torch.load(model_path))
model.eval()

# 进行前向传播
# 不要漏掉 with torch.no_grad():
# 我们的目标仅仅是根据输入数据得到模型的预测结果
# 并不需要更新模型的参数,计算梯度是不必要的开销。
with torch.no_grad():
    output = model(image)

# 获取预测的类别
# _,表示一个占位符,只关心另一个值的输出
_, predicted = torch.max(output.data, 1)

# CIFAR-10 数据集的类别名称
classes = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

# 打印预测结果
print(f"预测的类别是: {classes[predicted.item()]}")

运行结果:

        正确预测到了这个类别为dog。

        作者水平有限,有任何问题或错误,欢迎留言,我将持续分享深度学习相关的内容,你的投币点赞是我最大的创作动力!

        本文代码也可以在我的github上直接下载。https://github.com/Zik-code/CIFAR-10-model_train/tree/main/model_train。
 


http://www.kler.cn/a/534633.html

相关文章:

  • dl学习笔记(8):fashion-mnist
  • 理解 C 与 C++ 中的 const 常量与数组大小的关系
  • 【免费】2007-2019年各省科技支出占一般公共预算支出的比重数据
  • 蓝桥杯思维训练营(三)
  • 能否通过蓝牙建立TCP/IP连接来传输数据
  • 深度探索DeepSeek-R1:AI大模型的本地应用与个人知识库构建
  • 原生redis实现分布式锁
  • web 第二次作业
  • 关于Vue.js组件开发
  • 基于keepalived+GTID半同步主从复制的高可用MySQL集群
  • python学opencv|读取图像(五十七)使用cv2.bilateralFilter()函数实现图像像素双边滤波处理
  • 报错解决方案笔记01
  • 为什么使用nohup 和 启动的python脚本,日志没有在nohup.out中
  • 迅为RK3568开发板篇OpenHarmony实操HDF驱动控制LED-编写应用APP
  • 【ROS视频推流】使用web_video_server完成视频推流
  • LLMs之data:synthetic-data-generator的简介、安装和使用方法、案例应用之详细攻略
  • Ubuntu24登录PostgreSql数据库的一般方法
  • 常用的TS类型工具
  • lambda表达式写java比较器
  • deepseek技术细节1
  • 线程池如何知道一个线程的任务已经执行完成
  • 调用腾讯云批量文本翻译API翻译srt字幕
  • Text2Sql:开启自然语言与数据库交互新时代(3030)
  • postgresql-COALESCE函数、NULLIF函数、NVL函数使用
  • 腾讯云 TI 平台部署与调用DeepSeek-R1大模型的实战指南
  • docker常用基础