单隐层神经网络MNIST数据集分类实验:影响因素及结果分析
本次实验通过构建单隐层的神经网络,实现了MNIST数据集的分类任务,并通过配置不同隐层结点数量、不同学习率、不同激活函数等网络参数,观察分类准确率与总体均方误差。实验中,我们可以发现:
-
隐层结点数量的增加会提高分类准确率,但同时也会增加训练时间和计算资源的消耗。
-
学习率的选择需要在准确率和总体均方误差之间取得平衡,过高的学习率可能会导致网络无法收敛,过低的学习率则会使得网络收敛缓慢。
-
激活函数的选择对网络的性能有很大的影响,实验中我们尝试了sigmoid和ReLU两种激活函数,发现ReLU函数的表现更好,可以提高分类准确率和训练速度。
-
在测试MNIST数据集时,我们可以通过遍历所有测试数据,得出准确率。同时,我们还可以通过绘制学习曲线,即分类准确率和总体均方误差随训练世代的变化曲线,来直观地观察网络的训练过程和性能变化。
通过本次实验,我们对单隐层神经网络的构建、训练和测试有了更深入的理解,也了解了不同网络参数对网络性能的影响。在未来的研究中,可以尝试构建更复杂的网络结构,例如多隐层神经网络,并探索更多不同的激活函数和优化算法,以进一步提高分类准确率和网络性能。
以下为实验代码:
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 #导入文件对话框函数库
import matplotlib.pyplot as plt
#————————————————————————神经网络构建,三层结构——————————————————#
#激活函数
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)
pass
#训练神经网络
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))
pass
#接受输入,返回输出
#将输出进行激活,归一化
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
pass
pass
#——————————————初始化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, '请输入相关数据,构建一个网络
')
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网络构建成功!
')
text.insert(tk.END, '输入层节点数:'+var_inputs.get()+',隐藏层节点数:'+var_hidden.get()+',输出层节点数:'+var_outputs.get()+'
')
text.insert(tk.END, '学习率:' + var_lrate.get() + ',训练世代:' + var_epochs.get()+ '
')
text.insert(tk.END, '可以开始训练了!
')
#加载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) + '个世代
')
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, '训练完毕!
')
text.insert(tk.END, '可以开始测试你的网络了!
')
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数据集.....
')
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, '数据测试完毕
')
text.insert(tk.END, '正确率='+str(right_rate)+'%
')
text.update()
print('正确率= ', right_rate, '%')
accuracy_list = []
mse_list = []
for e in range(epochs):
print('训练中,第', e, '个世代')
t = 0
correct_count = 0
mse = 0.0
for record in training_data_list:
t += 1 # 用”,“来区分数据
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) # 计算网络的输出并判断是否正确
outputs = n.query(inputs)
label = np.argmax(outputs)
if label == int(all_values[0]):
correct_count += 1 # 更新总体均方误差
mse += np.sum(((targets - outputs) ** 2)) # 计算正确率和平均均方误差
accuracy = correct_count / len(training_data_list)
mse /= len(training_data_list)
accuracy_list.append(accuracy)
mse_list.append(mse) # 输出结果
print('正确率:', accuracy)
print('平均均方误差:', mse)
plt.plot(accuracy_list, label='Accuracy')
plt.plot(mse_list, label='MSE')
plt.legend()
plt.show()
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, '开始测试图片
')
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+'
')
# 用文件名来设置准确值标签
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, '输出节点的输出为
'+str(outputs)+'
')
text.update()
# 最高输出值所在的数字作为识别标签
label = np.argmax(outputs)
print('神经网络说:“它是', label, '”')
text.insert(tk.END, '神经网络认为图中的数字是' + str(label) + '
')
text.see(tk.END)
if (label == correct_label):
print('恭喜你,匹配成功!')
text.insert(tk.END, '恭喜你,识别成功了!
')
else:
print('很遗憾,识别失败了')
text.insert(tk.END, '很遗憾,识别失败了!再试一次吧
')
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
原文地址: https://www.cveoy.top/t/topic/k4XX 著作权归作者所有。请勿转载和采集!