本次实验通过构建单隐层的神经网络,实现了MNIST数据集的分类任务,并通过配置不同隐层结点数量、不同学习率、不同激活函数等网络参数,观察分类准确率与总体均方误差。实验中,我们可以发现:

  1. 隐层结点数量的增加会提高分类准确率,但同时也会增加训练时间和计算资源的消耗。

  2. 学习率的选择需要在准确率和总体均方误差之间取得平衡,过高的学习率可能会导致网络无法收敛,过低的学习率则会使得网络收敛缓慢。

  3. 激活函数的选择对网络的性能有很大的影响,实验中我们尝试了sigmoid和ReLU两种激活函数,发现ReLU函数的表现更好,可以提高分类准确率和训练速度。

  4. 在测试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
单隐层神经网络MNIST数据集分类实验:影响因素及结果分析

原文地址: https://www.cveoy.top/t/topic/k4XX 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录