MNIST 数据集分类:单隐层神经网络实现
MNIST 数据集分类:单隐层神经网络实现
本文使用 Python 构建单隐层神经网络,实现 MNIST 手写数字数据集的分类任务。通过调整隐层节点数量、学习率和激活函数等参数,观察分类准确率和总体均方误差的变化。
代码实现
# -*- coding: utf-8 -*-
'''
Created on Sat Apr 29 19:44:55 2023
@author: Lenovo
'''
import PIL
import numpy as np
import pandas as pd
import imageio
from PIL import Image, ImageTk # 导入图像处理函数库
import tkinter as tk
from tkinter import constants, ttk
from tkinter import filedialog #导入文件对话框函数库
#————————————————————————神经网络构建,三层结构——————————————————#
#激活函数
def sigmoid(x):
return 1.0 / (1.0 + np.exp(-x))
#定义神经网络函数
class neuralNetwork:
#初始化神经网络
def __init__(self,inputnodes,hiddennodes,outputnodes,learnrate):
#设立每个神经网络的输入、隐藏、输出层的节点数
self.inodes = inputnodes
self.hnodes = hiddennodes
self.onodes = outputnodes
#设置学习率
self.lrate = learnrate
self.wi_h = (np.random.rand(self.hnodes,self.inodes)-0.5)
self.wh_o = (np.random.rand(self.onodes,self.hnodes)-0.5)
#训练神经网络
def train(self,inputs_list,targets_list):
#输入与标准结果
inputs = np.array(inputs_list,ndmin=2).T
targets= np.array(targets_list, ndmin=2).T
#计算隐藏层的信号值
hidden_inputs = np.dot(self.wi_h,inputs)
hidden_outputs = sigmoid(hidden_inputs)
#计算输出层的信号值
outputs_inputs = np.dot(self.wh_o, hidden_outputs)
outputs_outputs = sigmoid(outputs_inputs)
#计算误差:精确值-实际值
output_errors = targets - outputs_outputs
hidden_errors = np.dot(self.wh_o.T,output_errors)
#根据公式得出的表达式,直接用
self.wh_o += self.lrate * np.dot((output_errors*outputs_outputs*(1.0-outputs_outputs)),
np.transpose(hidden_outputs))
self.wi_h += self.lrate * np.dot((hidden_errors * hidden_outputs * (1.0 - hidden_outputs)),
np.transpose(inputs))
#接受输入,返回输出
#将输出进行激活,归一化
def query(self,input_list):
inputs = np.array(input_list,ndmin=2).T
hidden_inputs = np.dot(self.wi_h, inputs)
hidden_outputs = sigmoid(hidden_inputs)
outputs_inputs = np.dot(self.wh_o,hidden_outputs)
outputs_outputs = sigmoid(outputs_inputs)
return outputs_outputs
#——————————————初始化GUI界面——————————————--#
window = tk.Tk()
window.title('神经网络识别MNIST数据集')
window.geometry('600x500')
global img_png # 定义全局变量 图像的
var = tk.StringVar() # 这时文字变量储存器
text = tk.Text(window,width=20,height=17)
text.pack(fill=tk.X,side=tk.BOTTOM)
text.insert(tk.END, '请输入相关数据,构建一个网络\n')
def craet_BPNN():
global n
global input_nodes
global hidden_nodes
global output_nodes
global learning_rate
global epochs
global training_data_list
#—————————————创建神经网络对象并用数据集训练网络——————————#
# 输入、隐藏、输出节点数
input_nodes =int(var_inputs.get())
hidden_nodes= int(var_hidden.get())
output_nodes = int( var_outputs.get())
# 学习率
learning_rate = float(var_lrate.get())
epochs = int(var_epochs.get())
# 创建神经网络对象
n = neuralNetwork(input_nodes, hidden_nodes, output_nodes, learning_rate)
text.insert(tk.END, 'BP网络构建成功!\n')
text.insert(tk.END, '输入层节点数:'+var_inputs.get()+',隐藏层节点数:'+var_hidden.get()+',输出层节点数:'+var_outputs.get()+'\n')
text.insert(tk.END, '学习率:' + var_lrate.get() + ',训练世代:' + var_epochs.get()+ '\n')
text.insert(tk.END, '可以开始训练了!\n')
#加载mnist数据集
training_data_file = open("mnist_train.csv", 'r')
training_data_list = training_data_file.readlines()
training_data_file.close()
#开始训练函数,训练MNist数据集
def beg_train():
for e in range(epochs):# 训练的世代,一次训练完成表示训练一个世代
print("训练中,第", e, "个世代")
text.insert(tk.END, '训练中,第' + str(e) + '个世代\n')
text.update()
t=0
for record in training_data_list:
t+=1
print("已训练",t,"个数据")
# 用”,“来区分数据
all_values = record.split(',')
# 将输入缩放和转换
inputs = (np.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01
# 将目标的输出值的0改为0.01,1改为0.99
targets = np.zeros(output_nodes) + 0.01
targets[int(all_values[0])] = 0.99
n.train(inputs, targets)
pass
text.insert(tk.END, '训练完毕!\n')
text.insert(tk.END, '可以开始测试你的网络了!\n')
pass
# 打开测试数据集MNIST-test
# 开始测试函数,遍历所有测试集中的测试数据,得出准确率
#————————————————————————测试MNIst数据集————————————————————#
def beg_test():
global test_data_list
#数据集的文件路径由自己定义,这是我自己的路径
test_data_file = open("mnist_test.csv", 'r')
test_data_list = test_data_file.readlines()
test_data_file.close()
all_values = test_data_list[0].split(',')
print(all_values[1])
n.query((np.asfarray(all_values[1:])/255.0*0.99)+0.01)
#用来存放分数,即正确率
scorecard = []
text.insert(tk.END, '开始测试MNIST数据集.....\n')
for record in test_data_list:
#用”,“号分开数据
all_values = record.split(',')
# 用准确值标签记录数字准确值
correct_label = int(all_values[0])
print("---------")
print("正确结果",correct_label)
# 缩放
inputs = (np.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01
# 计算输出
outputs = n.query(inputs)
# 输出的最大值即为判断值
label = np.argmax(outputs)
print( "神经网络判断",label)
# 将正确和错误的判断形成一个列表
if (label == correct_label):
# 正确为1
scorecard
scorecard.append(1)
else:
# 错误为0
scorecard
scorecard.append(0)
print(scorecard)
scorecard_array = np.asarray(scorecard)
#正确率
right_rate = (scorecard_array.sum() / scorecard_array.size) * 100
text.insert(tk.END, '数据测试完毕\n')
text.insert(tk.END, '正确率='+str(right_rate)+'%\n')
text.update()
print("正确率= ", right_rate, "%")
pass
#打开图片的函数,并尝试识别自己的图片
#编写GUI,让其更容易交互
def Open_Img():
global img_png
global path
global label_Img
OpenFile = tk.Tk() # 创建新窗口
OpenFile.withdraw()
file_path = filedialog.askopenfilename()
print("训练已结束,开始测试图片")
text.insert(tk.END, '开始测试图片\n')
path=file_path
Img =Image.open(file_path)
img_png = ImageTk.PhotoImage(Img)
label_Img = tk.Label(window, image=img_png)
Label_Show = tk.Label(window, image=img_png,
# 使用 textvariable 替换 text, 因为这个可以变化
bg='white', font=('Arial', 12), width=60, height=60)
Label_Show.place(x=80, y=80)
var.set('图像已打开')
#自己图片的数据存放在这里
our_own_dataset = []
image_file_name = path
print("加载中 ... ", image_file_name)
text.insert(tk.END, '加载中....'+image_file_name+'\n')
# 用文件名来设置准确值标签
label = int(image_file_name[20])
#将图片转换为数组
img_array = imageio.imread(image_file_name, as_gray=True)
# 将图片从28X28的数组转换成长为784的array
img_data = img_array.reshape(784)
# 缩放灰度值为0-1范围内
img_data = (img_data / 255.0 * 0.99) + 0.01
print("图像最小值为",np.min(img_data))
print("图像最大值为",np.max(img_data))
# 将标签值放到数组第一个
record = np.append(label, img_data)
our_own_dataset.append(record)
item = 0
correct_label = our_own_dataset[item][0]
# 将转换值作为输入
inputs = our_own_dataset[item][1:]
# 计算网络的输出
outputs = n.query(inputs)
print("输出节点的输出为:",outputs)
text.insert(tk.END, '输出节点的输出为\n'+str(outputs)+'\n')
text.update()
# 最高输出值所在的数字作为识别标签
label = np.argmax(outputs)
print("神经网络说:“它是", label, "”")
text.insert(tk.END, '神经网络认为图中的数字是' + str(label) + '\n')
text.see(tk.END)
if (label == correct_label):
print("恭喜你,匹配成功!")
text.insert(tk.END, '恭喜你,识别成功了!\n')
else:
print("很遗憾,识别失败了")
text.insert(tk.END, '很遗憾,识别失败了!再试一次吧\n')
pass
#显示图片的函数
def SHOW():
global img_png
Label_Show = tk.Label(window, image=img_png,
# 使用 textvariable 替换 text, 因为这个可以变化
bg='white', font=('Arial', 12), width=60, height=60)
Label_Show.place(x=80, y=80)
pass
img_frame = tk.LabelFrame(window, text='图像显示', padx=10, pady=10,
width=120,height=120)
img_frame.place(x=55,y=50)
# 创建文本窗口,显示当前操作8状态
in_lable=tk.Label(window,text='输入层节点数:')
in_lable.pack()
in_lable.place(x=300,y=40)
var_inputs=tk.StringVar()
var_inputs.set('784')
entry_inputs=tk.Entry(window,textvariable=var_inputs,width=10)
entry_inputs.place(x=380,y=40)
hi_lable=tk.Label(window,text='隐藏层节点数:')
hi_lable.pack()
hi_lable.place(x=300,y=70)
var_hidden=tk.StringVar()
var_hidden.set('50')
entry_hidden=tk.Entry(window,textvariable=var_hidden,width=10)
entry_hidden.place(x=380,y=70)
out_lable=tk.Label(window,text='输出层节点数:')
out_lable.pack()
out_lable.place(x=300,y=100)
var_outputs=tk.StringVar()
var_outputs.set('10')
entry_outputs=tk.Entry(window,textvariable=var_outputs,width=10)
entry_outputs.place(x=380,y=100)
rate_lable=tk.Label(window,text='学习率:')
rate_lable.pack()
rate_lable.place(x=300,y=130)
var_lrate=tk.StringVar()
var_lrate.set('0.1')
entry_lrate=tk.Entry(window,textvariable=var_lrate,width=10)
entry_lrate.place(x=380,y=130)
epochs_lable=tk.Label(window,text='训练世代:')
epochs_lable.pack()
epochs_lable.place(x=300,y=160)
var_epochs=tk.StringVar()
var_epochs.set('5')
entry_epochs=tk.Entry(window,textvariable=var_epochs,width=10)
entry_epochs.place(x=380,y=160)
#训练数据集按钮
btn_train = tk.Button(window,text='构建网络',width=15, height=2,
command=craet_BPNN)
btn_train.pack()
btn_train.place(x=30,y = 210)
#测试数据集按钮
btn_test = tk.Button(window,text='训练数据集',width=15, height=2,
command=beg_train)
btn_test.pack()
btn_test.place(x=170,y=210)
# 创建打开图像按钮
btn_Open = tk.Button(window,
text='测试数据集', # 显示在按钮上的文字
width=15, height=2,
command=beg_test) # 点击按钮式执行的命令
btn_Open.pack()
# 按钮位置
btn_Open.place(x=310,y=210)
# 创建显示图像按钮
btn_Show = tk.Button(window,
text='打开测试图片', # 显示在按钮上的文字
width=15, height=2,
command=Open_Img) # 点击按钮式执行的命令
btn_Show.pack()
# 按钮位置
btn_Show.place(x=450,y=210)
# 运行整体窗口
window.mainloop()
pass
实验结果分析
通过调整隐层节点数量、学习率和激活函数等参数,可以观察到以下规律:
- 隐层节点数量:随着隐层节点数量的增加,网络的表达能力增强,分类准确率在一定范围内提高。但当隐层节点数量过多时,可能会导致过拟合现象,造成训练集上表现良好,测试集上表现较差。
- 学习率:学习率决定了网络参数更新的步长。学习率过大,可能导致参数震荡,无法收敛到最优解;学习率过小,则训练速度过慢。需要根据具体情况选择合适的学习率。
- 激活函数:不同的激活函数具有不同的特点,例如 sigmoid 函数会将输出值压缩到 0 到 1 之间,ReLU 函数可以避免梯度消失问题。选择合适的激活函数可以提高网络的训练效率和分类效果。
总结
单隐层神经网络可以用于解决 MNIST 数据集的分类任务,但其性能受网络参数的影响很大。需要通过实验选择合适的网络参数,才能获得最佳的分类效果。
其他
- 以上代码实现仅供参考,可以使用其他深度学习框架,例如 TensorFlow 或 PyTorch,来实现更为复杂的神经网络结构。
- 可以尝试使用其他数据集,例如 CIFAR-10 或 ImageNet,来测试神经网络的泛化能力。
- 可以尝试使用更先进的网络结构,例如卷积神经网络 (CNN),来提高分类准确率。
原文地址: https://www.cveoy.top/t/topic/k4WM 著作权归作者所有。请勿转载和采集!