Pytorch从零开始实战04

news2024/12/22 20:37:49

Pytorch从零开始实战——猴痘病识别

本系列来源于365天深度学习训练营

原作者K同学

文章目录

  • Pytorch从零开始实战——猴痘病识别
    • 环境准备
    • 数据集
    • 模型选择
    • 模型训练
    • 数据可视化
    • 其他模型
    • 图片预测

环境准备

本文基于Jupyter notebook,使用Python3.8,Pytorch2.0.1+cu118,torchvision0.15.2,需读者自行配置好环境且有一些深度学习理论基础。本次实验的目的是学习模型的保存和预测单张图片的结果。
第一步,导入常用包。

import torch
import torch.nn as nn
import matplotlib.pyplot as plt
import torchvision
import torch.nn.functional as F
import torchvision.transforms as transforms
import random
import time
import numpy as np
import pandas as pd
import datetime
import gc
import pathlib
import os
import PIL
os.environ['KMP_DUPLICATE_LIB_OK']='True'  # 用于避免jupyter环境突然关闭
torch.backends.cudnn.benchmark=True  # 用于加速GPU运算的代码

创建设备对象

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

设置随机数种子

torch.manual_seed(428)
torch.cuda.manual_seed(428)
torch.cuda.manual_seed_all(428)
random.seed(428)
np.random.seed(428)

数据集

本次实验使用猴痘病图片数据集,共2142张图片,分别为有猴痘病的图片和没有猴痘病的图片,
两种类别的图片分别存在两个文件夹中。

data_dir = './data/monkeydata'
data_dir = pathlib.Path(data_dir)

data_paths = list(data_dir.glob('*'))
classNames = [str(path).split("/")[2] for path in data_paths]
classNames # ['Monkeypox', 'Others']

对数据通过dataset读取,并且将文件夹名设置为标签。

total_datadir = './data/monkeydata'
train_transforms = transforms.Compose([
    transforms.Resize([224, 224]),
    transforms.ToTensor(),
    transforms.Normalize(
        mean=[0.485, 0.456, 0.406],
        std=[0.229, 0.224, 0.225]
    )
])
total_data = torchvision.datasets.ImageFolder(total_datadir, transform=train_transforms)
total_data

在这里插入图片描述
我们可以查看所有标签

total_data.class_to_idx # {'Monkeypox': 0, 'Others': 1}

接下来划分数据集,以8比2划分训练集和测试集

# 划分数据集
train_size = int(0.8 * len(total_data))
test_size = len(total_data) - train_size
train_ds, test_ds = torch.utils.data.random_split(total_data, [train_size, test_size])
len(train_ds), len(test_ds)

随机查看5张图片

def plotsample(data):
    fig, axs = plt.subplots(1, 5, figsize=(10, 10)) #建立子图
    for i in range(5):
        num = random.randint(0, len(data) - 1) #首先选取随机数,随机选取五次
        #抽取数据中对应的图像对象,make_grid函数可将任意格式的图像的通道数升为3,而不改变图像原始的数据
        #而展示图像用的imshow函数最常见的输入格式也是3通道
        npimg = torchvision.utils.make_grid(data[num][0]).numpy()
        nplabel = data[num][1] #提取标签 
        #将图像由(3, weight, height)转化为(weight, height, 3),并放入imshow函数中读取
        axs[i].imshow(np.transpose(npimg, (1, 2, 0))) 
        axs[i].set_title(nplabel) #给每个子图加上标签
        axs[i].axis("off") #消除每个子图的坐标轴

plotsample(train_ds)

在这里插入图片描述
使用DataLoader划分批次和打乱数据集

batch_size = 32
train_dl = torch.utils.data.DataLoader(train_ds, batch_size=batch_size, shuffle=True)
test_dl = torch.utils.data.DataLoader(test_ds, batch_size=batch_size, shuffle=True)
for X, y in test_dl:
    print(X.shape) # 32, 3, 224, 224
    print(y) # 1, 0, 1, 1, 1, 1, 0....
    break
print(len(train_dl.dataset) + len(test_dl.dataset)) # 2142

模型选择

本次实验第一次选择的是一个简单的卷积神经网络,经过卷积+卷积+池化+卷积+卷积+池化+线性层,并中间进行数据归一化处理。

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

        self.conv1 = nn.Conv2d(3, 12, kernel_size=5, stride=1, padding=0)
        self.bn1 = nn.BatchNorm2d(12)
        self.conv2 = nn.Conv2d(12, 12, kernel_size=5, stride=1, padding=0)
        self.bn2 = nn.BatchNorm2d(12)
        self.pool = nn.MaxPool2d(2)
        self.conv3 = nn.Conv2d(12, 24, kernel_size=5, stride=1, padding=0)
        self.bn3 = nn.BatchNorm2d(24)
        self.conv4 = nn.Conv2d(24, 24, kernel_size=5, stride=1, padding=0)
        self.bn4 = nn.BatchNorm2d(24)

        self.fc1 = nn.Linear(24 * 50 * 50, len(classNames))

    def forward(self, x):
        x = F.relu(self.bn1(self.conv1(x)))
        x = F.relu(self.bn2(self.conv2(x)))  
        x = self.pool(x)
        x = F.relu(self.bn3(self.conv3(x)))     
        x = F.relu(self.bn4(self.conv4(x))) 
        x = self.pool(x)  
        x = x.view(-1, 24 * 50 * 50)
        x = self.fc1(x)
        return x;

使用summary查看模型

from torchsummary import summary
# 将模型转移到GPU中
model = Model().to(device)
summary(model, input_size=(3, 224, 224))

模型训练

训练函数

def train(dataloader, model, loss_fn, opt):
    size = len(dataloader.dataset)
    num_batches = len(dataloader)
    train_acc, train_loss = 0, 0

    for X, y in dataloader:
        X, y = X.to(device), y.to(device)
        pred = model(X)
        loss = loss_fn(pred, y)

        opt.zero_grad()
        loss.backward()
        opt.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

测试函数

def test(dataloader, model, loss_fn):
    size = len(dataloader.dataset)
    num_batches = len(dataloader)
    test_acc, test_loss = 0, 0
    with torch.no_grad():
        for X, y in dataloader:
            X, y = X.to(device), y.to(device)
            pred = model(X)
            loss = loss_fn(pred, y)
    
            test_acc += (pred.argmax(1) == y).type(torch.float).sum().item()
            test_loss += loss.item()

    test_acc /= size
    test_loss /= num_batches
    return test_acc, test_loss

定义一些超参数,经实验,将学习率设置为0.01效果最好。

loss_fn = nn.CrossEntropyLoss()
learn_rate = 0.01
opt = torch.optim.SGD(model.parameters(), lr=learn_rate)

开始训练,epochs设置为20,并且将训练集的最优结果保存。

import time
epochs = 20
train_loss = []
train_acc = []
test_loss = []
test_acc = []

T1 = time.time()

best_acc = 0
PATH = './my_model.pth'

for epoch in range(epochs):
    model.train()
    epoch_train_acc, epoch_train_loss = train(train_dl, model, loss_fn, opt)
    
    model.eval() # 确保模型不会进行训练操作
    epoch_test_acc, epoch_test_loss = test(test_dl, model, loss_fn)

    if epoch_test_acc > best_acc:
        best_acc = epoch_test_acc
        torch.save(model.state_dict(), PATH)
        print("model save")
        
        
    train_acc.append(epoch_train_acc)
    train_loss.append(epoch_train_loss)
    test_acc.append(epoch_test_acc)
    test_loss.append(epoch_test_loss)
    
    print("epoch:%d, train_acc:%.1f%%, train_loss:%.3f, test_acc:%.1f%%, test_loss:%.3f"
          % (epoch + 1, epoch_train_acc * 100, epoch_train_loss, epoch_test_acc * 100, epoch_test_loss))
print("Done")
T2 = time.time()
print('程序运行时间:%s毫秒' % ((T2 - T1)*1000))

可以看到,最好的时候,测试集准确率达到百分之91.8
在这里插入图片描述

数据可视化

使用matplotlib进行数据可视化。

import warnings
warnings.filterwarnings("ignore")               #忽略警告信息
plt.rcParams['font.sans-serif']    = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False      # 用来正常显示负号
plt.rcParams['figure.dpi']         = 100        #分辨率

epochs_range = range(epochs)

plt.figure(figsize=(12, 3))
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()

在这里插入图片描述

其他模型

本次实验也使用了ResNet模型,虽然参数量较大,但训练效果较好
定义模型

class Model(nn.Module):
    def __init__(self):
        super().__init__()
        
        # 创建预训练的ResNet-18模型
        self.resnet = torchvision.models.resnet18(pretrained=True)
        
        # 将ResNet的最后一层(全连接层)替换为适合二分类问题的新全连接层
        self.resnet.fc = nn.Linear(self.resnet.fc.in_features, len(classes))
        
    def forward(self, x):
        return self.resnet(x)

from torchsummary import summary
# 将模型转移到GPU中
model = Model().to(device)

经实验,把学习率设置为0.001,效果较好

import time
epochs = 50
train_loss = []
train_acc = []
test_loss = []
test_acc = []

loss_fn = nn.CrossEntropyLoss()
learn_rate = 0.001
opt = torch.optim.SGD(model.parameters(), lr=learn_rate)

T1 = time.time()

best_acc = 0
PATH = './my_model.pth'

for epoch in range(epochs):
    model.train()
    epoch_train_acc, epoch_train_loss = train(train_dl, model, loss_fn, opt)
    
    model.eval() # 确保模型不会进行训练操作
    epoch_test_acc, epoch_test_loss = test(test_dl, model, loss_fn)

    if epoch_test_acc > best_acc:
        best_acc = epoch_test_acc
        torch.save(model.state_dict(), PATH)
        print("model save")
        
        
    train_acc.append(epoch_train_acc)
    train_loss.append(epoch_train_loss)
    test_acc.append(epoch_test_acc)
    test_loss.append(epoch_test_loss)
    
    print("epoch:%d, train_acc:%.1f%%, train_loss:%.3f, test_acc:%.1f%%, test_loss:%.3f"
          % (epoch + 1, epoch_train_acc * 100, epoch_train_loss, epoch_test_acc * 100, epoch_test_loss))
print("Done")
T2 = time.time()
print('程序运行时间:%s毫秒' % ((T2 - T1)*1000))

最终在测试集的准确率可达到97.2%。
在这里插入图片描述
可视化训练过程

import warnings
warnings.filterwarnings("ignore")               #忽略警告信息
plt.rcParams['font.sans-serif']    = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False      # 用来正常显示负号
plt.rcParams['figure.dpi']         = 100        #分辨率

epochs_range = range(epochs)

plt.figure(figsize=(12, 3))
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()

在这里插入图片描述

图片预测

img_path:要进行预测的图像文件的路径。
model:用于进行图像分类预测的深度学习模型。
transform:用于对图像进行预处理的数据转换函数。
classes:包含类别标签的列表,用于将模型的输出索引映射回类别标签。
大致意思是图像与训练时的输入数据格式相匹配,模型接受批量输入,因此我们需要在维度上添加一个批次维度,从而进行预测

classes = list(total_data.class_to_idx)
def predict_img(img_path, model, transform, classes):
    test_img = Image.open(img_path).convert('RGB')
    test_img = transform(test_img)
    img = test_img.to(device).unsqueeze(0)
    model.eval()
    output = model(img)
    _, pred = torch.max(output, 1) # 在张量的第一个维度上取最大值操作
    pred_class = classes[pred]
    print(f'预测结果是:{pred_class}')

开始预测

predict_img(img_path='./data/monkeydata/Monkeypox/M01_01_00.jpg', 
                  model=model, 
                  transform=train_transforms, 
                  classes=classes)
# 预测结果是:Monkeypox

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

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

相关文章

mysql用事务实现更新数据

前言:在手动批量更新正式环境数据库时,建议使用事物进行更新,避免更错数据,造成不必要的麻烦。 现表中有三条数据,使用事物批量将name字段为mgx,phone字段为17837107346,所有数据中的address字段…

华为云云耀云服务器L实例评测|服务器反挖矿防护指南

前言 本文为华为云云耀云服务器L实例测评文章,测评内容是 云耀云服务器L实例 反挖矿防护指南 系统配置:2核2G 3M CentOS7.9 之前的文章中『一文教你如何防御数据库恶意攻击』,我们讲到黑客如何通过攻击数据库来获取权限,以及我们…

别再纠结线程池池大小、线程数量了,哪有什么固定公式 | 京东云技术团队

可能很多人都看到过一个线程数设置的理论: CPU 密集型的程序 - 核心数 1 I/O 密集型的程序 - 核心数 * 2 不会吧,不会吧,真的有人按照这个理论规划线程数? 线程数和CPU利用率的小测试 抛开一些操作系统,计算机原…

C++数据结构X篇_14_二叉树的递归遍历(先序遍历、中序遍历、后续遍历方法介绍;举例;代码实现)

我们知道数据的存储结构分为线性与非线性。线性就是1对1的结构,像栈与队列都属于线性结构。那什么是非线性的结构呢? 非线性即1对n的结构这更符合常规情况,线性结构本质上属于非线性结构中的一种特殊形式,像树就属于非线性结构。但…

iOS 17 Simulator Failed with HTTP status 400:bad request

升级 xcode 15 要 ios17 的 sdk 才能运行,但是更新这个 sdk 400 错误了 解决方案: 直接去官网下载开发者后台下载dmg文件,使用命令行快速安装即可 https://developer.apple.com/documentation/xcode/installing-additional-simulator-runti…

Anaconda和Pycharm详细安装 配置教程

Anaconda:是一个开源的Python发行版本,其中包含了conda、Python等180多个科学包及其依赖项。【Anaconda下载】 PyCharm:PyCharm是一种Python IDE,带有一整套可以帮助用户在使用Python语言开发时提高其效率的工具。【PyCharm下载】…

功能强大的PostgreSQL没有MySQL流行的10个原因,你知道几个?

以下内容是根据玖章算术CEO叶正盛在「3306π 数据库朋友圈」技术演讲整理。文末附完整的PPT下载链接! MySQL与PostgreSQL都是非常优秀并且非常成功的数据库,PostgreSQL的功能比MySQL强大,但是依然没有MySQL流行,作者从产品功能、…

Vue3中的computed函数详解

计算属性是Vue中常用的一种方式,主要用于在模板中放置逻辑计算,方便开发者进行数据操作和展示。在Vue3中,计算属性依然是非常重要的一种功能,而computed函数则更加的方便计算属性的使用。本文将对Vue3中的computed函数进行详细的介…

terraform简单的开始-vpc cvm创建

从网络开始 从创建VPC开始 复用前面的main.tf的代码: terraform {required_providers {tencentcloud {source "tencentcloudstack/tencentcloud"version "1.81.25"}} } variable "region" {description "腾讯云地域"…

西门子S7-1200F或1500F系列安全PLC的组态步骤和基础编程(二)

西门子S7-1200F或1500F系列安全PLC的组态步骤和基础编程(二) 上一次和大家分享了组态相关的重要内容,具体可以参考以下链接中的内容: 西门子S7-1200F或1500F系列安全PLC的组态步骤和基础编程(一) 本次继续和大家分享关于安全PLC基础编程的相关内容: 如下图所示,在左侧的…

开通经营收款码要手续费吗

我们都知道,目前市场上的支付宝或者微信商户收款,无论是线上收款还是实体店收款,一般都采用0.6%的收款费率,1万元就是60元。 其实这不低的。 大多数线下实体店商家可能使用的聚合支付码可能是0.38%,1万元是38。 虽然不…

21天学会C++:Day12----初始化列表

CSDN的uu们,大家好。这里是C入门的第十一讲。 座右铭:前路坎坷,披荆斩棘,扶摇直上。 博客主页: 姬如祎 收录专栏:C专题 目录 1. 初始化列表 1.1 引入 1.2 初始化列表 1.3 初始化列表的注意事项 1.…

字符集(ASCII、GBK、Unicode、UTF-8)

1、字符集基础知识 计算机底层不可以直接存储字符的。计算机中底层只能存储二进制(0、1)二进制是可以转换成十进制的结论:计算机底层可以表示十进制编号。计算机可以给人类字符进行编号存储,这套编号规则就是字符集。 2、ASCII字符…

如何像微信一样扫码自由?

https://github.com/devilsen/CZXing 我在使用zxing的总是想扫码的时候怎么才能够快速校准,多个二维码扫描的时候怎么才能指定二维码呢。于是我在Github上找到了CZxing这个控件。在使用的时候发现了有些小问题。 扫码结果没有回调。 回调没有反应;是因…

二维码智慧门牌管理系统:提升城市管理效率与服务水平

文章目录 前言一、系统原理及特点二、系统的优势与应用 前言 在当今快速发展的信息化时代,如何有效地管理城市地址信息成为了各大城市面临的重要问题。传统的门牌管理系统已经无法满足现代城市的需求,而二维码智慧门牌管理系统作为全新的解决方案&#…

数据库开发-MySQL

数据库设计-DDL 下面我们就正式的进入到SQL语句的学习,在学习之前先给大家介绍一下我们要开发一个项目,整个开发流程是什么样的,以及在流程当中哪些环节会涉及到数据库。 项目开发流程 需求文档: 在我们开发一个项目或者项目当中…

Linux文件内容显示练习

1.新建2个文件b1.txt b2.txt ,使用vim打开b1.txt 输入“Hello World”字符串,将b1.txt硬链接到b2.txt 查看2个文件的硬连接数 [rootserver ~]# vim b1.txt [rootserver ~]# ln b1.txt b2.txt #建立硬链接 [rootserver ~]# stat b2.txt [rootserver ~]# stat b1.txt [r…

现货黄金的价格如何变动

现货黄金每天的交易时间很长,价格几乎全天24小时都处于波动之中,由于受到各种政治、经济因素的影响,价格波动有时可以来得十分迅猛,在一小时就可以波动二、三十美元,但有时却可以连续几天都维持在数美元的区间内波动。…

《使用 sCrypt 构建井字游戏》课程上线

《使用 sCrypt 构建井字游戏》课程上线 Learn sCrypt。Learn sCrypt 是一个交互式学习网站,旨在帮助开发者更快、更轻松地学习和掌握比特币智能合约开发语言 sCrypt。 井字游戏非常简单,就是使用两个玩家(分别是 Alice 和 Bob)的比特币地址初始化合约&a…

Spring框架——介绍与基本概念!

一、Spring框架概述 1.什么是Spring Spring是一个轻量级的Java 开发开源框架,用于构建企业级应用程序。它提供了一组广泛使用的技术和API,包括依赖注入、AOP、数据访问、事务管理、Web开发和集成测试等。它是为了解决企业应用开发的复杂性而创建的。框…