2.猴痘识别

📌 本篇文章来源:K同学的一对一辅导教案
🍺要求:

  1. 训练过程中保存效果最好的模型参数。
  2. 加载最佳模型参数识别本地的一张图片。
  3. 调整网络结构使测试集accuracy到达88%(重点)。

🍻拔高:

  1. 调整模型参数并观察测试集的准确率变化。
  2. 尝试设置动态学习率。
  3. 测试集accuracy到达90%。

一、前期准备

如果设备上支持GPU就使用GPU,否则使用CPU

1.设置GPU

import torch
import torch.nn as nn
import torchvision.transforms as transforms
import torchvision
from torchvision import transforms, datasets

import os,PIL,pathlib

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

device

输出:.
device(type=‘cuda’)

2.导入数据

*导入的数据解压后是两个文件夹,放在新建的data中
** pathlib是python内置库,python文档给的定义是The pathlib module – object-oriented filesystem paths(面向对象的文件系统路径)。pathlib 提供表示文件系统路径的类,其语义适用于不同的操作系统。

import os,PIL,random,pathlib   
"""
1.os:opration system 操作系统,导入os模块,可以高效处理文件和文件夹
2.pil是python中较为基础的图形处理库,主要用于图像的基本处理,比如裁剪图像、调整图像大小和图像颜色处理等。OPencv和scikit-image的功能更为丰富
3.random()方法返回随机生成的一个实数,它在[0,1)范围内。
4.pathlib是跨平台的、面向对象的路径操作模块,可适用于不同的操作系统,其操作对象是各种操作系统中使用的路径(包括决定路径和相对路径),
5.pathlib有两个主要的类(purepath和path)。参考https://zhuanlan.zhihu.com/p/475661402
"""

data_dir = './4-data/'              # 对字符串'./4-data/'查询 ,需要建立文件夹,才能检索到,输出的结果是文件夹夹内文件 
data_dir = pathlib.Path(data_dir)   # 创建path对象

data_paths = list(data_dir.glob('*'))
# glob是可以查找符合自己目的的文件,类似于windows下的文件搜索,支持通配符操作(*?[]),
# *代表0个或多个字符,?代表一个字符,[]匹配指定范围内的字符,如[0-9]匹配数字,
# https://blog.csdn.net/qq_27586341/article/details/104693755?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-104693755-blog-76857493.pc_relevant_multi_platform_whitelistv3&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-104693755-blog-76857493.pc_relevant_multi_platform_whitelistv3&utm_relevant_index=2
classeNames = [str(path).split("\\")[1] for path in data_paths]   # 按此格式进行文件分类
classeNames

输出:
[‘Monkeypox’, ‘Others’]

torchvision.transforms.Compose()详解

torchvision是pytorch的一个图形库,它服务于PyTorch深度学习框架的,主要用来构建计算机视觉模型。torchvision.transforms主要是用于常见的一些图形变换。以下是torchvision的构成:
1.torchvision.datasets: 一些加载数据的函数及常用的数据集接口;
2.torchvision.models: 包含常用的模型结构(含预训练模型),例如AlexNet、VGG、ResNet等;
3.torchvision.transforms: 常用的图片变换,例如裁剪、旋转等;
4.torchvision.utils: 其他的一些有用的方法。

torchvision.transforms.Compose()类。这个类的主要作用是串联多个图片变换的操作。

torchvision.transforms.Compose()类 使用规则

"""
#  torchvision.transforms.Compose()类  使用规则
from torchvision.transforms import transforms

train_transforms = transforms.Compose([
    transforms.Resize([224, 224]),                  # 将输入图片resize成统一尺寸
    transforms.RandomRotation(degrees=(-10, 10)),   # 随机旋转,-10到10度之间随机选
    transforms.RandomHorizontalFlip(p=0.5),         # 随机水平翻转 选择一个概率概率
    transforms.RandomVerticalFlip(p=0.5),           # 随机垂直翻转
    transforms.RandomPerspective(distortion_scale=0.6, p=1.0),    # 随机视角
    transforms.GaussianBlur(kernel_size=(5, 9), sigma=(0.1, 5)),  # 随机选择的高斯模糊模糊图像
    transforms.ToTensor(),          # 将PIL Image或numpy.ndarray转换为tensor,并归一化到[0,1]之间
    transforms.Normalize(           # 标准化处理-->转换为标准正太分布(高斯分布),使模型更容易收敛
        mean=[0.485, 0.456, 0.406], 
        std = [0.229, 0.224, 0.225])  # 其中 mean=[0.485,0.456,0.406]与std=[0.229,0.224,0.225] 从数据集中随机抽样计算得到的。
])
"""
total_datadir = './4-data/'

# 关于transforms.Compose的更多介绍可以参考:https://blog.csdn.net/qq_38251616/article/details/124878863
train_transforms = transforms.Compose([
    transforms.Resize([224, 224]),  # 将输入图片resize成统一尺寸
    transforms.ToTensor(),          # 将PIL Image或numpy.ndarray转换为tensor,并归一化到[0,1]之间
    transforms.Normalize(           # 标准化处理-->转换为标准正太分布(高斯分布),使模型更容易收敛
        mean=[0.485, 0.456, 0.406], 
        std=[0.229, 0.224, 0.225])  # 其中 mean=[0.485,0.456,0.406]与std=[0.229,0.224,0.225] 从数据集中随机抽样计算得到的。
])
"""
如何抽样计算得到mean和std
mean (sequence): Sequence of means for each channel. 每个通道的均值序列
std (sequence): Sequence of standard deviations for each channel. 每个通道的标准偏差序列
inplace(bool,optional): Bool to make this operation in-place.
"""
total_data = datasets.ImageFolder(total_datadir,transform=train_transforms)
# total_datadir为根目录,transform对图片进行预处理操作(函数),train_transforms作为targets输入,输出对其转换。
# imageFoleder使用要求:假设所有的文件按文件夹保存好,每个文件夹下面存储同一类别的图片,文件夹的名字为分类的名字。
# ImageFolder解释:https://blog.csdn.net/weixin_43135178/article/details/115139178

total_data
"""
ImageFolder是一个通用的数据加载器,他要求我们使用下面格式组织数据集的训练、验证或者测试图片
root/dog/xxx.png
root/dog/xxy.png
root/dog/xxz.png
 
root/cat/123.png
root/cat/nsdf3.png
root/cat/asd932_.png



dataset=torchvision.datasets.ImageFolder(
                       root, transform=None, 
                       target_transform=None, 
                       loader=<function default_loader>, 
                       is_valid_file=None)
参数详解:
root:图片存储的根目录,即各类别文件夹所在目录的上一级目录。
transform:对图片进行预处理的操作(函数),原始图片作为输入,返回一个转换后的图片。
target_transform:对图片类别进行预处理的操作,输入为 target,输出对其的转换。如果不传该参数,即对 target 不做任何转换,返回的顺序索引 0,1, 2…
loader:表示数据集加载方式,通常默认加载方式即可。
is_valid_file:获取图像文件的路径并检查该文件是否为有效文件的函数(用于检查损坏文件)
————————————————
返回的dateset都有以下三种属性:
self.classes:用一个 list 保存类别名称
self.class_to_idx:类别对应的索引,与不做任何转换返回的 target 对应
self.imgs:保存(img-path, class) tuple的 list

   print(dataset.classes)  #根据分的文件夹的名字来确定的类别
   print(dataset.class_to_idx) #按顺序为这些类别定义索引为0,1...
   print(dataset.imgs) #返回从所有文件夹中得到的图片的路径以及其类别
'''
输出:
['cat', 'dog']
{'cat': 0, 'dog': 1}
[('./data/train\\cat\\1.jpg', 0), 
 ('./data/train\\cat\\2.jpg', 0), 
 ('./data/train\\dog\\1.jpg', 1), 
 ('./data/train\\dog\\2.jpg', 1)]
'''
原文链接:https://blog.csdn.net/weixin_43135178/article/details/115139178
"""

在这里插入图片描述

total_data.class_to_idx    # 按照分的dataw文件夹的名字顺序为这些data文件夹类别定义索引为0,1

{‘Monkeypox’: 0, ‘Others’: 1}

3.划分数据集

train_size = int(0.8 * len(total_data))  # len()返回对象的长度参数可以是序列(字符串str、元组tuple、列表list)或集合(字典dict、集合set或冻结集合frozenset)
test_size  = len(total_data) - train_size
train_dataset, test_dataset = torch.utils.data.random_split(total_data, [train_size, test_size])
train_dataset, test_dataset

输出:
(<torch.utils.data.dataset.Subset at 0x16dac6e3fd0>,
<torch.utils.data.dataset.Subset at 0x16dac6e3e50>)

train_size,test_size

输出:
(1713, 429)

batch_size = 32 # 一次训练取32个样本数,影响模型的优化速度和程度,内存不大可往小修改
# 在train_dataset加载训练集
train_dl = torch.utils.data.DataLoader(train_dataset,      # 加载的数据集
                                           batch_size=batch_size,    # mini batch size
                                           shuffle=True,   # 要不要打乱数据
                                           num_workers=1)  # 单线程读取数据,0表示数据将在主进程中加载
# 在train_dataset加载测试集                                           
test_dl = torch.utils.data.DataLoader(test_dataset,
                                          batch_size=batch_size,
                                          shuffle=True,
                                          num_workers=1)
# DataLoader就是数据加载器,结合了数据集和取样器,并且可以提供多个线程处理数据集。
# # 在训练模型时使用到此函数,用来把训练数据分成多个小组,此函数每次抛出一组数据。直至把所有的数据都抛出。就是做一个数据的初始化。lesson 1中包含此知识点。
# 取一个批次查看数据格式
# 数据的shape为:[batch_size, channel, height, weight]
# 其中batch_size为自己设定,channel,height和weight分别是图片的通道数,高度和宽度。
for X, y in test_dl:
    print("Shape of X [N, C, H, W]: ", X.shape)
    print("Shape of y: ", y.shape, y.dtype)
    break

输出:
Shape of X [N, C, H, W]: torch.Size([32, 3, 224, 224])
Shape of y: torch.Size([32]) torch.int64

二、构建简单的CNN网络

import torch.nn.functional as F

class Network_bn(nn.Module):
    def __init__(self):
        super(Network_bn, self).__init__()
        """
        nn.Conv2d()函数:https://blog.csdn.net/qq_38251616/article/details/114278995
        第一个参数(in_channels)是输入的channel数量
        第二个参数(out_channels)是输出的channel数量
        第三个参数(kernel_size)是卷积核大小
        (输出矩阵格式),与输出矩阵的维度顺序和含义相同,但是后三个维度(图像高度、图像宽度、图像通道数)的尺寸发生变化。(batch_size, height, width, depth)
        (输入矩阵格式),输入矩阵格式:四个维度,依次为:样本数、图像高度、图像宽度、图像通道数(batch_size,height,width,depth)
        (权重矩阵(卷积核)格式),同样是四个维度,但维度的含义与上面两者都不同,为:卷积核高度、卷积核宽度、输入通道数、输出通道数(卷积核个数)。
        第四个参数(stride)是步长,默认为1
        第五个参数(padding)是填充大小,默认为0
        """
        self.conv1 = nn.Conv2d(in_channels=3, out_channels=12, kernel_size=5, stride=1, padding=0)  # 卷积层1(输入通道数为3,输出通道12,卷积核5,步长1,填充大小0)
        self.bn1 = nn.BatchNorm2d(12)
        """
        BatchNorm2d函数原型:
        torch.nn.BatchNorm2d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
        卷积层之后总会添加BatchNorm2d进行数据的归一化处理,这使得数据在进行Relu之前不会因为数据过大而导致网络性能的不稳定。
        参数解释:
        num_features:一般输入参数为batch_size×num_features×height×width,即为其中特征的数量
        eps:分母中添加的一个值,目的是为了计算的稳定性,默认为:1e-5
        momentum:一个用于运行过程中均值和方差的一个估计参数
        affine:当设为true时,会给定可以学习的系数矩阵gamma和beta

        """
        self.conv2 = nn.Conv2d(in_channels=12, out_channels=12, kernel_size=5, stride=1, padding=0)
        self.bn2 = nn.BatchNorm2d(12)
        self.pool = nn.MaxPool2d(2,2)   # 常见的池化操作有最大池化和平均池化,池化层是由n×n大小的矩阵窗口滑动来进行计算的,类似于卷积层,只不过不是做互相关运算,而是求n×n大小的矩阵中的最大值和平均值。
        self.conv4 = nn.Conv2d(in_channels=12, out_channels=24, kernel_size=5, stride=1, padding=0)
        self.bn4 = nn.BatchNorm2d(24)
        self.conv5 = nn.Conv2d(in_channels=24, out_channels=24, kernel_size=5, stride=1, padding=0)
        self.bn5 = nn.BatchNorm2d(24)
        """
        # 补充更改卷积和池化
        self.conv6 = nn.Conv2d(in_channels=24, out_channels=48, kernel_size=5, stride=1, padding=0)
        self.bn6 = nn.BatchNorm2d(48)
        self.conv7 = nn.Conv2d(in_channels=48, out_channels=48, kernel_size=5, stride=1, padding=0)
        self.bn7 = nn.BatchNorm2d(48)
        self.fc1 = nn.Linear(48*21*21, len(classeNames))#(3*224*224)-(12*n*)
        # 补充结束
        """
        self.fc1 = nn.Linear(24*50*50, len(classeNames))

# 前向传播
    def forward(self, x):
        x = F.relu(self.bn1(self.conv1(x)))      # 卷积——BN——激活  [原文作者认为先BN再激活的顺序,更易产生良好效果](https://zhuanlan.zhihu.com/p/580661299#:~:text=%E4%B8%89%E3%80%81BN%E5%B1%82%E6%98%AF%E5%9C%A8%E6%BF%80%E6%B4%BB%E5%87%BD%E6%95%B0%E4%B9%8B%E5%89%8D%E8%BF%98%E6%98%AF%E4%B9%8B%E5%90%8E%E4%BD%BF%E7%94%A8%EF%BC%9F%20%E5%A6%82%E6%9E%9C%E6%88%91%E4%BB%AC%E5%9C%A8%E6%BF%80%E6%B4%BB%E5%87%BD%E6%95%B0%E4%B9%8B%E5%89%8D%E5%A2%9E%E5%8A%A0BN%E5%B1%82%EF%BC%8C%E9%82%A3%E4%B9%88%E6%88%91%E4%BB%AC%E5%8F%AF%E4%BB%A5%E5%AE%8C%E6%95%B4%E5%9C%B0%E5%88%A9%E7%94%A8%E6%BF%80%E6%B4%BB%E5%87%BD%E6%95%B0%E7%9A%84%E8%BE%93%E5%87%BA%E3%80%82%20%E4%BE%8B%E5%A6%82%EF%BC%8C%20Sigmoid,%E5%8F%AF%E4%BB%A5%E5%B0%86BN%E7%9A%84%E8%BE%93%E5%87%BA%E9%99%90%E5%88%B6%E5%9C%A80-1%E7%9A%84%E8%8C%83%E5%9B%B4%E5%86%85%E3%80%82%20%E8%BF%99%E7%A7%8D%E6%96%B9%E5%BC%8F%E4%B9%9F%E6%9C%89%E5%8E%9F%E4%BD%9C%E8%80%85%E7%9A%84%E8%A7%A3%E9%87%8A%EF%BC%88%E4%B9%9F%E6%98%AF%E5%8E%9F%E4%BD%9C%E8%80%85%E6%8E%A8%E8%8D%90%E7%9A%84%E6%96%B9%E5%BC%8F%EF%BC%89%EF%BC%9A%20%E6%84%8F%E6%80%9D%E6%98%AF%E5%9C%A8%E6%BF%80%E6%B4%BB%E5%87%BD%E6%95%B0%E4%B9%8B%E5%89%8D%E4%BD%BF%E7%94%A8BN%E5%8F%AF%E4%BB%A5%E8%BE%93%E5%87%BA%E6%9B%B4%E5%8A%A0%E7%A8%B3%E5%AE%9A%E7%9A%84%E5%88%86%E5%B8%83%E7%BB%93%E6%9E%9C%EF%BC%8C%E8%AE%A9%E6%BF%80%E6%B4%BB%E5%87%BD%E6%95%B0%E6%9B%B4%E5%A5%BD%E5%9C%B0%E5%8F%91%E6%8C%A5%E4%BD%9C%E7%94%A8%E3%80%82%20%E4%BD%86%E6%98%AF%EF%BC%8C%E5%A6%82%E6%9E%9C%E5%9C%A8%E6%BF%80%E6%B4%BB%E5%87%BD%E6%95%B0%E4%B9%8B%E5%90%8E%E4%BD%BF%E7%94%A8BN%E5%8F%AF%E4%BB%A5%E4%BD%BF%E5%BE%97%E4%B8%8B%E4%B8%80%E5%B1%82%E7%BD%91%E7%BB%9C%E7%9A%84%E8%BE%93%E5%85%A5%E6%98%AF%E4%B8%80%E4%B8%AAnormalized%E7%9A%84%E8%BE%93%E5%85%A5%EF%BC%8C%E5%8F%AF%E4%BB%A5%E8%AE%A9%E6%89%80%E6%9C%89%E7%9A%84%E5%80%BC%E9%83%BD%E5%9C%A8%E7%9B%B8%E5%90%8C%E7%9A%84%E5%B0%BA%E5%BA%A6%E8%8C%83%E5%9B%B4%E5%86%85%E3%80%82)
        x = F.relu(self.bn2(self.conv2(x)))      # 卷积——BN——激活  
        x = self.pool(x)                         # 池化  
        x = F.relu(self.bn4(self.conv4(x)))      # 卷积——BN——激活  
        x = F.relu(self.bn5(self.conv5(x)))      # 卷积——BN——激活  
        x = self.pool(x)                         # 池化
       """
        # 补充激活函数
        x = F.relu(self.bn6(self.conv6(x)))  
        x = F.relu(self.bn7(self.conv7(x)))
        x = self.pool(x)
        x = x.view(-1, 48*21*21)  
        # 补充结束
       """
        x = x.view(-1, 24*50*50)      
        x = self.fc1(x)

        return x
# 设置GPU
device = "cuda" if torch.cuda.is_available() else "cpu"
print("Using {} device".format(device))

model = Network_bn().to(device)
model

输出结果:
Using cuda device
Network_bn(
(conv1): Conv2d(3, 12, kernel_size=(5, 5), stride=(1,1))
(bn1): BatchNorm2d(12, eps=1e-05, momentum=0.1, affine=True,track_running_stats=True)
(conv2): Conv2d(12, 12, kernel_size=(5,5), stride=(1, 1))
(bn2): BatchNorm2d(12, eps=1e-05, momentum=0.1,affine=True, track_running_stats=True
(pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1,ceil_mode=False)
(conv4): Conv2d(12, 24, kernel_size=(5, 5),stride=(1, 1))
(bn4): BatchNorm2d(24, eps=1e-05, momentum=0.1,affine=True, track_running_stats=True)
(conv5): Conv2d(24, 24, kernel_size=(5, 5), stride=(1, 1))
(bn5): BatchNorm2d(24, eps=1e-05,momentum=0.1, affine=True, track_running_stats=True)
(fc1): Linear(in_features=60000, out_features=2, bias=True)
)

三、训练模型

1.设置超参数

loss_fn    = nn.CrossEntropyLoss() # 创建损失函数
learn_rate = 1e-4 # 学习率
opt        = torch.optim.SGD(model.parameters(),lr=learn_rate)
 # 返回一个优化器类,lr表示学习类,Parameters表示参数
 # class.torch.optim.SGB(param,lr=,momentum=0, dampening=0,weight_decay=0,nesterov=false)
 # optim.SGB()是pytorch中的随机梯度下降算法优化器,它可以调整神经网络中的参数以最小化损失函数。它通过迭代每个参数,以最小化损失函数,基本思想是,在每次迭代中,每个参数都会沿着梯度的反方向移动一小步,以期望最小化损失函数
"""
# 动态学习率设置
# lambda1 = lambda epoch: (0.5 ** (epoch // 2)  # 每两个epoch学习率下降到原来的0.5倍
learn_rate = 1e-4
optimizer = torch.optim.SGD(model.parameters(), lr=learn_rate)
scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda=lambda epoch: (0.5 ** (epoch // 4)))
# 原文链接:https://blog.csdn.net/zxhy_/article/details/129104674
"""

2.编写训练函数

# 训练循环
def train(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)  # 训练集的大小,一共60000张图片
    num_batches = len(dataloader)   # 批次数目,1875(60000/32)

    train_loss, train_acc = 0, 0  # 初始化训练损失和正确率
    
    for X, y in dataloader:  # 获取图片及其标签
        X, y = X.to(device), y.to(device)
        
        # 计算预测误差
        pred = model(X)          # 网络输出
        loss = loss_fn(pred, y)  # 计算网络输出和真实值之间的差距,targets为真实值,计算二者差值即为损失
        
        # 反向传播
        optimizer.zero_grad()  # grad属性归零
        loss.backward()        # 反向传播
        optimizer.step()       # 每一步自动更新
        
        # 记录acc与loss
        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.编写测试函数

测试函数和训练函数大致相同,但是由于不进行梯度下降对网络权重进行更新,所以不需要传入优化器

def test (dataloader, model, loss_fn):
    size        = len(dataloader.dataset)  # 测试集的大小,一共10000张图片
    num_batches = len(dataloader)          # 批次数目,313(10000/32=312.5,向上取整313)
    test_loss, test_acc = 0, 0
    
    # 当不进行训练时,停止梯度更新,节省计算内存消耗
    with torch.no_grad():   # 该句柄表明当前计算不需要反向传播,使用后,强制后边的内容不进行计算图的构建
        for imgs, target in dataloader:
            imgs, target = imgs.to(device), target.to(device)
            
            # 计算loss
            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

4.正式训练

epochs     = 20   # 训练20轮次
# train_loss ;train_acc ;test_loss ; test_acc 初始化
train_loss = []
train_acc  = []
test_loss  = []
test_acc   = []

for epoch in range(epochs):
    model.train()   # 启用 Batch Normalization 和 Dropout。
    epoch_train_acc, epoch_train_loss = train(train_dl, model, loss_fn, opt)       # def train(dataloader, model, loss_fn, optimizer):
    
    model.eval()    # 不启用 Batch Normalization 和 Dropout。
    epoch_test_acc, epoch_test_loss = test(test_dl, model, loss_fn)

    # append用于在列表末尾添加新的对象(epoch_train_acc),append()函数无返回值,但是会修改原本的列表
    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')
# format作为Python格式的字符串函数,主要通过字符串中的花括号{},来识别替换字段。从而完成字符串的格式化

输出结果:
Epoch: 1, Train_acc:60.8%, Train_loss:0.655, Test_acc:60.6%,Test_loss:0.668
Epoch: 2, Train_acc:70.2%, Train_loss:0.575, Test_acc:72.7%,Test_loss:0.560
Epoch: 3, Train_acc:74.5%, Train_loss:0.527, Test_acc:71.3%,Test_loss:0.549
Epoch: 4, Train_acc:78.4%, Train_loss:0.483, Test_acc:73.4%,Test_loss:0.519
Epoch: 5, Train_acc:81.1%, Train_loss:0.448, Test_acc:74.4%,Test_loss:0.536
Epoch: 6, Train_acc:82.4%, Train_loss:0.424, Test_acc:76.2%,Test_loss:0.480
Epoch: 7, Train_acc:84.6%, Train_loss:0.402, Test_acc:78.3%,Test_loss:0.474
Epoch: 8, Train_acc:83.9%, Train_loss:0.390, Test_acc:78.3%,Test_loss:0.455
Epoch: 9, Train_acc:85.5%, Train_loss:0.372, Test_acc:78.8%,Test_loss:0.444
Epoch:10, Train_acc:86.5%, Train_loss:0.359, Test_acc:78.8%,Test_loss:0.440
Epoch:11, Train_acc:88.1%, Train_loss:0.345, Test_acc:81.4%,Test_loss:0.420
Epoch:12, Train_acc:88.7%, Train_loss:0.329, Test_acc:81.1%,Test_loss:0.423
Epoch:13, Train_acc:89.3%, Train_loss:0.320, Test_acc:79.5%,Test_loss:0.405
Epoch:14, Train_acc:90.3%, Train_loss:0.304, Test_acc:81.4%,Test_loss:0.404
Epoch:15, Train_acc:90.3%, Train_loss:0.296, Test_acc:80.9%,Test_loss:0.394
Epoch:16, Train_acc:91.6%, Train_loss:0.283, Test_acc:81.4%,Test_loss:0.404
Epoch:17, Train_acc:92.5%, Train_loss:0.271, Test_acc:82.3%,Test_loss:0.389
Epoch:18, Train_acc:91.4%, Train_loss:0.271, Test_acc:83.0%,Test_loss:0.382
Epoch:19, Train_acc:92.6%, Train_loss:0.260, Test_acc:83.7%,Test_loss:0.381
Epoch:20, Train_acc:92.1%, Train_loss:0.260, Test_acc:82.3%,Test_loss:0.396
Done

四、结果可视化

1.Loss与Accurary图

import matplotlib.pyplot as plt
#隐藏警告
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.figure(figsize=(a, b)),figsize设置图形大小,a为图形的宽,b为图形的高,单位为英寸
plt.subplot(1, 2, 1)        # 可以将一组图放在一起进行比较, subplot(numRows, numCols, plotNum)

plt.plot(epochs_range, train_acc, label='Training Accuracy')   # plt.plot(x轴,y轴,标签=标签名称)
plt.plot(epochs_range, test_acc, label='Test Accuracy')
plt.legend(loc='lower right')   # legend图例(位置=右下),legend的位置主要通过函数的loc参数来设置,loc可以取的值可以是string,也可以是数字,lower right也可以写成4
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()

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

2.指定图片进行预测

⭐torch.squeeze()

详解 :对数据的维度进行压缩,去掉维数为1的的维度
函数原型:torch.squeeze(input, dim=None, *, out=None)

关键参数说明:
● input (Tensor):输入Tensor
● dim (int, optional):如果给定,输入将只在这个维度上被压缩

"""
>>> x = torch.zeros(2, 1, 2, 1, 2)
>>> x.size()
torch.Size([2, 1, 2, 1, 2])
>>> y = torch.squeeze(x)
>>> y.size()
torch.Size([2, 2, 2])
>>> y = torch.squeeze(x, 0)
>>> y.size()
torch.Size([2, 1, 2, 1, 2])
>>> y = torch.squeeze(x, 1)
>>> y.size()
torch.Size([2, 2, 1, 2])
"""

⭐torch.unsqueeze()

详解: 对数据维度进行扩充。给指定位置加上维数为一的维度

函数原型: torch.unsqueeze(input, dim)

关键参数说明:
● input (Tensor):输入Tensor
● dim (int):插入单例维度的索引

"""
>>> x = torch.tensor([1, 2, 3, 4])
>>> torch.unsqueeze(x, 0)
tensor([[ 1,  2,  3,  4]])
>>> torch.unsqueeze(x, 1)
tensor([[ 1],
        [ 2],
        [ 3],
        [ 4]])
"""
from PIL import Image

classes = list(total_data.class_to_idx)

def predict_one_image(image_path, model, transform,classes):

    test_img = Image.open(image_path).convert('RGB') 
    # 使用image.open读出图像,加convert(RGB)的作用,如不使用“RGB”进行转换的话,读出来的图像是GRBA四通道,A通道为透明通道,
    # 使用Image.open()读取图时,调用.convert()函数更改图像模式 
  
    # plt.imshow(test_img) # 展示预测的图片
    test_img = transform(test_img) # 转变形式
    img = test_img.to(device).unsqueeze(0) # 能够读懂语句,但是不理解作用

    model.eval() # 不是很理解这句在这的作用,保证BN层(Batch Normalization)能够用全部训练数据的均值和方差,即测试过程中要保证BN层和dropout所带来的性质,
    output = model(img)

    _,pred = torch.max(output,1) # _,
    pred_class = classes[pred]
    
    print(f'预测结果是: {pred_class}') # 预测训练集中的某张照片

# 预测训练集中的某张照片
predict_one_image(image_path='./4-data/Monkeypox/M01_01_00.jpg', 
                  model=model, 
                  transform=train_transforms, 
                  classes=classes)

输出结果:
预测结果是:Monkeypox

五、保存并加载模型

# 模型保存
PATH = './model.pth'  # 保存的参数文件名
torch.save(model.state_dict(), PATH)

# 将参数加载到model当中
model.load_state_dict(torch.load(PATH, map_location=device))

输出结果:
All keys matched successfully

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梦在黎明破晓时啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值