PyTorch MNIST手写体识别:SwanLab可视化训练

news2024/9/9 4:15:48

MNIST手写体识别是深度学习、CV的“Hello World”,无数人从这个任务入门,进入深度学习的世界~

进阶请看:PyTorch猫狗分类

这篇文章我将带大家使用SwanLab(训练可视化)、PyTorch(深度学习框架)这两个开源工具,完成从数据集准备、代码编写、到模型训练的全过程。 [图片]

代码同时适用于纯CPU、英伟达GPU或Apple M1训练。

完整代码请直接跳转到2.9节。

实验过程:SwanLab - MNIST手写体识别 数据集:百度云 提取码: 89y2 两个开源库:SwanLab、pytorch

1. 准备部分

在此之前,你需要确保你已经安装了Python。

1.1 什么是MNIST手写体识别

MNIST手写体识别任务是一个经典的计算机视觉问题,属于图像分类任务,目标是输入一个手写数字图像,AI模型可以正确预测数字是多少。 MNIST数据集包含70,000个手写数字图像,每个图像大小为28x28像素。这些图像分为两部分:60,000个训练集和10,000个测试集。 [图片]

1.2 安装Python库

需要安装下面这4个库:

shell复制代码torch
torchvision
swanlab>=0.3.2

安装命令:

复制代码pip install torch torchvision swanlab

1.3 创建文件目录

需要包含两个文件:

  1. train.py,它的作用训练模型
  2. MNIST文件夹,存放数据集。

MNIST数据集的下载有两种方式:一种是通过2.1中的代码下载(推荐),如果下载存在网络问题的话,则可以用我提供的百度云链接下载:百度云,提取码: 89y2。

[图片]

2. 训练部分

如果想直接看完整代码和效果,可直接跳转到第2.9。

2.1 载入MNIST数据集

训练部分的代码全部写在train.py中。 载入MNIST数据集分为三步:

  1. 下载MNIST数据集
  2. 将数据集分为训练集和验证集
  3. 封装到PyTorch DataLoader中,以供训练使用

下面的代码演示了这一过程:

python复制代码from torchvision.datasets import MNIST

# 下载MNIST训练集
dataset = MNIST(os.getcwd(), train=True, download=True, transform=ToTensor())

# 随机分为训练集和验证集, 训练集55000张,验证集5000张
train_dataset, val_dataset = utils.data.random_split(dataset, [55000, 5000])

# 写入Pytorch数据载入器中
train_loader = utils.data.DataLoader(train_dataset, batch_size=256, shuffle=True)
val_loader = utils.data.DataLoader(val_dataset, batch_size=1, shuffle=False)

ps:如果下载速度不给力的话,可以用我提供的百度云链接下载:百度云 提取码: 89y2。

目录结构是 根目录/MNIST/raw/...(下图所示的文件)。 在这里插入图片描述

2.2 载入ResNet18模型

模型我们直接选用经典的ResNet18,模型的具体原理本文不细说,重点放在工程实现上。 我们使用torchvision来创建1个resnet50模型,并载入预训练权重:

python复制代码import torchvision
from torchvision.models import ResNet18_Weights

model = torchvision.models.resnet18(weights=ResNet18_Weights.IMAGENET1K_V1)

因为MNIST是个1维图像和10分类任务(即输入一张图片,分类它是0~9这10个数字中的哪一个),而torchvision提供的resnet18默认是适配3维图像与1000分类,所以我们需要把模型的第一个卷积层的输入维度替换为1,全连接层的输出维度替换为10:\

python复制代码import torchvision
from torchvision.models import ResNet18_Weights
import torch

model = torchvision.models.resnet18(weights=ResNet18_Weights.IMAGENET1K_V1)

# 将模型的输入维度改为1,全连接层替换为10
model.conv1 = nn.Conv2d(1, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)
in_features = model.fc.in_features
model.fc = torch.nn.Linear(in_features, 10)

2.3 设置cuda/mps/cpu

如果你的电脑是英伟达显卡,那么cuda可以极大加速你的训练; 如果你的电脑是Macbook Apple Sillicon(M系列芯片),那么mps同样可以极大加速你的训练; 如果都不是,那就选用cpu:

python复制代码#检测是否支持mps
try:
    use_mps = torch.backends.mps.is_available()
except AttributeError:
    use_mps = False

#检测是否支持cuda
if torch.cuda.is_available():
    device = "cuda"
elif use_mps:
    device = "mps"
else:
    device = "cpu"

将模型加载到对应的device中:

python

复制代码model.to(torch.device(device))

2.4 初始化SwanLab

在训练中我们使用swanlab库作为实验管理与指标可视化工具。 SwanLab是一个类似Wandb(Weights and Biases)的在线开源训练可视化工具,能够在远程、在网页上看指标。除了能记录指标,还能自动记录训练的logging、硬件环境、Python环境、训练时间等信息。 如何入门SwanLab可以看这个文档:🚀快速开始 - SwanLab,也可以跟着本文往下走。

初始化并设置超参数 swanlab库使用swanlab.init记录超参数、实验名、实验介绍:

python复制代码import swanlab
# 初始化swanlab
run = swanlab.init(
    project="MNIST-example",
    experiment_name="ResNet18",
    config={
        "model": "ResNet18",
        "optim": "Adam",
        "lr": 1e-4,
        "batch_size": 256,
        "num_epochs": 10,
        "device": device,
    }

这里我们设置的超参数分别是:模型使用resnet18,学习率lr为1e-4,批大小batch_size为256,轮数epoch为20,分类数为10,设备为2.3检测到的设备值。 这里swanlab干了三件事:

  1. 创建了1个名为MNIST的项目
  2. 创建了1个名为ResNet18的实验,项目和实验是类似文件夹和文件的关系,每次训练都会产生一个新的实验
  3. 将超参数上传到实验中,被记录下来

跟踪训练指标 在训练MNIST的过程中,我们最关心的指标就是训练集的损失值loss和验证集的准确率acc,我们用SwanLab在记录这些指标,生成可视化的折线图。 下面是一个简单的记录loss的常见用法,具体的使用案例见2.6和2.7。

python复制代码for data in train_dataloader:
    ...
    swanlab.log({"loss": loss})

2.5 设置优化器和损失函数

设置损失函数为交叉熵损失,优化器为Adam。

python复制代码criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=run.config.lr)

交叉熵损失是常用于图像分类任务的损失函数,Adam是一种经典的优化器。

2.6 定义训练函数train

我们定义1个训练函数train:

python复制代码def train(model, device, train_dataloader, optimizer, criterion, epoch, num_epochs):
    model.train()
    # 1. 循环调用train_dataloader,每次取出1个batch_size的图像和标签
    for iter, (inputs, labels) in enumerate(train_dataloader):
        inputs, labels = inputs.to(device), labels.to(device)
        optimizer.zero_grad()
        # 2. 传入到resnet18模型中得到预测结果
        outputs = model(inputs)
        # 3. 将结果和标签传入损失函数中计算交叉熵损失
        loss = criterion(outputs, labels)
        # 4. 根据损失计算反向传播
        loss.backward()
        # 5. 优化器执行模型参数更新
        optimizer.step()
        print('Epoch [{}/{}], Iteration [{}/{}], Loss: {:.4f}'.format(epoch, num_epochs, iter + 1, len(train_dataloader),
                                                                      loss.item()))
        # 6. 每20次迭代,用SwanLab记录一下loss的变化
        if iter % 20 == 0:
            swanlab.log({"train/loss": loss.item()})

训练的逻辑很简单:我们循环调用train_dataloader,每次取出1个batch_size的图像和标签,传入到resnet18模型中得到预测结果,将结果和标签传入损失函数中计算交叉熵损失,最后根据损失计算反向传播,Adam优化器执行模型参数更新,循环往复。 在训练中我们最关心的指标是损失值loss,所以我们用swanlab.log跟踪它的变化。

2.7 定义测试函数test

我们定义1个测试函数test:

python复制代码def test(model, device, val_dataloader, epoch):
    model.eval()
    correct = 0
    total = 0
    with torch.no_grad():
        # 1. 循环调用val_dataloader,每次取出1个batch_size的图像和标签
        for inputs, labels in val_dataloader:
            inputs, labels = inputs.to(device), labels.to(device)
            # 2. 传入到resnet18模型中得到预测结果
            outputs = model(inputs)
            # 3. 获得预测的数字
            _, predicted = torch.max(outputs, 1)
            total += labels.size(0)
            # 4. 计算与标签一致的预测结果的数量
            correct += (predicted == labels).sum().item()
    
        # 5. 得到最终的测试准确率
        accuracy = correct / total
        # 6. 用SwanLab记录一下准确率的变化
        swanlab.log({"val/accuracy": accuracy}, step=epoch)

测试的逻辑同样很简单:我们循环调用test_dataloader,将验证集的图像传入到当前训练的resnet18模型中得到预测结果,与标签进行对比,计算整体的准确率。 在测试中我们最关心的指标是准确率accuracy,所以我们用swanlab.log跟踪它的变化。

2.8 保存模型权重文件

我们使用train.save进行模型的权重保存,保存到checkpoint文件夹下。

python复制代码# 如果不存在checkpoint文件夹,则自动创建一个
if not os.path.exists("checkpoint"):
    os.makedirs("checkpoint")
torch.save(model.state_dict(), 'checkpoint/latest_checkpoint.pth')

至此,我们完成已完成了绝大多数的代码,现在是时候将它们组合起来,开始训练!

2.9 完整训练代码

我们一共训练10轮,每2轮进行测试,并在最后保存权重文件:

python复制代码import os
import torch
from torch import nn, optim, utils
import torch.nn.functional as F
import torchvision
from torchvision.datasets import MNIST
from torchvision.transforms import ToTensor
from torchvision.models import ResNet18_Weights
import swanlab

def train(model, device, train_dataloader, optimizer, criterion, epoch, num_epochs):
    model.train()
    # 1. 循环调用train_dataloader,每次取出1个batch_size的图像和标签
    for iter, (inputs, labels) in enumerate(train_dataloader):
        inputs, labels = inputs.to(device), labels.to(device)
        optimizer.zero_grad()
        # 2. 传入到resnet18模型中得到预测结果
        outputs = model(inputs)
        # 3. 将结果和标签传入损失函数中计算交叉熵损失
        loss = criterion(outputs, labels)
        # 4. 根据损失计算反向传播
        loss.backward()
        # 5. 优化器执行模型参数更新
        optimizer.step()
        print('Epoch [{}/{}], Iteration [{}/{}], Loss: {:.4f}'.format(epoch, num_epochs, iter + 1, len(train_dataloader),
                                                                      loss.item()))
        # 6. 每20次迭代,用SwanLab记录一下loss的变化
        if iter % 20 == 0:
            swanlab.log({"train/loss": loss.item()})

def test(model, device, val_dataloader, epoch):
    model.eval()
    correct = 0
    total = 0
    with torch.no_grad():
        # 1. 循环调用val_dataloader,每次取出1个batch_size的图像和标签
        for inputs, labels in val_dataloader:
            inputs, labels = inputs.to(device), labels.to(device)
            # 2. 传入到resnet18模型中得到预测结果
            outputs = model(inputs)
            # 3. 获得预测的数字
            _, predicted = torch.max(outputs, 1)
            total += labels.size(0)
            # 4. 计算与标签一致的预测结果的数量
            correct += (predicted == labels).sum().item()
    
        # 5. 得到最终的测试准确率
        accuracy = correct / total
        # 6. 用SwanLab记录一下准确率的变化
        swanlab.log({"val/accuracy": accuracy}, step=epoch)
    

if __name__ == "__main__":

    #检测是否支持mps
    try:
        use_mps = torch.backends.mps.is_available()
    except AttributeError:
        use_mps = False

    #检测是否支持cuda
    if torch.cuda.is_available():
        device = "cuda"
    elif use_mps:
        device = "mps"
    else:
        device = "cpu"

    # 初始化swanlab
    run = swanlab.init(
        project="MNIST-example",
        experiment_name="ResNet18",
        config={
            "model": "ResNet18",
            "optim": "Adam",
            "lr": 1e-4,
            "batch_size": 256,
            "num_epochs": 10,
            "device": device,
        },
    )

    # 设置MNIST训练集和验证集
    dataset = MNIST(os.getcwd(), train=True, download=True, transform=ToTensor())
    train_dataset, val_dataset = utils.data.random_split(dataset, [55000, 5000])

    train_dataloader = utils.data.DataLoader(train_dataset, batch_size=run.config.batch_size, shuffle=True)
    val_dataloader = utils.data.DataLoader(val_dataset, batch_size=8, shuffle=False)

    # 初始化模型
    model =torchvision.models.resnet18(weights=ResNet18_Weights.IMAGENET1K_V1)
    # 让模型适配MNIST数据集
    model.conv1 = nn.Conv2d(1, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)
    in_features = model.fc.in_features
    model.fc = torch.nn.Linear(in_features, 10)
    model.to(torch.device(device))

    # 定义损失函数和优化器
    criterion = nn.CrossEntropyLoss()
    optimizer = optim.Adam(model.parameters(), lr=run.config.lr)

    # 开始训练和测试循环
    for epoch in range(1, run.config.num_epochs+1):
        swanlab.log({"train/epoch": epoch}, step=epoch)
        train(model, device, train_dataloader, optimizer, criterion, epoch, run.config.num_epochs)
        if epoch % 2 == 0: 
            test(model, device, val_dataloader, epoch)

    # 保存模型
    # 如果不存在checkpoint文件夹,则自动创建一个
    if not os.path.exists("checkpoint"):
        os.makedirs("checkpoint")
    torch.save(model.state_dict(), 'checkpoint/latest_checkpoint.pth')

2.10 开始训练!

实验过程可看这个SwanLab网页:SwanLab - MNIST 在开始运行时,如果你是第一次使用SwanLab,会提示你要输入SwanLab API Key。 这里的操作也非常简单,去SwanLab官网登录一下账号,在【设置】页面复制API Key,粘贴过来就可以: [图片] [图片]

然后,我们运行train.py: 在这里插入图片描述

这时候你会在看到在开头会给到你两个链接,我们点击第一个,里面包含了这个项目的信息和一个对比实验表格: 在这里插入图片描述

我们点开1个进行中的实验,会看到loss和acc整体的变化曲线: 在这里插入图片描述

切换到实验卡片,这里记录了实验的各种信息,包括超参数、最终的实验指标、实验状态、训练时长、Git仓库链接、主机名、操作系统、Python版本、硬件配置等等。 在这里插入图片描述

可以看到我们最终训练的准确率是98.66%,提高到99%以上应该不困难!可以把这个实验作为你的baseline,去尝试不同的trick来提高准确率~

在这里插入图片描述

至此我们完成了模型的训练和测试,得到了1个表现非常棒的猫狗分类模型,权重保存在了checkpoint目录下。 至此,我们完成了用SwanLab、PyTorch这两个开源工具训练1个猫狗分类模型的全部过程,更多想了解的可以参考相关链接或评论此文章。 如果有帮助,请点个赞和收藏吧~

读者福利:如果大家对大模型感兴趣,这套大模型学习资料一定对你有用

对于0基础小白入门:

如果你是零基础小白,想快速入门大模型是可以考虑的。

一方面是学习时间相对较短,学习内容更全面更集中。
二方面是可以根据这些资料规划好学习计划和方向。

资源分享

图片

大模型AGI学习包

图片

图片

资料目录

  1. 成长路线图&学习规划
  2. 配套视频教程
  3. 实战LLM
  4. 人工智能比赛资料
  5. AI人工智能必读书单
  6. 面试题合集

人工智能\大模型入门学习大礼包》,可以扫描下方二维码免费领取

1.成长路线图&学习规划

要学习一门新的技术,作为新手一定要先学习成长路线图方向不对,努力白费

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图&学习规划。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

图片

2.视频教程

很多朋友都不喜欢晦涩的文字,我也为大家准备了视频教程,其中一共有21个章节,每个章节都是当前板块的精华浓缩

图片

3.LLM

大家最喜欢也是最关心的LLM(大语言模型)

图片

人工智能\大模型入门学习大礼包》,可以扫描下方二维码免费领取

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1888677.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Hive常用的内置函数

文章目录 聚合类1.指定列值的数目2.指定列值求和3.最大值4.最小值5.平均值6.中位数函数7.分位数函数 数值类1.取整函数Round(a)2.指定精度取整ROUND(double a,int b)3.向上取整FLOOR()4.向下取整CEIL()5.随机数 rand()6.绝对值函数 日期类获取当前日期获取当前时间戳日期前后日…

【Java环境配置过程详解(包括IDEA配置Java)】

目录 一、JDK下载安装 1. 官网下载JDK 2. 本地安装JDK 3. 配置环境变量 4. 验证是否安装成功 ​编辑二、IDEA进行安装下载 1. 官网下载 IDEA 2、IDEA进行Java开发 1. 创建Java项目 2. 程序测试 一、JDK下载安装 1. 官网下载JDK 1)官网链接: https://www.o…

IT行业入门,如何假期逆袭,实现抢跑

目录 前言 1.IT行业领域分类 2.基础课程预习指南 3.技术学习路线 4.学习资源推荐 结束语 前言 IT(信息技术)行业是一个非常广泛和多样化的领域,它包括了许多不同的专业领域和职业路径。如果要进军IT行业,我们应该要明确自己…

keyclock实现三方登录

公司希望我把公司的keycloak作为新项目的一种第三方登录方式时,就像微信,google,github,使用keycloak上的账户资源 因此,先需要跟公司keyclock管理员联系,让他把各个端点url,keycloak颁发的cli…

MATLAB将两个折线图画在一个图里

界面如图 输入行数和列数,点击开始填入数据,其中第一列为x值,后面几列,每一列都是y坐标值,填好后点击画在同一张图里即可。点击置零就把所有数变成0,另外也可以选择节点样式。 .mlapp格式的文件如下 夸克…

AI时代的产品经理的成长指南_pdca循环理论制定ai学习成长计划

一、人人不都是产品经理 大多数人听到“产品经理”这个词,总会联想到“人人都是产品经理”这句话。但实际上产品经理这个岗位并没有那么简单。 用一句话概括产品经理的职责就是“帮助团队交付正确产品给用户的人”。也就是说,产品经理要能凝聚团队的力…

归并排序-MergeSort (C语言详解)

目录 前言归并排序的思想归并排序的递归法归并排序的非递归法归并排序的时间复杂度与适用场景总结 前言 好久不见, 前面我们了解到了快速排序, 那么本篇旨在介绍另外一种排序, 它和快速排序的思想雷同, 但又有区别, 这就是归并排序, 如下图, 我们对比快速排序与归并排序. 本…

在线JSON可视化工具--改进

先前发布了JSON格式化可视化在线工具,提供图形化界面显示结构关系功能,并提供JSON快速格式化、JSON压缩、快捷复制、下载导出、对存在语法错误的地方能明确显示,而且还支持全屏,极大扩大视野区域。 在线JSON格式化可视化工具 但…

[Labview] 二维数组写入表格

就一个二维数组写表,CSDN天天让我改进质量 简直是迫害完美主义 天知道Labview有什么思路好写,就一个破连连看(ˉ▽ˉ;)... 随便写点什么碎碎念占字数好了

Linux源码阅读笔记09-进程NICE案例分析1

task_nice task_nice函数功能:获取某个进程的nice值,其中nice值为进程的优先级,与静态优先级有关(nicestatic_prio-120)。 nice的取值范围:-20 ~ 19 内核源码 根据内核的注释可以知道:task_n…

时间12小时和24时转换方法

24小时时间转为12小时制 function convertTo12Hour(time24h){let [hours, minutes] time24h.split(:);let modifier 上午;if (parseInt(hours, 10) > 12) {modifier 下午;hours (parseInt(hours, 10) - 12).toString();}if (parseInt(hours, 10) 12) {modifier 下午;}…

Arduino 与树莓派常用的 IMU 传感器

惯性测量单元(IMU)是一种高度集成的传感器系统,广泛应用于需要高精度运动和姿态信息的领域。某些高精度要求下,还需要辅以温度、气压等其他传感器信息。 一、组成与功能 1. 组成 9 轴 IMU 由三个主要部分组成:3 轴加…

系留无人机+自组网+单兵图传:低空集群组网指挥系统技术详解

低空无人机集群的控制、调度、信息回传需要有高度可靠和稳定的无线通信链路来保障。我国发达的公网基础设施为上述应用创造了良好的条件,但低空应用必须要考虑到在极端情况下公网瘫痪造成的通信链路中断带来的影响,因此有必要在公网之外,寻求…

C语言----文件操作

1.为什么使用文件? 如果没有⽂件,我们写的程序的数据是存储在电脑的内存中,如果程序退出,内存回收,数据就丢失了,等再次运⾏程序,是看不到上次程序的数据的,如果要将数据进⾏持久化…

Vue85-Vuex的求和案例

一、需求 二、开发 2-1、index.js中vuex的代码 注意: 书写格式:actions中的函数名用小写!mutations中的函数名,用大写。 注意: 2-2、组件count.vue中的代码 2-3、代码优化

oracle数据库无法open,报“ORA-01113 file 1 needs media recovery”错误,教你用“六脉神剑”来开库

作者介绍:老苏,10余年DBA工作运维经验,擅长Oracle、MySQL、PG数据库运维(如安装迁移,性能优化、故障应急处理等) 公众号:老苏畅谈运维 欢迎关注本人公众号,更多精彩与您分享。在系统…

Linux:文件系统与日志分析

一、block与inode 1.1、概述 文件是存储在硬盘上的,硬盘的最小存储单位叫做“扇区”(sector),每个扇区存储512字节。 一般连续八个扇区组成一个"块”(block),一个块是4K大小,是文件存取的最小单位。 文件数据包括实际数据…

如何在 Odoo 16 中通过函数创建和管理自定义字段

Odoo 几乎为每种功能提供了每种类型的字段。通常,我们为字段定义一个类定义并将其包含在模型中。但是,在某些业务实例中,我们可能需要通过添加新字段从用户界面本身修改模型。在本博客中,让我们研究如何定义自定义字段在视图中的位…

智能数字人系统的主要功能

智能数字人系统或虚拟数字人系统,是指利用人工智能技术构建的虚拟人物形象,能够与人进行自然交互的系统。数字人系统的主要功能包括以下几个方面。北京木奇移动技术有限公司,专业的软件外包开发公司,欢迎交流合作。 1. 语言理解与…

从游戏到营销:抽卡机小程序的多维度应用探索

在数字化时代,小程序作为一种轻量级、即用即走的应用形态,正逐步渗透到人们生活的方方面面。其中,抽卡机小程序以其独特的趣味性和互动性,不仅在游戏领域大放异彩,更在营销领域展现出广阔的应用前景。本文将从游戏起源…