【机器学习】使用卷积神经网络模型训练MNIST(含混淆矩阵)

使用卷积神经网络模型训练MNIST

        设计了一个卷积神经网络模型训练MNIST并根据混淆矩阵对其进行测试以评估其性能和准确度。
        计算机视觉领域,手写数字识别是一个经典的问题。本文将介绍如何使用卷积神经网络(Convolutional Neural Network, CNN)来实现手写数字识别任务。我们将使用PyTorch框架和MNIST数据集,通过搭建一个简单的卷积神经网络模型来完成任务,并在测试集上评估模型的准确率。

1.简介

        手写数字识别是计算机视觉领域的经典问题之一。它的任务是将手写的数字图像分类为相应的数字类别,从0到9。在本篇博客中,我们将使用深度学习中常用的卷积神经网络来解决这个问题。卷积神经网络在图像处理任务中表现出色,能够自动学习图像的特征,并进行准确的分类。

2.MNIST数据集和预处理

        我们使用了MNIST数据集,MNIST数据集是机器学习领域中最著名的数据集之一,常被用于图像分类任务。它包含了一系列手写数字的灰度图像,每个图像的尺寸为28x28像素。MNIST数据集共有60000个训练样本和10000个测试样本,涵盖了从0到9的10个数字类别。

        MNIST数据集的广泛应用使其成为计算机视觉领域的基准测试数据集之一。它的相对简单性使得研究人员可以快速验证新的模型和算法,并进行性能比较。同时,MNIST数据集也为初学者提供了一个入门的机会,帮助他们理解和实践基本的图像分类方法。

        为了更好地适应模型的训练,我们对MNIST数据集进行了预处理。首先,我们将图像转换为张量的形式,以便能够在深度学习框架中进行处理。然后,我们对图像进行了标准化处理,将像素值缩放到0到1之间,这有助于提高模型的训练效果。通过这些预处理步骤,我们将MNIST数据集准备好,可以用于训练和测试我们的卷积神经网络模型。

        MNIST数据集的简单性和广泛应用使得它成为图像分类任务的经典数据集。通过在MNIST上进行实验和研究,我们可以更好地理解和探索图像分类算法的性能和效果。

在这里插入图片描述

引入PyTorch库和所需要的模块:

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix

3.构建卷积神经网络模型

        卷积神经网络(Convolutional Neural Network, CNN)是一种在计算机视觉领域广泛应用的深度学习模型。它通过卷积层和池化层的组合,能够有效地捕捉图像的局部特征和空间结构,从而实现图像分类、目标检测和图像生成等任务。

在我们的手写数字识别任务中,我们将使用一个简单的卷积神经网络模型。该模型由以下几个主要组件组成:

  • 卷积层(Convolutional Layers): 卷积层是卷积神经网络的核心部分。它通过卷积操作对输入图像进行特征提取。在我们的模型中,我们使用了两个卷积层,每个卷积层都有一组可学习的卷积核(filters),并应用了ReLU激活函数来增加模型的非线性能力。

  • 池化层(Pooling Layers): 池化层用于减小特征图的尺寸,并保留重要的特征信息。在我们的模型中,我们使用了最大池化(Max Pooling)层,它从输入特征图中提取出每个区域的最大值,从而减小特征图的维度。

  • 全连接层(Fully Connected Layers): 全连接层将卷积层和池化层提取的特征进行展平,并通过一系列全连接层进行分类。在我们的模型中,我们使用了两个全连接层,最后一个全连接层的输出大小与手写数字的类别数相匹配。

        在PyTorch框架中,我们可以通过继承nn.Module类来构建卷积神经网络模型。我们需要实现模型的前向传播函数,即定义模型如何将输入数据通过各个层进行处理并得到输出结果。通过PyTorch提供的各种卷积层、激活函数和池化层等模块,我们可以轻松地构建卷积神经网络模型。

定义一个自定义的模型类 CustomModel,继承自 nn.Module:

class CustomModel(nn.Module):
    def __init__(self):
        super(CustomModel, self).__init__()
        self.conv1 = nn.Conv2d(1, 16, kernel_size=3, stride=1, padding=1)  # 第一个卷积层
        self.relu = nn.ReLU()  # ReLU激活函数
        self.maxpool = nn.MaxPool2d(kernel_size=2)  # 最大池化层
        self.conv2 = nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=1)  # 第二个卷积层
        self.fc1 = nn.Linear(7 * 7 * 32, 128)  # 第一个全连接层
        self.fc2 = nn.Linear(128, 10)  # 第二个全连接层

    def forward(self, x):
        x = self.conv1(x)  # 第一个卷积层
        x = self.relu(x)  # ReLU激活函数
        x = self.maxpool(x)  # 最大池化层
        x = self.conv2(x)  # 第二个卷积层
        x = self.relu(x)  # ReLU激活函数
        x = self.maxpool(x)  # 最大池化层
        x = x.view(x.size(0), -1)  # 展开为一维向量
        x = self.fc1(x)  # 第一个全连接层
        x = self.relu(x)  # ReLU激活函数
        x = self.fc2(x)  # 第二个全连接层
        return x

4. 训练模型

        在构建完卷积神经网络模型之后,我们需要对模型进行训练,以便使其能够准确地对手写数字进行分类。下面是训练模型的一般步骤:

  1. 数据加载和预处理: 在训练模型之前,我们需要加载并预处理训练数据。对于手写数字识别任务,我们已经介绍过MNIST数据集,并对其进行了预处理,将图像转换为张量并进行了标准化处理。现在,我们将加载训练集的图像和对应的标签,并将其划分为小批量数据用于训练。

  2. 定义损失函数: 在训练模型时,我们需要定义一个损失函数来度量模型在训练数据上的预测结果与实际标签之间的差异。对于分类任务,常用的损失函数是交叉熵损失函数(Cross-Entropy Loss),它能够有效地衡量模型的分类性能。

  3. 定义优化器: 优化器用于更新模型的参数,使得模型能够逐渐优化并提高性能。常用的优化器算法包括随机梯度下降(Stochastic Gradient Descent, SGD)、Adam等。我们需要选择一个合适的优化器,并设置学习率等超参数。

  4. 循环训练: 在训练过程中,我们将通过循环迭代来优化模型。每个训练迭代中,我们将输入训练数据到模型中,计算模型的预测结果,并与实际标签进行比较,得到损失值。然后,我们使用优化器来反向传播误差,并更新模型的参数。这个过程将重复多个训练迭代,直到模型收敛或达到预定的训练轮数。

  5. 评估模型: 在训练过程中,我们需要定期评估模型的性能。为了评估模型在未见过的数据上的泛化能力,我们将测试集输入到训练好的模型中,并计算模型在测试集上的准确率或其他评估指标。

  6. 调参和优化: 如果模型在训练集和测试集上的性能不够理想,我们可以考虑调整模型的超参数、增加训练数据、改变网络结构等来进一步优化模型。

定义数据预处理:

transform = transforms.Compose([
    transforms.ToTensor(),  # 将数据转换为Tensor格式
    transforms.Normalize((0.1307,), (0.3081,))  # 数据归一化处理
])

加载数据集和测试集:

train_dataset = datasets.MNIST(root='./data', train=True, transform=transform, download=True)  # 加载训练集
test_dataset = datasets.MNIST(root='./data', train=False, transform=transform)  # 加载测试集

创建数据加载器:

train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)  # 创建训练集数据加载器
test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False)  # 创建测试集数据加载器

创建模型实例,定义损失函数和优化器:

model = CustomModel()  # 创建自定义模型实例
criterion = nn.CrossEntropyLoss()  # 交叉熵损失函数
optimizer = optim.Adam(model.parameters(), lr=0.001)  # Adam优化器

定义一个训练函数 train,用于训练模型:

def train(model, train_loader, criterion, optimizer, num_epochs):
    model.train()
    train_losses = []
    test_accuracies = []

    for epoch in range(num_epochs):
        running_loss = 0.0
        for i, (images, labels) in enumerate(train_loader):
            optimizer.zero_grad()
            outputs = model(images)
            loss = criterion(outputs, labels)
            loss.backward()
            optimizer.step()

            running_loss += loss.item()
            if (i + 1) % 100 == 0:
                print(f"Epoch [{epoch + 1}/{num_epochs}], Step [{i + 1}/{len(train_loader)}], Loss: {running_loss / 100:.4f}")
                running_loss = 0.0

        accuracy, _ = test(model, test_loader)
        print(f"Epoch [{epoch + 1}/{num_epochs}], Accuracy on test set: {accuracy:.4f}")

        train_losses.append(running_loss / len(train_loader))
        test_accuracies.append(accuracy)

    return train_losses, test_accuracies

5.测试模型

        在完成模型的训练后,我们需要对其进行测试以评估其性能和准确度。下面是测试模型的一般步骤:

  1. 加载测试数据: 首先,我们需要加载测试集的图像和对应的标签,这些数据是模型在训练过程中未使用过的数据。与训练数据类似,我们需要对测试数据进行相同的预处理,例如将图像转换为张量并进行标准化处理。

  2. 模型预测: 接下来,我们将测试数据输入已训练好的模型中,通过前向传播计算模型的预测结果。模型将根据输入图像的特征提取和学习到的参数,预测出图像所代表的数字类别。

  3. 结果评估: 对于每个测试样本,我们比较模型的预测结果与实际标签,以确定模型的准确度。常用的评估指标包括准确率(Accuracy)、精确率(Precision)、召回率(Recall)和F1分数等。这些指标可以帮助我们了解模型对不同类别的预测性能。

    定义了一个测试函数 test,用于评估模型在测试集上的准确率:

    def test(model, test_loader):
        model.eval()
        correct = 0
        total = 0
        predicted_labels = []
        with torch.no_grad():
            for images, labels in test_loader:
                outputs = model(images)
                _, predicted = torch.max(outputs.data, 1)
                total += labels.size(0)
                correct += (predicted == labels).sum().item()
                predicted_labels.extend(predicted.tolist())  # Collect predicted labels
        accuracy = correct / total
        return accuracy, predicted_labels
    

    调用 train 函数进行模型训练,并输出每个epoch的准确率:

    num_epochs = 10
    train(model, train_loader, criterion, optimizer, num_epochs)  # 开始训练
    

6.可视化分析

        为了更好地理解模型的性能和错误分类的原因,我们可以对测试结果进行可视化分析,为了更加直观的清晰的进行评估模型的性能,我们在模型中加入混淆矩阵,损失函数曲线,准确率曲线。

混淆矩阵

        混淆矩阵是一个表格,它通过汇总分类模型在测试集上的预测来可视化分类模型的性能。它使我们能够了解模型对不同类别的实例进行分类的程度,并识别其预测中的任何模式或偏差。混淆矩阵是根据预测标签与测试集的真实标签之间的比较计算得出的。

在这里插入图片描述

混淆矩阵中的

  • 每行表示真实类(实际类)的实例。

  • 每列表示模型预测的实例(预测类)。

  • 对角线元素表示真正数,其中预测类与真类匹配。

  • 偏离对角线的元素表示误差。

解释混淆矩阵

  • 真阳性 (TP): 这些是模型正确预测类的实例。

  • 误报 (FP): 当真正的类不同时,模型会错误地预测类。

  • 漏报 (FN): 这些是模型无法正确预测类的情况。

性能指标

  • 准确性: 它衡量模型预测的整体正确性,计算公式为
    ( T P + T N ) / ( T P + T N + F P + F N ) (TP + TN) / (TP + TN + FP + FN) TP+TN/TP+TN+FP+FN

  • 精度: 它测量正确预测的阳性实例在所有预测为阳性的实例中的比例,计算为

    T P / ( T P + F P ) TP / (TP + FP) TP/TP+FP

  • 召回率(灵敏度或真阳性率): 它测量正确预测的阳性实例占所有实际阳性实例的比例,计算为
    T P / ( T P + F N ) TP / (TP + FN) TP/TP+FN

  • 特异性(真阴性率): 它测量正确预测的阴性实例占所有实际阴性实例的比例,计算为
    T N / ( T N + F P ) TN / (TN + FP) TN/TN+FP

  • F1 分数: 它将精确率和召回率组合成一个指标,计算为
    2 ∗ (精确率 ∗ 召回率) / (精确率 + 召回率) 2 *(精确率* 召回率)/(精确率 + 召回率) 2(精确率召回率)/(精确率+召回率)

混淆矩阵

   
# 计算混淆矩阵并绘制
_, predicted_labels = test(model, test_loader)
true_labels = test_dataset.targets
confusion_mat = confusion_matrix(true_labels, predicted_labels)

plt.figure(figsize=(8, 6))
plt.imshow(confusion_mat, interpolation='nearest', cmap=plt.cm.Blues)
plt.title("Confusion Matrix")
plt.colorbar()
tick_marks = np.arange(10)
plt.xticks(tick_marks, tick_marks)
plt.yticks(tick_marks, tick_marks)

thresh = confusion_mat.max() / 2.
for i in range(confusion_mat.shape[0]):
    for j in range(confusion_mat.shape[1]):
        plt.text(j, i, format(confusion_mat[i, j], 'd'),
                 ha="center", va="center",
                 color="white" if confusion_mat[i, j] > thresh else "black")

plt.xlabel("Predicted Label")
plt.ylabel("True Label")
plt.show()
 

训练一轮结果:

 #训练一轮结果
''
Confusion Matrix:
[[ 969    0    0    0    2    3    1    1    4    0]
 [   0 1130    2    0    1    0    0    1    1    0]
 [   1    1 1012    1    2    0    0    5   10    0]
 [   0    0    2  992    0   12    0    2    2    0]
 [   0    0    0    0  979    0    0    1    1    1]
 [   1    0    0    0    0  887    2    0    2    0]
 [   1    2    0    1    8    4  938    0    4    0]
 [   0    1    8    2    3    0    0 1010    3    1]
 [   4    0    1    2    2    1    0    2  961    1]
 [   1    1    0    2   22   11    0    8   12  952]]''

输出混淆矩阵图像:

在这里插入图片描述

根据第一轮次混淆矩阵中实际类和预测类之间的关系计算准确率,召回率,精确率,F1得分。

accuracy = np.sum(np.diag(confusion_mat)) / np.sum(confusion_mat)

# 计算召回率
recall = np.diag(confusion_mat) / np.sum(confusion_mat, axis=1)

# 计算精确率
precision = np.diag(confusion_mat) / np.sum(confusion_mat, axis=0)

# 计算F1得分
f1_score = 2 * (precision * recall) / (precision + recall)

print("准确率:", accuracy)
print("召回率:", recall)
print("精确率:", precision)
print("F1得分:", f1_score)

#输出结果:
'''准确率: 0.983
召回率: [0.98877551 0.99559471 0.98062016 0.98217822 0.99694501 0.99439462
 0.97912317 0.98249027 0.98665298 0.94350842]
精确率: [0.99181167 0.99559471 0.98731707 0.992      0.96074583 0.96623094
 0.9968119  0.98058252 0.961      0.99685864]
F1得分: [0.99029126 0.99559471 0.98395722 0.98706468 0.97851074 0.9801105
 0.98788836 0.98153547 0.97365755 0.9694501 ]
'''

(1)准确率(Accuracy)

        准确率(Accuracy)是指模型正确预测的样本数与总样本数之比。在混淆矩阵中,准确率可以通过计算对角线上所有元素之和除以矩阵中所有元素之和来获得。准确率提供了模型整体分类正确的程度,但它不能反映模型在不同类别上的表现差异。

例子中的准确率为0.983,表示模型在整个数据集上正确分类的比例较高,约为98.3%。

(2)召回率(Recall)

        召回率(Recall):召回率也被称为灵敏度(Sensitivity)或真正例率(True Positive Rate)。它衡量了模型对实际为某一类别的样本正确预测的能力。在混淆矩阵中,召回率可以通过计算每个类别的真正例(对角线元素)与该类别所有样本数量之比来获得。

例子中的不同类别的召回率在0.943到0.996之间,说明模型对大多数类别的召回率较高,但对类别9的召回率较低,为0.943。这表示模型在预测类别9时相对较多地出现了假反例。

注:在二分类问题中,假反例(False Negative)是指实际上是正例(Positive),但被错误地预测为负例。换句话说,假反例是指模型将一个正例错误地判定为负例。

(3)精确率(Precision)

        精确率(Precision):精确率衡量了模型预测为某一类别的样本中实际属于该类别的比例。在混淆矩阵中,精确率可以通过计算每个类别的真正例与预测为该类别的样本数量之比来获得。

例子中大多数类别的精确率在0.961到0.996之间,表明模型对大多数类别的预测是准确的。类别4的精确率较低,为0.961,这表示模型在预测为类别4时相对较多地出现了假正例。

注: 在二分类问题中,假正例(False Positive)是指实际上是负例(Negative),但被错误地预测为正例。换句话说,假正例是指模型将一个负例错误地判定为正例。

(4)F1得分

        F1得分是综合考虑精确率和召回率的指标,它是精确率和召回率的调和平均值。F1得分可以帮助我们评估模型的整体性能,特别是在处理不平衡数据集或注重模型在不同类别上均衡表现时。F1得分的计算公式为:2 * (精确率 * 召回率) / (精确率 + 召回率)。

例子中大多数类别的F1得分在0.969到0.995之间,说明模型对大多数类别的预测具有较好的平衡性。类别4的F1得分较低,为0.978,这进一步确认了在该类别上模型出现了一定的假正例和假反例。

训练十轮结果:

在这里插入图片描述

十轮次的损失曲线和准确率曲线:

在这里插入图片描述

在这里插入图片描述

7.总结

        在本文中,我们详细介绍了训练和测试模型的过程。通过训练模型,我们能够通过大量的数据和反向传播算法来优化模型的参数,使其能够准确地对手写数字进行分类。同时,测试模型能够帮助我们评估模型的性能和准确度,以及找出模型可能存在的问题和改进的方向。

        在训练模型的过程中,我们需要加载和预处理训练数据,定义损失函数和优化器,并通过循环训练来逐步优化模型。同时,我们还介绍了如何评估模型的性能,并进行可视化分析以深入了解模型的预测情况。

8.相关链接⭐️

[MNIST数据集](mnist | TensorFlow Datasets)

[卷积神经网络](卷积神经网络(Convolutional Neural Network, CNN) | TensorFlow Core)

文章的全部代码放在[代码地址](Machine-learning/使用卷积网络模型训练MNIST.py at main · 2214018128zcy/Machine-learning (github.com)

在这里插入图片描述

喜欢本文的朋友可以收藏点赞起来!!!🎉🎉🎉

以后会继续更新相关领域的博客内容,大家可以点点关注!🌟

  • 18
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
### 回答1: 基于神经网络的手写字符识别MNIST是一个经典的机器学习问题,旨在通过训练一个神经网络模型来识别手写数字图像。MNIST数据集包了60000张28x28像素的训练图像和10000张测试图像,每个图像都标有对应的数字(0到9)。 神经网络模型通常由多个神经元构成的多层结构组成。每个神经元将输入与其自身的权重相乘,并通过激活函数输出结果。在MNIST任务中,输入层接收一个28x28的图像矩阵,并将其展平为一个784个像素的向量。随后的隐藏层通常由多个层级组成,最后输出层包10个神经元,代表数字0到9的类别。 模型训练过程通过反向传播算法来自动调整权重以最小化损失函数。常用的优化算法如梯度下降法可以帮助模型训练中不断调整权重,提高准确性。训练时,模型训练图像输入网络,输出一个概率向量,表示每个数字的可能性。最后,我们选择概率最高的数字作为预测结果。 对于测试图像,我们将其输入已经训练好的模型中,通过前向传播得到预测结果。通过将预测结果与实际标签进行比较,可以评估模型的性能。常用的评估指标包括准确率和混淆矩阵等。 在实际应用中,基于神经网络的手写字符识别MNIST已经取得了很高的准确率。并且该问题也可以作为入门级别的机器学习任务,帮助初学者理解神经网络的基本原理和训练过程。 ### 回答2: 基于神经网络的手写字符识别MNIST(Modified National Institute of Standards and Technology) 是一个经典的问题,旨在通过训练模型来正确识别手写数字图像。 MNIST数据集包60,000个用于训练的手写数字图像和10,000个用于测试的手写数字图像,每个图像的大小为28x28像素。神经网络是一种模拟人脑的计算模型,通过模拟大量的神经元之间的连接以及它们之间的传递和处理信息的方式,来实现学习和推理能力。 在手写字符识别MNIST问题中,神经网络的输入层对应于图像的像素值,通常使用784个输入神经元(28x28)来表示一个图像。然后,神经网络通过多个隐藏层来对图像进行特征提取和抽象化。每个隐藏层都由多个神经元组成,每个神经元将前一层的输出与一个权重向量进行线性组合,并通过使用激活函数对结果进行非线性变换。 常见的激活函数有ReLU、Sigmoid和Tanh函数等。通过反向传播算法,神经网络可以通过调整权重和偏置来最小化训练数据与实际值之间的差异,从而实现对手写字符的准确识别。 一旦神经网络完成了训练,我们就可以使用测试集进行性能评估。我们将测试集中的每个图像输入神经网络,并根据输出层中神经元的激活程度来判断图像代表的数字。我们可以根据预测结果与实际标签之间的差异来评估模型的准确性。 基于神经网络的手写字符识别MNIST是一个经典的机器学习问题,它不仅可以帮助我们理解神经网络的工作原理,还可以作为一个起点来研究更复杂的问题,如自然语言处理、图像识别等。 ### 回答3: 基于神经网络的手写字符识别是一种广泛应用的机器学习技术,尤其在MNIST数据集上。MNIST是一个包手写数字图片的数据集,其中每个图片都是28x28像素的灰度图片,并对应一个0到9的标签。 神经网络是由多个神经元组成的网络结构,由于其能够自动学习特征,并且适用于处理高维数据,因此被广泛应用于手写字符识别。基于神经网络的手写字符识别MNIST通常包以下步骤: 1. 数据准备:首先,需要从MNIST数据集中获取训练集和测试集。我们将训练集作为神经网络的输入数据,并使用测试集评估网络的性能。 2. 网络架构设计:选择合适的网络架构是非常重要的。常见的神经网络架构包括卷积神经网络CNN)和全连接神经网络(FCN)。对于MNIST手写字符识别,常用的网络结构包括LeNet-5和AlexNet等。 3. 网络训练:通过将训练集的图片输入网络,网络会自动学习输入图片的特征和对应的标签。在训练过程中,使用反向传播算法来更新网络中的权重和偏差,以最小化预测值和真实标签之间的差异。 4. 网络评估:训练完成后,使用测试集评估网络的性能。通常使用准确率(Accuracy)来衡量网络的性能,即正确预测的样本数占总样本数的比例。 基于神经网络的手写字符识别MNIST具有较高的准确率和鲁棒性,已经在现实生活中得到广泛应用。随着深度学习的快速发展,也出现了更加复杂和高效的网络结构,进一步提升了手写字符识别的准确率和性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

春阳的科研日志

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

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

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

打赏作者

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

抵扣说明:

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

余额充值