赞
踩
目录
该程序实现了一个基于卷积神经网络的手写数字识别系统,用户可以使用鼠标在GUI界面上手写数字,然后通过加载预训练的模型进行数字识别,并在界面上显示识别结果。
原始测试数据来自MNIST数据集,其中包含了大量的手写数字图片。训练集用于训练模型,测试集用于评估模型在未见过的数据上的准确率。
1~8就是原来的代码,9~13是鼠标交互代码
1、导入所需库:
图2.1-1
这些库用于使用PyTorch进行深度学习、数据加载、定义神经网络、优化、可视化和处理数组。
2、设置基本参数:
图2.1-2
这段代码是用于设置基础参数和导入数据的部分。
3、加载MNIST数据集:
图2.1-3
4、定义网络模型:
图2.1-4
5、定义损失函数和优化器:
图2.1-5
6、训练函数:
图2.1-6
7、测试函数:
图2.1-7
8、保存模型:
图2.1-8
9、
图2.1-9
这部分代码引入了一些需要使用的库和模块:
通过引入这些库和模块,可以在后续的代码中使用它们的功能,例如创建窗口应用程序、处理图像数据等。
10、GUI窗口和画布:
图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、鼠标事件处理函数:.
图2.1-11
这段代码定义了一个名为 “paint” 的鼠标事件处理函数,用于在画布上绘制手写数字。
函数的参数 “event” 是一个鼠标事件对象,它包含了鼠标事件的信息,例如鼠标坐标等。
在 “paint” 函数中,我们通过 “event.x” 和 “event.y” 获取当前鼠标的坐标位置。然后,我们使用这个坐标位置作为中心,在画布上绘制一个黑色的圆形,形成了手写数字的笔画。
接着,我们使用 “draw.line” 方法在 “Image” 对象上绘制一条直线,这条直线连接了上一个鼠标位置和当前鼠标位置,也就是绘制了手写数字的轨迹。
当用户按住鼠标左键并在画布上移动时,就会触发 “<B1-Motion>” 事件,然后调用 “paint” 函数,从而实现了手写数字的绘制。
12、加载预训练模型及识别函数:
图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、清除函数:
图2.1-13
在上述代码中,添加了一个名为 “clear_canvas” 的函数,用于清除画布上的内容。在函数中,调用了 “canvas.delete("all")” 来删除画布上的所有图形对象,然后使用 “draw.rectangle” 方法将整个画布填充为白色。
接下来,通过 “tk.Button” 创建了一个按钮控件 “clear_button”,按钮上显示文本为 "清除",并通过 “command” 参数指定了点击按钮时执行的函数为 “clear_canvas” 函数。
另外,创建了一个名为 “result_label” 的标签控件,用于显示识别结果,默认文本内容为 "识别结果:"。
最后,通过调用 “root.mainloop()” 启动主事件循环,使窗口进入等待用户操作的状态。
这样,用户可以通过点击 "清除" 按钮来清除画布上的内容,方便重新绘制手写数字,并在标签控件中显示识别结果。主事件循环将保持运行,直到用户关闭窗口为止。
图3.1-1 “0”
图3.1-2 “1”
图3.1-3 “2”
图3.1-4 “3”
图3.1-5 “4”
图3.1-6 “5”
图3.1-7 “6”
图3.1-8 “7”
图3.1-9 “8”
图3.1-10 “9
通过卷积神经网络和GUI界面的结合,为用户提供了一种直观的数字识别体验。这个项目不仅锻炼了我的编程技能,还深化了我的机器学习知识。我喜欢看到用户可以通过鼠标手写数字,然后看到模型准确地识别它们,这是一种满足感。预训练的模型使整个系统更具实用性。通过这个项目,我不仅提高了技术水平,还帮助了其他人解决了数字识别的难题。这个经验启发我在未来继续深入研究和开发机器学习应用程序,以改善人们的生活。
参考博客:基于Pytorch的MNIST手写数字识别实现(含代码+讲解)_手写数字识别github_M_ovo的博客-CSDN博客
- 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()
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。