《MNIST利用鼠标手写数字识别》

目录

1. 功能和原始数据

1.1 主要功能

1.2 原始测试数据

2.1 代码块

2. 调试

2.1 调试截图

3.总结

4. 附录

4.1 参考资料

4.2 代码清单


1. 功能和原始数据

1.1 主要功能

    该程序实现了一个基于卷积神经网络的手写数字识别系统,用户可以使用鼠标在GUI界面上手写数字,然后通过加载预训练的模型进行数字识别,并在界面上显示识别结果。

1.2 原始测试数据

       原始测试数据来自MNIST数据集,其中包含了大量的手写数字图片。训练集用于训练模型,测试集用于评估模型在未见过的数据上的准确率。

2.1 代码块

1~8就是原来的代码,9~13是鼠标交互代码

1、导入所需库:

10c579845efc4a3bbd584c868b3d88d8.png

图2.1-1

这些库用于使用PyTorch进行深度学习、数据加载、定义神经网络、优化、可视化和处理数组。

2、设置基本参数:

b14099299d0c4ab5b61e02e07fcee724.png

图2.1-2

这段代码是用于设置基础参数和导入数据的部分。

  • epoch:表示模型训练的迭代次数,即要遍历整个训练集的次数。在这段代码中,设置为3,即训练3轮。
  • learning_rate:表示学习率,控制模型参数更新的步长。在这段代码中,学习率设置为0.001。
  • batch_size_train:表示每次训练使用的样本(图片)数量。在这段代码中,每次训练使用64个样本。
  • batch_size_test:表示每次测试使用的样本(图片)数量。在这段代码中,每次测试使用1000个样本。
  • gpu = torch.cuda.is_available():这行代码用于检测是否有可用的GPU设备。如果有可用的GPU,变量gpu将被设置为True,否则为False
  • momentum:是一个影响模型收敛速度的参数。在这段代码中,设置为0.5。

3、加载MNIST数据集:

4d436f58b8a04d5484789c25fe3d6b61.png

图2.1-3

4、定义网络模型:

b2d5d3a4cee84715961a0e69834b2eed.png

图2.1-4

5、定义损失函数和优化器:

b11b970594c946719f12cf824b3e2353.png

图2.1-5

6、训练函数:

f5f9852e64024053a372e228e071c405.png

图2.1-6

7、测试函数:

68651d634d2a45d99f3481233f2bf753.png

图2.1-7

8、保存模型:

0fd54ae25ac24fe88e6daac03cd6c009.png

图2.1-8

9、

ac58992f879c40ac83644dde2d0cdc96.png

图2.1-9

这部分代码引入了一些需要使用的库和模块:

  1. tkinter: 是一个常用的 Python 图形用户界面(GUI)库,用于创建窗口应用程序。
  2. PIL(Python Imaging Library): 是一个用于图像处理的库,提供了许多图像操作的功能。
  3. torch:是 PyTorch 深度学习框架的主要库。
  4. torchvision.transforms:是 PyTorch 提供的用于数据预处理的模块,可以用来对图像进行转换和处理。

通过引入这些库和模块,可以在后续的代码中使用它们的功能,例如创建窗口应用程序、处理图像数据等。

10、GUI窗口和画布:

e5518a5a73cb475681f8effd8e77e816.png

图2.1-10

这部分代码用于创建一个 GUI 窗口和画布,以及相关的图像处理对象。

1. “tk.Tk()”: 创建一个根窗口对象,即 GUI 窗口的顶层窗口。

2. “root.title("手写数字识别")”: 设置窗口的标题为 "手写数字识别"。

3. “canvas = tk.Canvas(root, width=canvas_width, height=canvas_height, bg='white')”: 创建一个画布对象,并指定其宽度和高度为 “canvas_width” 和 “canvas_height”,背景颜色为白色。

4. “canvas.pack()”: 将画布放置到窗口中,以便显示。

5. “image = Image.new("L", (canvas_width, canvas_height), "white")”: 创建一个新的灰度图像对象,宽度为 “canvas_width”,高度为 “canvas_height”,背景颜色为白色。这里使用了 PIL 库的 “Image” 类。

6. “draw = ImageDraw.Draw(image)”: 创建一个绘图对象,用于在图像上进行绘制操作。这里使用了 PIL 库的 “ImageDraw” 类。

通过这段代码,创建了一个窗口,并在窗口中添加了一个白色的画布和一个空白的图像对象,用于后续的手写数字绘制和识别。

11、鼠标事件处理函数:.

bd12dd8c92b14ddd986bd803c5413156.png

图2.1-11

       这段代码定义了一个名为 “paint” 的鼠标事件处理函数,用于在画布上绘制手写数字。

函数的参数 “event” 是一个鼠标事件对象,它包含了鼠标事件的信息,例如鼠标坐标等。

  在 “paint” 函数中,我们通过 “event.x” 和 “event.y” 获取当前鼠标的坐标位置。然后,我们使用这个坐标位置作为中心,在画布上绘制一个黑色的圆形,形成了手写数字的笔画。

    接着,我们使用 “draw.line” 方法在 “Image” 对象上绘制一条直线,这条直线连接了上一个鼠标位置和当前鼠标位置,也就是绘制了手写数字的轨迹。

    当用户按住鼠标左键并在画布上移动时,就会触发 “<B1-Motion>” 事件,然后调用 “paint” 函数,从而实现了手写数字的绘制。

12、加载预训练模型及识别函数:

4a5384b702b447d583978e43f2260801.png

图2.1-12

这段代码完成了以下几个任务:

    首先,通过 “torch.load” 函数加载了预训练的模型。模型文件路径为 “model/mnist_model.pth”,并使用 “map_location=torch.device('cpu')” 参数指定了在 CPU 上加载模型。

    然后,定义了一个名为 “recognize” 的识别函数,用于对绘制的图像进行预处理和识别。

在 “recognize” 函数中,首先对绘制的图像进行预处理。预处理过程包括将图像大小调整为 (28, 28)、转为灰度图像、转为张量,并进行归一化处理。

    接着,将处理后的图像添加批次维度,并使用模型进行识别。通过调用模型的 “forward” 方法,将处理后的图像输入模型中进行前向传播,得到输出结果。

    最后,从输出结果中获取预测结果,即使用 “torch.argmax” 函数找到概率最大的类别,并将其转换为整数形式。

    为了触发识别操作,代码创建了一个按钮控件 “recognize_button”,按钮上显示文本为 "识别",并通过 `command` 参数指定了点击按钮时执行的函数为 `recognize` 函数。

    这样,当用户绘制完手写数字后,可以点击 "识别" 按钮来进行识别操作,并将识别结果显示在界面上。

13、清除函数:

e6f76fbeb23240c097ab88addb8e7fa2.png

图2.1-13

       在上述代码中,添加了一个名为 “clear_canvas” 的函数,用于清除画布上的内容。在函数中,调用了 “canvas.delete("all")” 来删除画布上的所有图形对象,然后使用 “draw.rectangle” 方法将整个画布填充为白色。

    接下来,通过 “tk.Button” 创建了一个按钮控件 “clear_button”,按钮上显示文本为 "清除",并通过 “command” 参数指定了点击按钮时执行的函数为 “clear_canvas” 函数。

    另外,创建了一个名为 “result_label” 的标签控件,用于显示识别结果,默认文本内容为 "识别结果:"。

    最后,通过调用 “root.mainloop()” 启动主事件循环,使窗口进入等待用户操作的状态。

    这样,用户可以通过点击 "清除" 按钮来清除画布上的内容,方便重新绘制手写数字,并在标签控件中显示识别结果。主事件循环将保持运行,直到用户关闭窗口为止。

2. 调试

2.1 调试截图

67d9f317e30f4a4d873f823dcca4aabf.png

图3.1-1 “0”

9f525903aa014904969eff2d1f0c15e5.png

图3.1-2 “1”

80f00b350b194da6b78ce62c25bc02da.png

图3.1-3 “2”

4e558acea2544dfba50bc8dc4a21b1e0.png

图3.1-4 “3”

b4bc85367e9343e2a3482358696aa155.png

图3.1-5 “4”

70499d52d6f04797a2281d8cc4a6577c.png

图3.1-6 “5”

acb4d157a04d482aaf39f02ef61b5036.png

图3.1-7 “6”

6fe728701053488b964ec2b3c313332b.png

图3.1-8 “7”

89b7c7b7f38d407aa1d23933651f89f0.png

图3.1-9 “8”

11381abe23074f2ab63b3cd9c8eb8501.png

图3.1-10 “9

3.总结

        通过卷积神经网络和GUI界面的结合,为用户提供了一种直观的数字识别体验。这个项目不仅锻炼了我的编程技能,还深化了我的机器学习知识。我喜欢看到用户可以通过鼠标手写数字,然后看到模型准确地识别它们,这是一种满足感。预训练的模型使整个系统更具实用性。通过这个项目,我不仅提高了技术水平,还帮助了其他人解决了数字识别的难题。这个经验启发我在未来继续深入研究和开发机器学习应用程序,以改善人们的生活。

4. 附录

4.1 参考资料

参考博客:基于Pytorch的MNIST手写数字识别实现(含代码+讲解)_手写数字识别github_M_ovo的博客-CSDN博客

4.2 代码清单

import torch
import torchvision
from torch.utils.data import DataLoader
import torch.nn as nn
import torch.optim as optim
import time
import matplotlib.pyplot as plt
import random
from numpy import argmax

#基础参数设置----------------------------
epoch = 3                 #模型训练迭代次数
learning_rate = 0.001      #学习率
batch_size_train = 64     #每次训练使用的样本(图片)数量   
batch_size_test = 1000    #每次测试使用的样本(图片)数量
gpu = torch.cuda.is_available()  #设置GPU可用 
momentum = 0.5            #影响收敛的速度的参数

#导入数据-------------------------------
train_loader = 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)    #按照每次训练样本数将训练集划分成多组

test_loader = 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)    #按照每次测试样本数将测试集划分成多组

train_data_size = len(train_loader)    #训练集划分组数
test_data_size = len(test_loader)

#定义网络模型----------------------------
class Net(nn.Module):
   def __init__(self):
       super(Net,self).__init__()
       self.model = nn.Sequential(
           nn.Conv2d(in_channels=1, out_channels=32, kernel_size=3, stride=1, padding=1),    #卷积
           nn.ReLU(),    #激活
           nn.MaxPool2d(kernel_size=2, stride=2),    #最大池化
           nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, stride=1, padding=1),
           nn.ReLU(),
           nn.MaxPool2d(kernel_size=2, stride=2),
           nn.Flatten(),    #维度展开
           nn.Linear(in_features=3136, out_features=128),    #全连接
           nn.Linear(in_features=128, out_features=10),
      )
   
   def forward(self, x):    #定义网络前向传播
       return self.model(x)
if gpu:
   net = Net().cuda()
else:
   net = Net()

#定义损失函数和优化器---------------

if gpu: 
   loss_fn = nn.CrossEntropyLoss().cuda()    #定义损失函数
else:
   loss_fn = nn.CrossEntropyLoss()


optimizer = optim.SGD(net.parameters(), lr=learning_rate, momentum=0.9)    #定义优化器

#Train训练函数---------------------------------
total_train_step = 0

def train(epoch):
   global total_train_step    #定义了一个全局变量,用于计算训练次数
   total_train_step = 0
   for data in train_loader:  #按照分组循环遍历所有训练数据
       imgs,targets = data
       if gpu:
           imgs,targets = imgs.cuda(),targets.cuda()
       optimizer.zero_grad()
       outputs = net(imgs)    #将图片送入网络进行训练(前向传播)
       loss = loss_fn(outputs,targets)  #获取损失函数
       loss.backward()        #反向传播
       optimizer.step()       #更新模型参数
       if total_train_step % 200 == 0:    #每训练200组输出训练提示信息
           print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
               epoch, total_train_step, train_data_size,
               100. * total_train_step / train_data_size, loss.item()))
       total_train_step += 1
       
#Test测试函数,输出测试准确率---------------------------------
def test():
   correct = 0
   total = 0
   with torch.no_grad():
       for data in test_loader:
           imgs,targets = data
           if gpu:
               imgs,targets = imgs.cuda(),targets.cuda()
           outputs = net(imgs)    #进入模型进行测试
           _,predicted = torch.max(outputs.data,1)  #获取预测最有可能的分类信息
           total += targets.size(0)
           correct += (predicted == targets).sum().item()  #计算正确预测的数量
   print('Test Accuracy: {}/{} ({:.0f}%)'.format(correct, total, 100.*correct/total))  #correct/total正确预测所占比例
   return correct/total

#运行训练和测试函数,保存训练模型并输出测试准确率----------------------------------
for i in range(1,epoch+1):
   print("-----------------Epoch: {}-----------------".format(i))
   train(i)
   test()
   #save model
   torch.save(net,'model/mnist_model.pth')    #保存模型
   print('Saved model')


import tkinter as tk
from PIL import Image, ImageDraw
import torch
import torchvision.transforms as transforms

# 创建GUI窗口和画布
root = tk.Tk()
root.title("手写数字识别")
canvas_width = 500
canvas_height = 500
canvas = tk.Canvas(root, width=canvas_width, height=canvas_height, bg='white')
canvas.pack()
image = Image.new("L", (canvas_width, canvas_height), "white")
draw = ImageDraw.Draw(image)

# 定义鼠标事件处理函数,用于绘制数字
def paint(event):
    x1, y1 = (event.x - 10), (event.y - 10)
    x2, y2 = (event.x + 10), (event.y + 10)
    canvas.create_oval(x1, y1, x2, y2, fill="black", outline="black")
    draw.line([x1, y1, x2, y2], fill="black", width=20)

canvas.bind("<B1-Motion>", paint)

# 加载预训练的模型
model = torch.load('model/mnist_model.pth', map_location=torch.device('cpu'))
model.eval()

# 定义识别函数,将绘制的图像进行预处理和识别
def recognize():
    # 将绘制的图像进行预处理
    transformed_image = transforms.Compose([
        transforms.Resize((28, 28)),
        transforms.Grayscale(),
        transforms.ToTensor(),
        transforms.Normalize((0.1307,), (0.3081,))
    ])(image)

    # 添加批次维度并送入模型进行识别
    input_image = transformed_image.unsqueeze(0)
    output = model(input_image)

    # 获取预测结果并显示
    predicted = torch.argmax(output, dim=1).item()
    result_label.configure(text="识别结果:{}".format(predicted))

# 添加一个按钮用于触发识别
recognize_button = tk.Button(root, text="识别", command=recognize)
recognize_button.pack()

# 添加一个按钮用于清除画布
def clear_canvas():
    canvas.delete("all")
    draw.rectangle((0, 0, canvas_width, canvas_height), fill="white")

clear_button = tk.Button(root, text="清除", command=clear_canvas)
clear_button.pack()

result_label = tk.Label(root, text="识别结果:")
result_label.pack()
# 启动主事件循环
root.mainloop()

要实现GUI界面的MNIST手写数字识别,您可以使用Python的Tkinter模块来创建GUI界面,并使用Python的Keras库来构建和训练数字分类模型。以下是一个简单的实现步骤: 1. 导入所需的模块和库: ``` from tkinter import * from keras.models import load_model from PIL import Image, ImageDraw import numpy as np ``` 2. 创建GUI界面: ``` root = Tk() root.title("MNIST手写数字识别") root.geometry('280x280') canvas = Canvas(root, width=280, height=280, bg='white') canvas.pack() image = Image.new("L", (280, 280), 0) draw = ImageDraw.Draw(image) ``` 3. 加载预训练的Keras模型: ``` model = load_model('mnist_model.h5') ``` 4. 定义画布上的鼠标事件: ``` def paint(event): x1, y1 = (event.x - 10), (event.y - 10) x2, y2 = (event.x + 10), (event.y + 10) canvas.create_oval(x1, y1, x2, y2, fill='black', width=10) draw.line([x1, y1, x2, y2], fill='white', width=10) canvas.bind('<B1-Motion>', paint) ``` 5. 定义识别手写数字的函数: ``` def recognize(): digit = image.resize((28, 28)) digit = np.array(digit) digit = digit.reshape(1, 28, 28, 1) digit = digit.astype('float32') digit /= 255.0 result = model.predict(digit) result = np.argmax(result) label_result.config(text="识别结果:" + str(result)) ``` 6. 添加按钮和标签: ``` btn_recognize = Button(root, text="识别", command=recognize) btn_recognize.pack(side=BOTTOM) label_result = Label(root, text="") label_result.pack(side=BOTTOM) ``` 7. 运行GUI界面: ``` root.mainloop() ``` 这样,您就可以在GUI界面上手写数字,点击“识别”按钮,识别结果将显示在标签上。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值