欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 美食 > 【深度学习实战】构建AI模型,实现手写数字自动识别

【深度学习实战】构建AI模型,实现手写数字自动识别

2024/10/23 23:28:42 来源:https://blog.csdn.net/u012171005/article/details/143117499  浏览:    关键词:【深度学习实战】构建AI模型,实现手写数字自动识别

本文收录于 《人工智能学习入门》专栏。从零基础开始,分享一些人工智能、机器学习、深度学习相关的知识,包括基本概念、技术原理、应用场景以及如何开发实战等等。

相信完整学习后会有很多收获。欢迎关注,谢谢!

文章目录

    • 一、前言
    • 二、什么是手写数字识别任务
    • 三、如何构建AI模型,实现手写数字识别
        • 开发环境准备
        • 3.1 数据处理
        • 3.2 模型设计
        • 3.3 模型训练
        • 3.4 模型评估
        • 3.5 模型测试
    • 四、总结

一、前言

近年来,人工智能(AI)大模型在计算机科学领域引起了广泛的兴趣和关注。这些模型以其庞大的参数规模和卓越的性能,在各种领域展现了巨大的潜力。

本文介绍如何构建一个AI模型,实现一个简单的手写数字识别任务。

二、什么是手写数字识别任务

‌手写数字识别是一种利用计算机自动辨认人手写在纸张上的阿拉伯数字的技术。‌ 这一技术属于光学字符识别技术(OCR)的一个分支,其核心是通过构建模型学习一系列手写数字图片及其对应的数字标签,进而实现对新的手写数字图片的自动识别‌。

手写数字识别主要应用于汇款单号识别、手写邮政编码识别等领域,这些应用大大缩短了业务处理时间,提升了工作效率和质量‌。此外,手写数字识别在理论研究中也有重要价值,例如,它有助于验证和评价各种识别方法的有效性‌。

在这里插入图片描述

简单地说,手写数字识别就是用笔在白纸上写一个阿拉伯数字,之后,拍照上传图片,让AI模型去识别,看它能不能认出来。

三、如何构建AI模型,实现手写数字识别

开发环境准备

使用操作系统:Windows11

安装开发环境管理工具Anaconda,参考链接:https://blog.csdn.net/u012171005/article/details/137468307

安装开发工具PyCharm,参考链接:https://blog.csdn.net/u012171005/article/details/137505590

安装百度飞桨AI平台PaddlePaddle,参考链接:https://www.paddlepaddle.org.cn/documentation/docs/zh/install/pip/windows-pip.html

安装可视分析工具VisualDL,参考链接:https://www.paddlepaddle.org.cn/documentation/docs/zh/2.2/guides/03_VisualDL/visualdl_cn.html

构建AI模型的流程如下:
在这里插入图片描述

3.1 数据处理

我们面临的任务和数据环境千差万别,通常需要自己编写适合当前任务的数据处理程序,进行数据处理。其中数据处理的内容包括划分数据集、归一化和标准化处理、生成批次数据、训练样本乱序、数据有效性校验、数据增强增广等等。

这里使用广泛使用的一个基准测试数据集,MNIST数据集。

  • MNIST数据集是一个包含了手写数字图片及其对应标签的数据集,是机器学习和计算机视觉领域广泛使用的一个基准测试数据集。它由Yann LeCun等人创建,包含60,000个训练样本和10,000个测试样本。每个图片是一个28x28像素的灰度图,每个像素点对应一个0-255之间的整数,表示像素的强度。

在这里插入图片描述

import paddle
from paddle.vision.transforms import Normalizedef get_MNIST_dataloader():# 定义图像归一化处理方法,这里的CHW指图像格式需为 [C通道数,H图像高度,W图像宽度]transform = Normalize(mean=[127.5], std=[127.5], data_format='CHW')# 下载数据集并初始化 DataSettrain_dataset = paddle.vision.datasets.MNIST(mode='train', transform=transform)test_dataset = paddle.vision.datasets.MNIST(mode='test', transform=transform)# 定义并初始化数据读取器train_loader = paddle.io.DataLoader(train_dataset, batch_size=64, shuffle=True, num_workers=0, drop_last=True)test_loader = paddle.io.DataLoader(test_dataset, batch_size=64, shuffle=False, num_workers=0, drop_last=False)return train_loader, test_loader
3.2 模型设计

对于计算机视觉问题,效果最好的模型仍然是卷积神经网络。卷积神经网络针对视觉问题的特点进行了网络结构优化,可以直接处理原始形式的图像数据,保留像素间的空间信息,因此更适合处理视觉问题。所以,这里选择使用多层卷积神经网络。

在这里插入图片描述

如上图所示,卷积神经网络由多个卷积层和池化层组成。其中,卷积层负责对输入进行扫描以生成更抽象的特征表示,池化层对这些特征表示进行过滤,保留最关键的特征信息。

# 定义模型网络结构【**说明,这里使用多层卷积神经网络实现**】
class MNIST(paddle.nn.Layer):def __init__(self):super(MNIST, self).__init__()# 定义卷积层,输出特征通道out_channels设置为20,卷积核的大小kernel_size为5,卷积步长stride=1,padding=2self.conv1 = Conv2D(in_channels=1, out_channels=20, kernel_size=5, stride=1, padding=2)# 定义池化层,池化核的大小kernel_size为2,池化步长为2self.max_pool1 = MaxPool2D(kernel_size=2, stride=2)# 定义卷积层,输出特征通道out_channels设置为20,卷积核的大小kernel_size为5,卷积步长stride=1,padding=2self.conv2 = Conv2D(in_channels=20, out_channels=20, kernel_size=5, stride=1, padding=2)# 定义池化层,池化核的大小kernel_size为2,池化步长为2self.max_pool2 = MaxPool2D(kernel_size=2, stride=2)# 定义一层全连接层,输出维度是10self.fc = Linear(in_features=980, out_features=10)# 定义网络前向计算过程,卷积后紧接着使用池化层,最后使用全连接层计算最终输出# 卷积层激活函数使用Relu,全连接层激活函数使用softmaxdef forward(self, inputs, label=None):x = self.conv1(inputs)x = F.relu(x)x = self.max_pool1(x)x = self.conv2(x)x = F.relu(x)x = self.max_pool2(x)x = paddle.reshape(x, [x.shape[0], 980])x = self.fc(x)if label is not None:acc = paddle.metric.accuracy(input=x, label=label)return x, accelse:return x

定义好模型的网络结构之后,可以打印查看网络结构信息。

model = MNIST()
params_info = paddle.summary(model, (1, 1, 28, 28))
print(params_info)

之后,打印查看该网络结构信息如下:

在这里插入图片描述

3.3 模型训练

​ 首先,指定训练的设备为CPU(这里是个人学习使用CPU,实际项目中会使用GPU,加快训练速度)。之后,设置优化器算法为SGD,设置学习率为0.01。为动态追踪训练过程,使用VisualDL对训练过程进行可视化。

​ 在深度学习神经网络模型中,通常使用标准的随机梯度下降算法更新参数,学习率代表参数更新幅度的大小,即步长。当学习率最优时,模型的有效容量最大,最终能达到的效果最好。学习率和深度学习任务类型有关,合适的学习率往往需要大量的实验和调参经验。

在这里插入图片描述

探索学习率最优值时需要注意如下两点:

  • 学习率不是越小越好。学习率越小,损失函数的变化速度越慢,意味着我们需要花费更长的时间进行收敛,如上图左所示。
  • 学习率不是越大越好。只根据总样本集中的一个批次计算梯度,抽样误差会导致计算出的梯度不是全局最优的方向,且存在波动。在接近最优解时,过大的学习率会导致参数在最优解附近震荡,损失难以收敛,如上图右所示。

在训练前,我们往往不清楚一个特定问题设置成怎样的学习率是合理的,因此在训练时可以尝试调小或调大,通过观察Loss下降的情况判断合理的学习率。

# 设置训练设置为CPU,如使用GPU机器时,可以将use_gpu变量设置成True
use_gpu = False
paddle.set_device('gpu:0') if use_gpu else paddle.set_device('cpu')# 模型训练
def train(model):# 生成模型对象model = MNIST()# 设置为训练模式model.train()# 设置优化算法为SGD、学习率参数为0.01# 四大主流优化算法以及不同的学习率参数,可以逐一尝试效果opt = paddle.optimizer.SGD(learning_rate=0.01, parameters=model.parameters())# opt = paddle.optimizer.Momentum(learning_rate=0.01, momentum=0.9, parameters=model.parameters())# opt = paddle.optimizer.Adagrad(learning_rate=0.01, parameters=model.parameters())# opt = paddle.optimizer.Adam(learning_rate=0.01, parameters=model.parameters())# 设置训练次数EPOCH_NUM = 5iter = 0iters = []losses = []for epoch_id in range(EPOCH_NUM):for batch_id, data in enumerate(train_loader()):# 准备数据images, labels = dataimages = paddle.to_tensor(images)labels = paddle.to_tensor(labels)# 前向计算的过程,同时拿到模型输出值和分类准确率predicts, avg_acc = model(images, labels)# 计算损失,取一个批次样本损失的平均值,【**说明,这里使用交叉熵损失函数计算损失,均方误差(常用于回归问题),交叉熵误差(常用于分类问题)】loss = F.cross_entropy(predicts, labels)avg_loss = paddle.mean(loss)# 每训练了100批次的数据,打印下当前Loss的情况if batch_id % 100 == 0:print("epoch: {}, batch: {}, loss is: {}, acc is {}".format(epoch_id, batch_id, avg_loss.numpy(),avg_acc.numpy()))# 数据记录到日志中,后续可以使用VisualDL查看log_writer.add_scalar(tag='acc', step=iter, value=avg_acc.numpy())log_writer.add_scalar(tag='loss', step=iter, value=avg_loss.numpy())# iters.append(iter)# losses.append(avg_loss.numpy())iter = iter + 100# 后向传播,更新参数,消除梯度的过程avg_loss.backward()opt.step()opt.clear_grad()# 保存模型参数paddle.save(model.state_dict(), 'output/mnist.pdparams')

训练模型时,经常需要观察模型的评价指标,分析模型的优化过程,以确保训练是有效的。将模型训练效果等数据可视化作图可选用两种工具:Matplotlib库和VisualDL。这里使用VisualDL工具对训练的结果数据进行可视化分析。

在命令行窗口,使用命令启动工具;

visualdl --logdir ./log  --port 8080

之后,打开浏览器使用访问工具界面:

http://127.0.0.1:8080/app

在这里插入图片描述

**从图中可以发现,分类准备率ACC不是很稳定,损失函数LOSS值还可以再小一些。说明,该模型还可以继续训练和优化。模型训练完成后,保存模型训练参数。 **

在我们的实际应用中,由于训练数据有限、数据获取较难、训练资源有限等原因,往往利用在大规模开源数据集上训练得到的模型参数作为我们自己模型的初始值(也称为预训练模型),这样可以加速网络训练、并得到较高精度。

3.4 模型评估

在训练过程中,我们会发现模型在训练样本集上的损失在不断减小。但这是否代表模型在未来的应用场景上依然有效?为了验证模型的有效性,通常将样本集合分成三份,训练集、校验集和测试集。

  • 训练集 :用于训练模型的参数,即训练过程中主要完成的工作。

  • 验证集 :用于对模型超参数的选择,比如网络结构的调整、正则化项权重的选择等。

  • 测试集 :用于模拟模型在应用后的真实效果。因为测试集没有参与任何模型优化或参数训练的工作,所以它对模型来说是完全未知的样本。在不以校验数据优化网络结构或模型超参数时,校验数据和测试数据的效果是类似的,均更真实的反映模型效果。

如下程序读取上一步训练保存的模型参数,读取校验数据集,并测试模型在校验数据集上的效果。

# 模型评估
def evaluation(model):print('start evaluation .......')# 加载模型参数params_file_path = 'output/mnist.pdparams'param_dict = paddle.load(params_file_path)model.load_dict(param_dict)# 设置模型为评估模式model.eval()acc_set = []avg_loss_set = []for batch_id, data in enumerate(test_loader()):images, labels = dataimages = paddle.to_tensor(images)labels = paddle.to_tensor(labels)predicts, acc = model(images, labels)# 计算损失,取一个批次样本损失的平均值,【**说明,这里使用交叉熵损失函数计算损失,均方误差(常用于回归问题),交叉熵误差(常用于分类问题)】loss = F.cross_entropy(input=predicts, label=labels)avg_loss = paddle.mean(loss)avg_loss_set.append(float(avg_loss.numpy()))# 计算分类准确率accacc_set.append(float(acc.numpy()))# 计算多个batch的平均损失和准确率acc_val_mean = np.array(acc_set).mean()avg_loss_val_mean = np.array(avg_loss_set).mean()print('loss={}, acc={}'.format(avg_loss_val_mean, acc_val_mean))

运行结果如下:

start evaluation .......
loss=0.06925583740963806, acc=0.9796974522292994

从测试的效果来看,模型在验证集上依然有97.9%的准确率,证明它是有预测效果的。

3.5 模型测试

模型评估通过后,可以手工制作一些数字图片,对模型进行测试,查看模型的预测效果。

以下是在白纸上手写的数字图片:
在这里插入图片描述

对图片数据进行处理:

# 读取一张本地的样例图片,转变成模型输入的格式
def load_image(img_path):# 从img_path中读取图像,并转为灰度图im = Image.open(img_path).convert('L')# 调整图像大小为(28, 28)im = im.resize((28, 28), Image.LANCZOS)im = np.array(im).reshape(1, 1, 28, 28).astype(np.float32)# 图像归一化# im = 1.0 - im / 255.im = ((255 - im) / 127.5) - 1.0  # 把测试图片转黑底白字,因为训练的图片都是黑底白字 edit 20241012return im

之后,使用模型进行预测:

def batchTest(model):print('start test ..................')# 手写数字图片和标签data_list = [("image/hand/0.png", "0"),("image/hand/1.png", "1"),("image/hand/2.png", "2"),("image/hand/3.png", "3"),("image/hand/4.png", "4"),("image/hand/5.png", "5"),("image/hand/6.png", "6"),("image/hand/7.png", "7"),("image/hand/8.png", "8"),("image/hand/9.png", "9")]for num_data_path, data_label in data_list:# 加载和预处理测试数据print("--------test result is:")# 加载模型参数params_file_path = 'model/mnist_20241008.pdparams'param_dict = paddle.load(params_file_path)model.load_dict(param_dict)# 灌入数据tensor_img = load_image(num_data_path)# 预测model.eval()# 查看结果,模型反馈10个分类标签的对应概率results = model(paddle.to_tensor(tensor_img))# 取概率最大的标签作为预测输出lab = np.argsort(results.numpy())print("本次预测的数字是: ", lab[0][-1])print('数据标签是: ' + str(data_label))

预测的结果如下:

在这里插入图片描述

从以上测试结果,可以发现,手写数字6,出现识别错误,预测结果为5。可见该模型,还需要进一步的优化和训练。

四、总结

以上从安装开发环境、数据处理、模式设计、模型训练、模型评估到模型测试,完整的介绍了如何构建AI模型,实现手写数据识别任务。希望通过这篇博文,让大家对AI模型有初步的认识和了解。

如果您对文章中内容有疑问,欢迎在评论区进行留言,我会尽量抽时间给您回复。如果文章对您有帮助,欢迎点赞、收藏。您的点赞,是对我最大的支持和鼓励,谢谢 :-)

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com