基于pytorch的手写体识别

news2024/12/24 3:18:31

一、环境搭建
链接: python与深度学习——基础环境搭建

二、数据集准备
本次实验用的是MINIST数据集,利用MINIST数据集进行卷积神经网络的学习,就类似于学习单片机的点灯实验,学习一门机器语言输出hello world。MINIST数据集,可以调用torchvision里面的模块进行下载。

三、导入模块.
1.导入模块的代码

import torch
import torchvision
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

2.每个模块的作用
torch:导入pytorch的库
torchvision:导入torchvision,它PyTorch中的一个库,它提供了一些计算机视觉任务的工具和预训练模型。
from torch.utils.data import DataLoader:关于DataLoader,从PyTorch的torch.utils.data模块中导入DataLoader类。DataLoader类是PyTorch中用于数据加载的实用工具,它提供了对数据集的批量加载和并行处理的功能。通过使用DataLoader,可以方便地将数据集划分为小批量(batch)进行训练, 同时还可以利用多线程进行数据加载和预处理,以加快训练过程。
matplotlib .pyplot:主要适用于绘图,待会我们会用它查看数据集里面的图片,以及绘制训练损失和测试损失的曲线。
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

导入torch中的一些模块用于构建神经和优化网络的参数。

四、设置卷积神经网络的超参数
这些超参数都是全局变量,调整超参数也是优化神经网络的一个重要的手段

n_epochs = 3

epoch的数量定义了循环整个数据集的次数。也就是训练和测试的次数。

batch_size_train = 64
batch_size_test = 1000

这里是批处理,批处理的好处是可以大幅缩短每张图像的处理时间。batch_size表示批量大小,指每次模型更新时所使用的样本数。其中较大的批量大小可以提高训练速度,但可能降低模型的泛化能力;较小的批量大小可能导致训练过程更加噪声,并且需要更多的训练迭代次数。这里我们用batch_size=64进行训练,利用batch_size=1000进行测试。

learning_rata = 0.01

learning_rate表示学习率。用于控制每次参数更新的步长。较小的学习率可以使训练更稳定,但可能需要更多的训练迭代次数;较大的学习率可能导致训练不稳定或无法收敛。

momentum = 0.5

动量是一种在优化算法中使用的技术,用于加速梯度下降的收敛过程。它通过在更新时引入之前的更新方向,帮助模型跳出局部极小值。

log_interval = 10

这行代码将日志间隔(log interval)设置为10。在训练过程中,每隔10个批次(batch)将打印一次训练日志,用于跟踪训练的进度和性能。

random_seed = 1
torch.manual_seed(random_seed)

将随机种子(random seed)设置为1,并将其应用于PyTorch的随机数生成器。通过设置随机种子,可以使得每次运行代码时的随机过程可复现,即获得相同的随机结果。这在实验和调试中很有用,可以确保实验结果的一致性。

设置超参数部分的完整代码

n_epochs = 3
batch_size_train = 64
batch_size_test = 1000
learning_rate = 0.01
momentum = 0.5
log_interval = 10
random_seed = 1
torch.manual_seed(random_seed)

五、加载训练数据集和测试数据集

train_loader = torch.utils.data.DataLoader

torch.utils.data.DataLoader是PyTorch提供的用于数据加载的实用工具。

torchvision.datasets.MNIST('./data/', train=False, download=True,                              

torchvision.datasets.MNIST是用来加载MNIST数据集的函数,其中train=True表示加载训练集,download=True表示如果数据集不存在时会从互联网上下载。

 transform=torchvision.transforms.Compose([
                                   torchvision.transforms.ToTensor(),
                                   torchvision.transforms.Normalize(
                                       (0.1307,), (0.3081,))
                               ])),

transform=torchvision.transforms.Compose([…])定义了一系列的数据转换操作,用于对数据进行预处理。torchvision.transforms.ToTensor()将数据转换为Tensor对象,将图像数据从PIL Image对象转换为Tensor对象。
torchvision.transforms.Normalize((0.1307,), (0.3081,))对图像数据进行归一化处理,使其均值为0.1307,标准差为0.3081。这是针对MNIST数据集的归一化处理,目的是将数据转换为均值为0、方差为1的分布。

  batch_size=batch_size_train,shuffle=True

规定了每个批次加载的数量,shuffle=True表示要对数据进行随机洗牌,在每个周期中随机选择样本

上述代码是加载训练数据集,完整代码如下。

train_loader = torch.utils.data.DataLoader(
    torchvision.datasets.MNIST('./data/', train=True, download=True,
                               transform=torchvision.transforms.Compose([
                                   torchvision.transforms.ToTensor(),
                                   torchvision.transforms.Normalize(
                                       (0.1307,), (0.3081,))
                               ])),
    batch_size=batch_size_train, shuffle=True)

加载测试数据集的方式和加载训练数据集的方式一样,不同的是要把train=True改为train=False,

加载测试数据集的代码如下:

test_loader = torch.utils.data.DataLoader(
    torchvision.datasets.MNIST('./data/', train=False, download=True,
                               transform=torchvision.transforms.Compose([
                                   torchvision.transforms.ToTensor(),
                                   torchvision.transforms.Normalize(
                                       (0.1307,), (0.3081,))
                               ])),
    batch_size=batch_size_test, shuffle=True)

六、查看数据,确定数据的维度
所谓数据的维度,就是数据的数量,通道数,高度和宽度等信息。

examples = enumerate(test_loader)

使用enumerate函数对test_loader进行枚举,返回一个枚举对象examples。枚举对象可以用于迭代加载test_loader中的批次数据。

batch_idx, (example_data, example_targets) = next(examples)

通过next函数从examples枚举对象中获取下一个批次的数据。batch_idx表示批次的索引,(example_data, example_targets)表示批次中的示例数据和对应的标签。

print(example_targets)

打印出示例数据的标签,就是图片实际对应的数字标签。 这里的example_targets是一个张量,包含了当前批次中每个样本的标签。

print(example_data.shape)

打印出示例数据的形状,example_data是一个张量,表示当前批次中每个样本的图像数据,通过shape属性,可以查看数据的维度信息,如通道数、高度和宽度。

以上代码的功能是查看部分测试数据,并查看示例数据的标签和查看示例数据的形状,这部分的完整代码如下。

examples = enumerate(test_loader)
batch_idx, (example_data, example_targets) = next(examples)
print(example_targets)
print(example_data.shape)

运行结果
在这里插入图片描述
这里显示,示例数据的尺寸是torch.Size([1000, 1, 28, 28]),表示的是1000张测试数据,黑白图像,通道为1,像素为28*28。

七、查看示例数据
这里我们要用到之前导入的matplotlib.pyplot模块。这里有点类似于MATLAB的绘图

fig = plt.figure()

创建一个新的图形对象。

for i in range(6):

循环6次,用于遍历6个子图的位置。

plt.subplot(2,3,i+1)

在图形中创建一个2行3列的子图,并选择第i + 1个子图位置。

plt.tight_layout()

调整子图的布局,使其更加紧凑

 plt.imshow(example_data[i][0], cmap='gray', interpolation='none')

使用imshow函数显示第i个示例数据的图像。example_data[i][0],表示第i个示例数据的图像张量;cmap = ‘gray’,表示使用灰度颜色映射;interpolation = ‘none’, 表示不使用插值来显示图像。

 plt.title("Ground Truth:{}".format(example_targets[i]))

设置当前子图的标题,包括示例数据的标签信息。

 plt.xticks([])
 plt.yticks([])

隐藏子图的横纵刻度标签。

plt.show()

进行图形的绘制。

此部分的完整代码为:

fig = plt.figure()
for i in range(6):
    plt.subplot(2, 3, i + 1)
    plt.tight_layout()
    plt.imshow(example_data[i][0], cmap='gray', interpolation='none')
    plt.title("Ground Truth: {}".format(example_targets[i]))
    plt.xticks([])
    plt.yticks([])
plt.show()

运行结果
在这里插入图片描述
八、构建神经网络和传播函数
现在就到了最激动人心的时刻了,构建一个卷积神经网络,并了解整个的流程。粗略看来,我们的所构建的这个网络的结构为:
在这里插入图片描述
两个卷积层,那么对应就有两个激活函数和池化层,同时还使用dropout层和全连接层。各层的作用如下。
卷积层
卷积层的主要作用是提取输入数据中的特征。卷积层通过使用一组可学习的卷积核(也称为滤波器)对输入数据进行卷积操作。卷积操作可以看作是一种窗口滑动的过程,将卷积核与输入数据的不同位置进行逐元素相乘,并求和得到输出的单个元素。通过对整个输入数据进行卷积操作,卷积层可以得到一张特征图(也称为卷积特征)。
激活函数
我们设想一下,如果使用线性函数或者是将线性函数叠加成网络,那么它始终无法解决非线性的问题,所以针对此问题,我们引入了一些非线性函数作为激活函数,为什么称之为激活函数,我们拿神经细胞来举例。
在这里插入图片描述
如图所示:神经元是由轴突和树突构成的。当轴突接收到上一个神经元传来的信号的时候,树突上会产生一个动作电压,那么这个歌神经元就会被激活,从而向后继续传导信号。同样的,有了激活函数我们深度学习中的神经元才可以被激活,神经网络才能够正常工作,解决实际问题。
在深度学习中,常见的激活函数有:sigmoid函数,Relu函数,softmax函数等。这里我们使用的是Relu函数。
池化层
池化层通常紧跟在卷积层之后。池化的主要作用是对特征图进行下采样,减少数据的空间维度,并且保留重要的特征信息。。在池化窗口内,通常选择最大值(Max Pooling)或平均值(Average Pooling)作为汇总特征。这样可以过滤掉一些噪声和不重要的细节,保留对分类或识别任务有用的特征。在此次实验中,我们采用的是最大池化的方式,池化窗口为2。
Dropout层
Dropout层是一种常用的正则化技术,在深度学习中用于减少过拟合(overfitting)问题。它的主要作用是随机地在神经网络的训练过程中将一部分神经元的输出置为零。也就是随机删除一些神经元。Dropout层通过随机地丢弃神经元的输出,可以减少过拟合、防止共适应、提高泛化能力,并且降低了模型的复杂性。它是一种简单而有效的正则化技术,因此在深度学习中被广泛应用。
全连接层
全连接层的作用是将前一层的所有神经元与当前层的所有神经元相连接,每个连接都有一个可学习的权重。全连接层通常是神经网络最后的层,用于将中间表示映射到最终的输出类别或预测值。例如,在图像分类任务中,全连接层将学习到的特征转换为类别的概率分布。在回归任务中,全连接层可以将学习到的特征映射为连续的数值输出。

代码实现以及具体参数

class Net(nn.Module):

创建神经网络的类,该类的父类是nn.Module类

def __init__(self):

初始化方法,用于初始化网络的各个层和组件

 super(Net, self).__init__()

super(Net, self).init()的作用是调用父类的构造函数,以便在子类的构造函数中执行父类的初始化逻辑。通过调用父类的构造函数,可以确保子类在创建实例时继承并初始化父类的属性和方法。

self.conv1 = nn.Conv2d(1, 10, kernel_size=5)

定义了一个卷积层conv1,输入通道数为1,输出通道数为10,卷积核大小为5x5。

self.conv2 = nn.Conv2d(10, 20, kernel_size=5)

定义了另一个卷积层conv2,输入通道数为10,输出通道数为20,卷积核大小为5x5。

 self.conv2_drop = nn.Dropout2d()

定义了一个二维Dropout层conv2_drop。

  self.fc1 = nn.Linear(320, 50)

定义了一个全连接层fc1,输入大小为320,输出大小为50。

self.fc2 = nn.Linear(50, 10)

定义了另一个全连接层fc2,输入大小为50,输出大小为10。

def forward(self,x):

Net类的前向传播函数,用于定义网络的数据流向。

x = F.relu(F.max_pool2d(self.conv1(x),2))

对输入x进行卷积、ReLU激活和最大池化操作,池化窗口大小为2,每个窗口的大小为2x2。最大池化是指特征图的每个2x2的窗口内的值取最大值,从而将特征图的尺寸减小一半。

x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)),2))

对第一个卷积层的输出进行卷积、Dropout、ReLU激活和最大池化操作。

 x = x.view(-1, 320)

将张量x进行展平,变为一维向量。

x = F.relu(self.fc1(x))

对展平后的向量进行全连接并进行ReLU激活操作。

x = F.dropout(x, training=self.training)

对第一个全连接层的输出进行Dropout操作,self.training用于指示当前是否处于训练模式。

x = self.fc2(x)

对第二个全连接层的输出进行全连接操作。

return  F.log_softmax(x)

对输出进行log_softmax操作,用于多分类问题的概率预测。

前面一段定义了网络结构,以及网络的前向传播函数,总体代码如下

class Net(nn.Module):
    # 初始化方法,用于初始化网络的各个层和组件。
    def __init__(self):
        # 继承父类的一些属性
        super(Net, self).__init__()
        # 定义了一个卷积层conv1,输入通道数为1,输出通道数为10,卷积核大小为5x5。
        self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
        # 定义了另一个卷积层conv2,输入通道数为10,输出通道数为20,卷积核大小为5x5。
        self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
        # 定义了一个二维Dropout层conv2_drop。
        self.conv2_drop = nn.Dropout2d()
        # 定义了一个全连接层fc1,输入大小为320,输出大小为50。
        self.fc1 = nn.Linear(320, 50)
        # 定义了另一个全连接层fc2,输入大小为50,输出大小为10。
        self.fc2 = nn.Linear(50, 10)

    # Net类的前向传播函数,用于定义网络的数据流向。
    def forward(self,x):
        # 对输入x进行卷积、ReLU激活和最大池化操作,池化窗口大小为2,每个窗口的大小为2x2
        # 最大池化是指特征图的每个2x2的窗口内的值取最大值,从而将特征图的尺寸减小一半。
        x = F.relu(F.max_pool2d(self.conv1(x),2))
        # 对第一个卷积层的输出进行卷积、Dropout、ReLU激活和最大池化操作。
        x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)),2))
        # 将张量x进行展平,变为一维向量。
        x = x.view(-1, 320)
        # 对展平后的向量进行全连接、ReLU激活操作。
        x = F.relu(self.fc1(x))
        # 对第一个全连接层的输出进行Dropout操作,self.training用于指示当前是否处于训练模式。
        x = F.dropout(x, training=self.training)
        # 对第二个全连接层的输出进行全连接操作。
        x = self.fc2(x)
        # 对输出进行log_softmax操作,用于多分类问题的概率预测。
        return  F.log_softmax(x)

如上的网络结构以及数据流向,如下图所示。
在这里插入图片描述
九、初始化网络和优化器

#实例化对象
# 创建一个Net类的实例,即创建了一个神经网络对象。
network = Net()
# 创建一个随机梯度下降(SGD)优化器对象,用于优化网络的参数。
# network.parameters()返回网络中的可学习参数,即需要进行梯度更新的参数。
optimizer = optim.SGD(network.parameters(),lr=learning_rata,momentum=momentum)

创建空列表,记录训练以及测试过程中的损失值和步数

# 用于记录训练过程中的损失值和训练步数。
train_losses = []
train_counter = []
# 创建了另外两个空列表,用于记录测试过程中的损失值和测试步数。
# 根据训练数据集的大小和训练周期数来确定测试步数的间隔。
test_losses = []
test_counter = [i*len(train_loader.dataset) for i in range(n_epochs + 1)]

这一块完整的代码为

# 初始化网络和优化器
#实例化对象
# 创建一个Net类的实例,即创建了一个神经网络对象。
network = Net()
# 创建一个随机梯度下降(SGD)优化器对象,用于优化网络的参数。
# network.parameters()返回网络中的可学习参数,即需要进行梯度更新的参数。
optimizer = optim.SGD(network.parameters(),lr=learning_rata,momentum=momentum)

# 用于记录训练过程中的损失值和训练步数。
train_losses = []
train_counter = []
# 创建了另外两个空列表,用于记录测试过程中的损失值和测试步数。
# 根据训练数据集的大小和训练周期数来确定测试步数的间隔。
test_losses = []
test_counter = [i*len(train_loader.dataset) for i in range(n_epochs + 1)]

十、模型训练与测试
模型训练

def train(epoch):
    # 将神经网络模型设置为训练模式,这是为了确保在训练过程中启用一些特定的操作,如Dropout。
    network.train()

将网络模型设置为训练模式

    for batch_idx, (data, target) in enumerate(train_loader):
        # 在每个批次开始时,将优化器的梯度缓冲区清零,以准备计算当前批次的梯度。
        optimizer.zero_grad()
        # 通过将输入数据传递给网络模型(network),计算模型的输出
        output = network(data)
        # 使用负对数似然损失函数(F.nll_loss), 计算模型输出和目标标签之间的损失
        loss = F.nll_loss(output,target)
        # 根据损失值,执行反向传播过程,计算相对于模型参数的梯度。
        loss.backward()
        # 根据梯度更新模型的参数,使用优化器(optimizer),来执行参数更新步骤。
        optimizer.step()

读取训练集的数据,计算输出并计算输出和目标之间的损失,损失函数是机器学习中比较重要的一个内容,其作用是衡量输出值和目标值之间的差距,损失函数有很多种,也可以自己定义,这里我们采用的是负对数似然损失函数(F.nll_loss), 计算模型输出和目标标签之间的损失。计算完损失值之后,执行反向传播过程,并进行更新

为了更直观的了解训练的进度,在终端进行当前训练轮数、已处理的样本数量、总样本数量的百分比以及当前批次的损失值等信息的打印。

        # 如果当前批次的索引能被log_interval整除,表示达到了指定的打印间隔。
        if batch_idx % log_interval == 0:
            # 打印当前训练轮数、已处理的样本数量、总样本数量的百分比以及当前批次的损失值。
            print('Train Epoch: {}  [{}/{} ({:.0f}%)]\tLoss: {: .6f}'.format(
                epoch, batch_idx*len(data), len(train_loader.dataset),
                    100.*batch_idx/len(train_loader), loss.item()))
            # append,给列表添加元素的指令
            # 将当前批次的损失值和对应的训练步数添加到训练损失列表(train_losses)
            # 和训练步数列表(train_counter)中,用于后续的可视化和分析。
            train_losses.append(loss.item())
            train_counter.append(
                (batch_idx *64) + ((epoch -1) * len(train_loader.dataset)))
            torch.save(network.state_dict(),'./model.pth')
            torch.save(optimizer.state_dict(),'./optimizer.pth')

在训练结束后,要对模型参数以及优化器状态进行保存,以便于之后可以接着上一次的训练结果接着训练

# 调用训练的函数,次数为1
train(1)

其中1就是epoch,即训练轮数,可以根据实际情况进行调整

测试
和上面的训练过程基本类似,遍历测试集,计算模型输出,并且计算损失

def test():
    # 将神经网络模型设置为评估模式,这是为了确保在评估过程中不启用一些特定的操作,如Dropout。
    network.eval()
    # test_loss,用于累积测试损失
    test_loss = 0
    # correct用于累积预测正确的样本数量。
    correct = 0
    # 使用torch.no_grad()
    # 上下文管理器,表示在评估过程中不进行梯度计算,以减少内存消耗和加快计算速度。
    with torch.no_grad():
        # 遍历测试数据集(test_loader),其中data是输入数据的批量,target是对应的标签。
        for data, target in test_loader:
            # 通过将输入数据传递给网络模型(network),计算模型的输出。
            output = network(data)
            # 使用负对数似然损失函数(F.nll_loss),计算模型输出和目标标签之间的损失,并累积到test_loss中。
            test_loss = test_loss + F.nll_loss(output, target, size_average=False).item()
            # 获取模型输出中概率最高的类别预测,即预测值。
            pred = output.data.max(1, keepdim=True)[1]
            # 计算预测值与目标标签相等的样本数量,并累积到correct中
            correct = correct + pred.eq(target.data.view_as(pred)).sum()

    # 计算平均测试损失,将累积的测试损失值除以测试数据集的样本数量。
    test_loss = test_loss / len(test_loader.dataset)
    # 将平均测试损失值添加到测试损失列表(test_losses)中,
    test_losses.append(test_loss)

同样我们在控制台对结果进行打印

    # 打印评估结果,包括平均测试损失和测试数据集上的准确率。
    print('\nTest set: Avg. loss: {: .4f},Accuracy : {}/{} ({: .0f}%)\n'.format(
        test_loss, correct, len(test_loader.dataset),
        100.* correct / len(test_loader.dataset)))

下面一段代码是用于进行训练和测试的主要循环,在开始训练之前,首先调用test()函数对当前的模型在测试数据集上进行评估, 以了解初始模型在未经训练的情况下的性能。

test()
# 从1到n_epochs(训练轮数+1)进行循环,表示训练过程中的每个训练轮次(epoch)。
for epoch in range(1, n_epochs + 1):
    # 调用train(epoch)函数,进行一次完整的训练轮次。
    # 在train()函数中,会遍历训练数据集,并执行前向传播、计算损失、反向传播和参数更新等步骤。
    train(epoch)
    # 在完成一次训练轮次后,调用test()函数对当前模型在测试数据集上进行评估,
    # 以了解训练过程中模型的性能变化。
    test()

通过这样的循环,每个训练轮次都会进行一次完整的训练和评估, 以不断优化模型的参数,并监测训练的进展。这样的循环将重复多次,直到达到指定的训练轮数(n_epochs)为止。

以上是模型训练和测试的内容,这一部分完整代码如下:

#模型训练
#尝试一次循环,看看精度与损失,准确度
# epoch遍历数据集的次数,即训练轮数
def train(epoch):
    # 将神经网络模型设置为训练模式,这是为了确保在训练过程中启用一些特定的操作,如Dropout。
    network.train()
    # 使用enumerate函数遍历训练数据集(train_loader)
    # batch_idx表示当前批次的索引,data是输入数据的批量,target是对应的标签。
    for batch_idx, (data, target) in enumerate(train_loader):
        # 在每个批次开始时,将优化器的梯度缓冲区清零,以准备计算当前批次的梯度。
        optimizer.zero_grad()
        # 通过将输入数据传递给网络模型(network),计算模型的输出
        output = network(data)
        # 使用负对数似然损失函数(F.nll_loss), 计算模型输出和目标标签之间的损失
        loss = F.nll_loss(output,target)
        # 根据损失值,执行反向传播过程,计算相对于模型参数的梯度。
        loss.backward()
        # 根据梯度更新模型的参数,使用优化器(optimizer),来执行参数更新步骤。
        optimizer.step()

        # 如果当前批次的索引能被log_interval整除,表示达到了指定的打印间隔。
        if batch_idx % log_interval == 0:
            # 打印当前训练轮数、已处理的样本数量、总样本数量的百分比以及当前批次的损失值。
            print('Train Epoch: {}  [{}/{} ({:.0f}%)]\tLoss: {: .6f}'.format(
                epoch, batch_idx*len(data), len(train_loader.dataset),
                    100.*batch_idx/len(train_loader), loss.item()))
            # append,给列表添加元素的指令
            # 将当前批次的损失值和对应的训练步数添加到训练损失列表(train_losses)
            # 和训练步数列表(train_counter)中,用于后续的可视化和分析。
            train_losses.append(loss.item())
            train_counter.append(
                (batch_idx *64) + ((epoch -1) * len(train_loader.dataset)))

            # 保存当前的网络模型参数和优化器状态,以便在需要时恢复和继续训练。
            # 训练结束后都要保存网络
            torch.save(network.state_dict(),'./model.pth')
            torch.save(optimizer.state_dict(),'./optimizer.pth')

# 调用训练的函数,次数为1
train(1)

# 进行测试
def test():
    # 将神经网络模型设置为评估模式,这是为了确保在评估过程中不启用一些特定的操作,如Dropout。
    network.eval()
    # test_loss,用于累积测试损失
    test_loss = 0
    # correct用于累积预测正确的样本数量。
    correct = 0
    # 使用torch.no_grad()
    # 上下文管理器,表示在评估过程中不进行梯度计算,以减少内存消耗和加快计算速度。
    with torch.no_grad():
        # 遍历测试数据集(test_loader),其中data是输入数据的批量,target是对应的标签。
        for data, target in test_loader:
            # 通过将输入数据传递给网络模型(network),计算模型的输出。
            output = network(data)
            # 使用负对数似然损失函数(F.nll_loss),计算模型输出和目标标签之间的损失,并累积到test_loss中。
            test_loss = test_loss + F.nll_loss(output, target, size_average=False).item()
            # 获取模型输出中概率最高的类别预测,即预测值。
            pred = output.data.max(1, keepdim=True)[1]
            # 计算预测值与目标标签相等的样本数量,并累积到correct中
            correct = correct + pred.eq(target.data.view_as(pred)).sum()

    # 计算平均测试损失,将累积的测试损失值除以测试数据集的样本数量。
    test_loss = test_loss / len(test_loader.dataset)
    # 将平均测试损失值添加到测试损失列表(test_losses)中,
    test_losses.append(test_loss)

    # 打印评估结果,包括平均测试损失和测试数据集上的准确率。
    print('\nTest set: Avg. loss: {: .4f},Accuracy : {}/{} ({: .0f}%)\n'.format(
        test_loss, correct, len(test_loader.dataset),
        100.* correct / len(test_loader.dataset)))

# 下面一段代码是用于进行训练和测试的主要循环
# 在开始训练之前,首先调用test()函数对当前的模型在测试数据集上进行评估,
# 以了解初始模型在未经训练的情况下的性能。
test()
# 从1到n_epochs(训练轮数+1)进行循环,表示训练过程中的每个训练轮次(epoch)。
for epoch in range(1, n_epochs + 1):
    # 调用train(epoch)函数,进行一次完整的训练轮次。
    # 在train()函数中,会遍历训练数据集,并执行前向传播、计算损失、反向传播和参数更新等步骤。
    train(epoch)
    # 在完成一次训练轮次后,调用test()函数对当前模型在测试数据集上进行评估,
    # 以了解训练过程中模型的性能变化。
    test()

# 通过这样的循环,每个训练轮次都会进行一次完整的训练和评估,
# 以不断优化模型的参数,并监测训练的进展。
# 这样的循环将重复多次,直到达到指定的训练轮数(n_epochs)为止。

十一、评估模型的性能,并进行可视化展示

# 创建一个新的图形窗口。
fig = plt.figure()
# 绘制训练损失曲线。train_counter是训练步数的列表,train_losses是对应的训练损失值的列表。
# 通过plt.plot函数将训练步数和训练损失连接起来,形成一条蓝色曲线。
plt.plot(train_counter, train_losses, color='blue')
# 绘制测试损失数据点。test_counter是测试步数的列表,test_losses是对应的测试损失值的列表。
# 通过plt.scatter函数将测试步数和测试损失以红色的散点图形式展示。
plt.scatter(test_counter, test_losses, color='red')
# 添加图例,标明蓝色曲线表示训练损失,红色散点表示测试损失。图例显示在图的右上方。
plt.legend(['Train Loss', 'Test Loss'],loc='upper right')
# 设置横轴和纵轴的标签,分别表示训练步数和损失值
# 横轴上标上,所看到的训练样本的数量
plt.xlabel('number of training examples seen')
# 纵轴上标上负对数似然损失
plt.ylabel('negative log likelihood loss')

# 比较模型的输出,并输出相应的预测值
# 使用enumerate函数遍历测试数据集(test_loader),获取每个样本的索引和数据。
examples = enumerate(test_loader)
# 调用next函数获取下一个样本的索引和数据。
# batch_idx表示当前样本在批次中的索引,
# example_data和example_targets分别表示输入数据和对应的目标标签。
batch_idx,(example_data,example_targets) = next(examples)
# 使用torch.no_grad()上下文管理器,表示在计算模型输出时不进行梯度计算
with torch.no_grad():
    # 通过将example_data输入到网络模型(network),计算模型的输出。
    output = network(example_data)

这里也对预测图像可视化展示

# 创建一个新的图形窗口。
fig = plt.figure()
# 使用循环遍历前6个样本,绘制子图并显示样本图像
for i in range(6):
    # 创建2x3的子图网格,并在当前子图中显示第i + 1个样本图像。
    plt.subplot(2,3,i+1)
    # 使得子图在图形窗口中紧凑且不重叠
    plt.tight_layout()
    # example_data[i][0],表示第i个样本的图像数据。
    # plt.imshow函数用于显示图像,
    # cmap = 'gray',表示使用灰度颜色映射,
    # interpolation = 'none',表示不进行插值。
    plt.imshow(example_data[i][0],cmap='gray', interpolation='none')
    # 显示预测结果。output.data.max(1, keepdim=True)[1][i].item()
    # 表示对于第i个样本,获取预测概率最高的类别,并将其作为预测结果。
    plt.title("Prediction: {}".format(output.data.max(1, keepdim=True)[1][i].item()))
    # 不添加横纵坐标
    plt.xticks([])
    plt.yticks([])
plt.show()

由于前面设置的超参数中训练轮数为3,所以这里运行了三轮
在这里插入图片描述
绘制的训练损失曲线和测试损失曲线如下
在这里插入图片描述
预测结果如下
在这里插入图片描述
绘制曲线的完整代码如下

十二、继续训练
我们这里重复上面的过程,接着第三轮之后继续进行训练(因为前面有对前三轮运行之后的模型参数等进行保存),并再次绘制曲线,对训练损失和测试损失进行可视化展示。

# 手动添加计数器进行训练
for i in  range(4,9):
    test_counter.append(i*len(train_loader.dataset))
    train(i)
    test()

#使用图像检查训练结果
fig = plt.figure()
plt.plot(train_counter, train_losses, color='blue')
plt.scatter(test_counter, test_losses, color='red')
plt.legend(['Train Loss', 'Test Loss'], loc='upper right')
plt.xlabel('number of training examples seen')
plt.ylabel('negative log likelihood loss')
plt.show()

接着前面的三轮,我们一共训练了八轮,所得到的曲线如下
在这里插入图片描述
在这里插入图片描述

至此,一个简单的机器学习实例已经完成,当然也可以调整超参数,以达到更高的准确率和更小的损失。

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

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

相关文章

03-grafana的下拉列表选项制作-grafana的变量

一、准备环境 为了实现下拉列表筛选的样例,我们监控两个linux节点; 目前,我们已经有了一个节点了,再添加一个; 二、grafana的仪表盘变量 如果想给仪表盘自定义下拉列表,那么,需要设置变量&#…

Full-RNS CKKS

参考文献: [HS13] Halevi S, Shoup V. Design and implementation of a homomorphic-encryption library[J]. IBM Research (Manuscript), 2013, 6(12-15): 8-36.[BEHZ16] Bajard J C, Eynard J, Hasan M A, et al. A full RNS variant of FV like somewhat homomo…

如何用可调电源给 GSX-250R 摩托车电瓶充电

查看如何用可调电源充电瓶的教程请直接下滑至“总结”栏目。 天气很好,想骑车去玩一下,发现摩托车两个月之后摩托车电瓶只有10V左右了,不足以启动摩托车,貌似是这次电瓶电压确实过低了,推了好几次没推起来&#xff08…

蓝桥杯前端Web赛道-新鲜的蔬菜

蓝桥杯前端Web赛道-新鲜的蔬菜 题目链接:1.新鲜的蔬菜 - 蓝桥云课 (lanqiao.cn) 题目要求如下: 其实很容易联想到使用flex布局,这是flex布局一种非常经典的骰子布局,推荐Flex 布局教程:实例篇 - 阮一峰的网络日志 (r…

如何利用Flutter来写后端 服务端应用

前言 Flutter是谷歌推出的一款跨平台开发框架,现在属于此领域star最多的框架,其被广泛应用于构建前台界面,但或许很少人知道,他也可以写后端应用。 本文主角 flutter非常著名的getx库推出的get server jonataslaw/get_server:…

如何在 Windows 11/10 中合并分区而不丢失数据

在本文中,我们将了解如何在 Window 11/10 中合并分区而不丢失个人数据。每个人都会觉得需要扩大驱动器/分区的容量,但是在计算机中重新安装Windows对他们来说很麻烦。在 Windows PC 中合并分区的方法有很多种。我们将在下面逐步讨论一些工作方法&#xf…

docker 常用命令大全(基础、镜像、容器、数据卷)

文章目录 1.docker基础命令2.docker镜像命令2.1 镜像名称2.2 镜像命令2.3 案例1--拉取、查看镜像2.4 案例2--保存、导入镜像 3.docker容器命令3.1 容器命令3.2 案例--创建并运行一个容器3.3 案例--进入容器,修改文件3.4 小结 4.数据卷4.1 什么是数据卷4.2 数据卷操作…

学习python的地11天,昨天偶遇她失败了

昨天是目标偶遇她的第二次,我猜测是由于没算准时间,故而今日计划8:15那班车。 如果今日没有偶遇上,那就明天呗,后天,大后天,就像随机数一样,总有命中那一刻 昨天,我尝试给我的linu…

适用于 Windows 的 5 款最佳免费数据恢复软件榜单

每个计算机用户都曾经历过数据丢失的情况。很容易错误地删除重要的文件和文件夹,当发生这种情况时,可能会导致不必要的心痛和压力。值得庆幸的是,可以恢复 Windows PC 上丢失的数据。在本文中,我们将分享您可以使用的五种最佳 Win…

【Android 内存优化】怎么理解Android PLT hook?

文章目录 前言什么是hook?PLT hook作用基本原理PLT hook 总体步骤 代码案例分析方案预研面临的问题怎么做?ELFELF 文件头SHT(section header table) 链接视图(Linking View)和执行视图(Execution View&…

Linux系统宝塔面板搭建Typecho博客并实现公网访问本地网站【内网穿透】

文章目录 前言1. 安装环境2. 下载Typecho3. 创建站点4. 访问Typecho5. 安装cpolar6. 远程访问Typecho7. 固定远程访问地址8. 配置typecho 前言 Typecho是由type和echo两个词合成的,来自于开发团队的头脑风暴。Typecho基于PHP5开发,支持多种数据库&#…

【MGR】MySQL Group Replication 背景

目录 17.1 Group Replication Background 17.1.1 Replication Technologies 17.1.1.1 Primary-Secondary Replication 17.1.1.2 Group Replication 17.1.2 Group Replication Use Cases 17.1.2.1 Examples of Use Case Scenarios 17.1.3 Group Replication Details 17.1…

初学arp欺骗

首先准备一台靶机这里用虚拟机的win10 已知网关与ip地址(怕误伤) 现在返回kali从头开始 首先探测自己的网关 然后扫内网存活的ip 发现有3台 用nmap扫一下是哪几台 成功发现我们虚拟机的ip 现在虚拟机可以正常访问网络 接下来直接开梭 ip网关 返回虚拟机…

十秒学会Ubuntu命令行:从入门到进阶

一、引言 在使用Ubuntu操作系统时,命令行界面(CLI)是不可或缺的一部分。对于初学者来说,掌握基本的命令行操作可以帮助他们更高效地管理系统和软件。本文将介绍一些常见的Ubuntu命令以及如何解决与命令行相关的问题。 二、常用Ubu…

Java两周半速成之路(第十天)

一.String和StringBuffer的相互转换 1.适用场景&#xff1a; 场景1&#xff1a;可以需要用到另一种类中的方法&#xff0c;来方便处理我们的需求 场景2&#xff1a;方法传参 场景一演示&#xff1a; /*String和StringBuffer的相互转换A<-->B什么场景需要做转换操作&…

字节跳动发布SDXL-Lightning模型,支持WebUI与ComfyUI双平台,只需一步生成1024高清大图!

字节跳动发布SDXL-Lightning模型,WebUI与ComfyUI平台,只需一步生成1024高清大图,需要的步数比 LCM 更低了! 什么是SDXL-Lightning: SDXL-Lightning 是一种快速的文本到图像生成模型。SDXL-Lightning模型的核心优势在于其创新的蒸馏策略,它可以通过几个步骤生成高质量的 1…

【数据分享】1988~2020年中国月度1KM植被指数NDVI空间分布数据集

各位同学们好&#xff0c;今天和大伙儿分享的是1988~2020年中国月度1KM植被指数NDVI空间分布数据集。如果大家有下载处理数据等方面的问题&#xff0c;您可以私信或评论。 徐新良.中国月度植被指数&#xff08;NDVI&#xff09;空间分布数据集.中国科学院资源环境科学数据中心数…

SpringCloud-RabbitMQ消息模型

本文深入介绍了RabbitMQ消息模型&#xff0c;涵盖了基本消息队列、工作消息队列、广播、路由和主题等五种常见消息模型。每种模型都具有独特的特点和适用场景&#xff0c;为开发者提供了灵活而强大的消息传递工具。通过这些模型&#xff0c;RabbitMQ实现了解耦、异步通信以及高…

数据结构(一)——概述

一、绪论 1.1数据结构的基本概念 数据&#xff1a;用来描述客观事物的数、计算机中是字符及所有能输入并被程序识别和处理的符号的集合。 数据元素&#xff1a;数据的基本单位&#xff0c;一个数据元素可由若干数据项组成。 数据结构&#xff1a;指相互之间存在一种或多种特…

C++:Vector的使用

一、vector的介绍 vector的文档介绍 1. vector是表示可变大小数组的序列容器。 2. 就像数组一样&#xff0c;vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问&#xff0c;和数组一样高效。但是又不像数组&#xff0c;它的大小是可以…