利用卷积神经网络进行手写数字的识别

数据集介绍

MNIST(Modified National Institute of Standards and Technology)数据集是一个广泛使用的手写数字识别数据集,常用于机器学习和计算机视觉领域中的分类任务。它包含了从0到9的手写数字样本,常用于训练和测试各种图像分类算法。

数据集概况

MNIST数据集由60,000个训练样本和10,000个测试样本组成,每个样本是一张28×28像素的灰度图像,表示一个手写数字。每个图像是一个二维矩阵,像素值范围从0(黑色)到255(白色),灰度值表示不同的颜色深度。数据集中的标签是这些图像对应的数字(0-9)。

数据集格式

  • 训练集:60,000个图像,每个图像有一个对应的标签(0到9之间的数字)。
  • 测试集:10,000个图像,也有对应的标签。

使用场景

  1. 图像分类任务:由于数据集较小且标准化,MNIST是机器学习算法(尤其是深度学习模型)测试和比较性能的一个标准数据集。
  2. 模型性能评估:MNIST被广泛用于评估各种机器学习模型的效果,尤其是在图像处理领域。
  3. 教学:由于其简单性,MNIST常作为入门学习机器学习和神经网络的教学材料。

特点

  • 图像尺寸固定:28×28像素,适合用作标准输入。
  • 图像内容简单:大多数手写数字都是规范且易于分辨的。
  • 数据集较小,适合于快速实验和初步的模型验证。

数据集获取

MNIST数据集可以通过多个平台获取,例如:

  • 通过TensorFlow、PyTorch等框架的内建API加载。
  • 从MNIST官网下载。

数据预处理及参数选择

数据处理

python 复制代码
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))])
# softmax归一化指数函数(https://blog.csdn.net/lz_peter/article/details/84574716),其中0.1307是mean均值和0.3081是std标准差

train_dataset = datasets.MNIST(root='./data/mnist', train=True, transform=transform)  # 本地没有就加上download=True
test_dataset = datasets.MNIST(root='./data/mnist', train=False, transform=transform)  # train=True训练集,=False测试集
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)

参数的选择

python 复制代码
batch_size = 64                #每个批次大小中有64个样本
learning_rate = 0.01           #学习率
momentum = 0.5                 #梯度下降冲量
epochs = 10                    #训练轮数
  • batch_size = 64:每次训练时使用64个样本来计算梯度并更新权重。
  • learning_rate = 0.01:每次权重更新时,步长为0.01,影响训练速度和稳定性。
  • momentum = 0.5:通过加权平均过去的梯度,帮助加速收敛并减少梯度更新的震荡。
  • epochs = 10:模型将在训练数据上进行10次完整的迭代,通常可以在这个范围内找到适合的训练状态。

网络模型

python 复制代码
class Net(torch.nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = torch.nn.Sequential(
            torch.nn.Conv2d(1, 10, kernel_size=5),
            torch.nn.ReLU(),
            torch.nn.MaxPool2d(kernel_size=2),
        )
        self.conv2 = torch.nn.Sequential(
            torch.nn.Conv2d(10, 20, kernel_size=5),
            torch.nn.ReLU(),
            torch.nn.MaxPool2d(kernel_size=2),
        )
        self.conv3 = torch.nn.Sequential(
            torch.nn.Flatten(),
            torch.nn.Linear(320, 50),
            torch.nn.Linear(50, 10),
        )

    def forward(self, x):
        batch_size = x.size(0)
        x = self.conv1(x)  # 一层卷积层,一层池化层,一层激活层(图是先卷积后激活再池化,差别不大)
        x = self.conv2(x)  # 再来一次
        x = self.conv3(x)
        return x  # 最后输出的是维度为10的,也就是(对应数学符号的0~9)
  • 输入层:

    • 输入尺寸 :每张输入图像是28x28像素的灰度图,单通道。输入张量的形状为 (batch_size, 1, 28, 28),其中 batch_size 是一次处理的图像数量,1 是表示单通道的灰度图像,28x28 是图像的尺寸。
  • 第一层卷积层(conv1):

    • 卷积层 :使用一个大小为 5x5 的卷积核,将输入图像的1个通道(灰度)转换为10个通道。卷积核的步幅为1,填充为0(即没有边缘扩展)。这会产生一个大小为 24x24 的特征图(由于没有填充,尺寸会减少)。
    • 激活函数 :ReLU(Rectified Linear Unit),它会对每个像素值进行非线性转换(ReLU(x) = max(0, x)),有效地引入了非线性特性。
    • 池化层 :最大池化层使用 2x2 的池化窗口和步幅为2。池化操作减少了特征图的尺寸,将每个 2x2 的区域映射为最大值。池化操作将图像尺寸减半,从 24x24 减小为 12x12,同时减少计算量。
  • 第二层卷积层(conv2):

    • 卷积层 :卷积核的大小为 5x5,将前一层输出的10个通道转换为20个通道。同样,步幅为1,没有填充。这个操作将特征图的大小从 12x12 减少到 8x8
    • 激活函数:使用ReLU激活函数。
    • 池化层 :再次使用最大池化,池化窗口为 2x2,步幅为2。此操作将尺寸从 8x8 减小为 4x4
  • 全连接层(conv3):

    • 展平操作(Flatten) :经过两层卷积和池化操作后,输出特征图的大小为 20x4x4。在传入全连接层之前,需要将这个多维的张量展平成一维向量。展平后的尺寸是 320(即 20 * 4 * 4)。
    • 第一个全连接层:将展平后的320个元素映射到50个神经元。该层的作用是通过加权和偏置的线性变换对输入进行处理,并通过激活函数进行非线性转换。
    • 第二个全连接层:将50个神经元映射到10个神经元,输出的每个神经元代表一个数字类别(0到9)。
  • 输出层:

    • 输出尺寸:最终输出为一个10维的向量,其中每个值表示输入图像属于每个类别的"分数"。这个分数可以通过softmax层转化为概率,用于多类分类任务。

模型训练

python 复制代码
# Construct loss and optimizer ------------------------------------------------------------------------------
loss_f = torch.nn.CrossEntropyLoss()  # 交叉熵损失
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate, momentum=momentum)  # lr学习率,momentum冲量


# Train and Test CLASS --------------------------------------------------------------------------------------
# 把单独的一轮一环封装在函数类里
def train(epoch):
    running_loss = 0.0  # 这整个epoch的loss清零
    running_total = 0
    running_correct = 0
    for batch_idx, data in enumerate(train_loader, 0):  #第一个代表训练的批次,data中包括数据和标签,第一个数据代表输入即inputs,第二个数据代表标签labels

        inputs, target = data
        optimizer.zero_grad()   #将之前的梯度清零

        # forward + backward + update
        outputs = model(inputs)
        loss = loss_f(outputs, target)
        #反向传播
        loss.backward()
        #参数更新
        optimizer.step()

        # 把运行中的loss累加起来,为了下面300次一除
        running_loss += loss.item()
        # 把运行中的准确率acc算出来
        _, predicted = torch.max(outputs.data, dim=1)
        running_total += inputs.shape[0]
        running_correct += (predicted == target).sum().item()
        if batch_idx % 300 == 299:  # 不想要每一次都出loss,浪费时间,选择每300次出一个平均损失,和准确率
            print('[%d, %5d]: loss: %.3f , acc: %.2f %%'
                  % (epoch + 1, batch_idx + 1, running_loss / 300, 100 * running_correct / running_total))
            running_loss = 0.0  # 这小批300的loss清零
            running_total = 0
            running_correct = 0  #


            # 这小批300的acc清零

        # torch.save(model.state_dict(), './model_Mnist.pth')
        # torch.save(optimizer.state_dict(), './optimizer_Mnist.pth')


def test():
    correct = 0
    total = 0
    with torch.no_grad():  # 测试集不用算梯度
        for data in test_loader:
            images, labels = data
            outputs = model(images)
            _, predicted = torch.max(outputs.data, dim=1)  # dim = 1 列是第0个维度,行是第1个维度,沿着行(第1个维度)去找1.最大值和2.最大值的下标
            total += labels.size(0)  # 张量之间的比较运算
            correct += (predicted == labels).sum().item()
    acc = correct / total
    print('[%d / %d]: Accuracy on test set: %.1f %% ' % (epoch + 1, epochs, 100 * acc))  # 求测试的准确率,正确数/总数
    return acc


# Start train and Test --------------------------------------------------------------------------------------
if __name__ == '__main__':
    acc_list_test = []
    for epoch in range(epochs):
        train(epoch)
        # if epoch % 10 == 9:  #每训练10轮 测试1次
        acc_test = test()
        acc_list_test.append(acc_test)

    plt.plot(acc_list_test)
    plt.xlabel('Epoch')
    plt.ylabel('Accuracy On TestSet')
    plt.show()

训练结果

相关推荐
神一样的老师1 分钟前
【鸿睿创智开发板试用】在OpenHarmony 4.1环境下移植OpenCV示例
人工智能·opencv·计算机视觉
神一样的老师2 分钟前
【鸿睿创智开发板试用】移植OpenCV 4到OpenHarmony 4.1
人工智能·opencv·计算机视觉·鸿蒙·openharmony
珂朵莉MM3 分钟前
第六届全球校园人工智能算法精英大赛-算法巅峰专项赛(系列文章)-- 开篇
java·人工智能·python·算法·职场和发展
油泼辣子多加6 分钟前
【计算机视觉】边缘检测
人工智能·python·计算机视觉
liuming19927 分钟前
Halcon中background_seg(Operator)算子原理及应用详解
图像处理·人工智能·算法·计算机视觉·目标跟踪·视觉检测
qwe3526337 分钟前
OpenCV常用图像处理函数解析
图像处理·人工智能·opencv
王亭_66611 分钟前
深度学习中损失函数(loss function)介绍
人工智能·pytorch·深度学习·损失函数
MaoziShan18 分钟前
PyTorch3D 可视化
人工智能·pytorch·3d
阿正的梦工坊36 分钟前
什么是正则化?Regularization: The Stabilizer of Machine Learning Models(中英双语)
人工智能·机器学习
**之火37 分钟前
(十二)机器学习 - 训练/测试
人工智能·机器学习