使用BP神经网络实现手写数字识别,要求使用Keras框架、sklearn机器学习库、无框架自编代码三种方法实现分类

发布时间:2024年01月19日

目标:

对BP神经网络学习,从底层实现推理到调用sklearn库,搭建keras框架来对数据集进行处理分析得到模型的准确率。


题目

使用BP神经网络实现手写数字识别,要求使用Keras框架、sklearn机器学习库、无框架自编代码三种方法实现分类。

思路

1、使用底层实现整体做题思路和流程:

(1)数据读取;
(2)Sigmoid函数;
(3)神经网络的训练:
第一步,为每个神经元计算偏差8, 6是从后向前计算的,故称之为后向算法。对输出层来说,偏差是act (预测值-真值),act为激活函数。对隐层而言,需要从输出层开始,逐层通过权值计算得到。确切的说,例如对.上面的单隐层神经网络而言,隐层各神经元的6就是输出层6乘以对应的权值,如果输出层有多个神经元,则是各输出层神经元6按连接的权值加权生成隐层的神经元6。
第二步,更新权值,W=W+η6v其中η是学习率,是常数。v是要更新的权值输入端的数值,6是要更新的权值输出端的数值。例如更新隐层第一个神经元到输出层的权值,则6是第一步计算得到的输出层数值,v是该权值输入端的值,即隐层第-个神经元的激活值。
同理如果更新输入层第一个单元到隐层第一个单元的权值,6就是隐层第一个单元的值,而v是输入层第一个单元的值。偏置可以看作“1”对各神经元加权产生的向量,因而其更新方式相当于v=1的更新。
在编程中可以将1强行加入各层输入向量的末尾,从而不单独进行偏置更新,也可以不这样做,单独把偏置抽出来更新。该题算法采用的是第二种方法
(4)神经网络的测试。

2、使用Keras框架整体做题思路和流程:

(1)加载keras自带的mnist数据集:自带的mnist第一次需要下载,返回两个元组对象,每个元组里是两个numpy数组,把它拆分成四个numpy数据数据集;
(2)查看它们的形状;
(3)将训练数据 X_train,y_train、测试数据X_test,y_test转化成网络要求的数据格式(由灰度(0,255)转换为[0,1]之间的数据,并把数据类型由unit更改成float32;
(4)设计网络模型选择优化器、损失函数和监控指标:使用多层全连接神经网络模型;
(5)使用训练好的模型对test_image进行预测:调用网络的 fit 方法来完成。

3、调用sklearn库整体做题思路和流程:

(1)加载训练数据和文件;
(2)读取数据;
(3)使用神经网络方法得到训练模型:激活函数是logistic,梯度下降;
(4)测试集评价:准确率。

代码

第一种:底层实现

# -*- coding: utf-8 -*-
import numpy as np
import math
import scipy.io as sio

#sigmoid函数
def get(x):          
    act_vec=[]
    for i in x:
        act_vec.append(1/(1+math.exp(-i)))
    act_vec=np.array(act_vec)
    return act_vec

# 训练
def TrainNetwork(sample,label):
   sample_num = len(sample)      #训练集样本总数
   print ("训练集样本数:%d" %sample_num)#输出训练集样本数
   sample_len = len(sample[0])   #输入层节点数
   print('输入层神经元个数为:%d' %sample_len)#输出输入层节点数
   out_num = 10        #输出层神经元
   hid_num = 10        #隐藏层神经元
   w1 = 0.2 * np.random.random((sample_len, hid_num)) - 0.1#初始化输入层到隐层权矩阵
   w2 = 0.2 * np.random.random((hid_num, out_num)) - 0.1   #初始化隐层到输出层权矩阵
   hid_offset = np.zeros(hid_num)        #隐层偏置向量
   out_offset = np.zeros(out_num)        #输出层偏置向量
   input_learnrate = 0.2                 #输入层权值学习率
   hid_learnrate = 0.2                   #隐层学权值习率
   for i in range(0,len(sample)):
       t_label=np.zeros(out_num)
       t_label[label[i]]=1
       
       #前向的过程
       hid_value=np.dot(sample[i],w1)+hid_offset     #隐层的输入
       hid_act=get(hid_value)                        #隐层激活值,调用sigmoid函数
       out_value=np.dot(hid_act,w2)+out_offset
       out_act=get(out_value)                        #输出层激活值
       
       #后向过程
       err=t_label-out_act                            #输出值与真实值的误差
       out_delta=err*out_act*(1-out_act)              #输出层dela计算
       hid_delta = hid_act*(1 - hid_act) * np.dot(w2, out_delta)     #隐藏层dela计算
       for j in range(0,out_num):
           w2[:,j]+=hid_learnrate*out_delta[j]*hid_act             
       for k in range(0,hid_num):
           w1[:,k]+=input_learnrate*hid_delta[k]*sample[i]
       out_offset += hid_learnrate * out_delta   #阈值的更新
       hid_offset += input_learnrate * hid_delta
   return w1,w2,hid_offset,out_offset

# 测试网络
def Test(test_sample,test_label):
   right = np.zeros(10)
   numbers = np.zeros(10)
   for i in test_label:
       numbers[i]+=1
   print ('测试集中数字0到9的样本数:')
   print(numbers)
   for i in range(0,len(test_label)):
       hid_value=np.dot(test_sample[i],w1)+hid_offset
       hid_act=get(hid_value)
       out_value=np.dot(hid_act,w2)+out_offset
       out_act=get(out_value)
       if np.argmax(out_act) == test_label[i]:
           right[test_label[i]] += 1 
           result = right / numbers
           sum1 = right.sum()
           print ("测试集中数字0到9的预测正确率:")
           print (result)
           print ("测试集预测总正确率:")
           print(right.sum()/ len(test_label))
           

#读入数据
filename =('mnist_train.mat') 
sample = sio.loadmat(filename)
sample = sample["mnist_train"]
sample /= 256.0                           #特征向量归一化

filename = ('mnist_train_labels.mat')  
label = sio.loadmat(filename)
label = label["mnist_train_labels"]

#测试集
test_datafile=('mnist_test.mat')
test_sample=sio.loadmat(test_datafile)
test_sample=test_sample['mnist_test']
test_sample /= 256.0

test_labelfile=('mnist_test_labels.mat')
test_labelfile=sio.loadmat(test_labelfile)
test_label=test_labelfile['mnist_test_labels']
 
#函数调用
w1,w2,hid_offset,out_offset=TrainNetwork(sample,label)
Test(test_sample,test_label)
  

运行结果

在这里插入图片描述
在这里插入图片描述

第二种:mnist-keras实现

# -*- coding: utf-8 -*-
import tensorflow.keras as ka
import numpy as np 
import datetime

np.random.seed(0)
(X_train,y_train),(X_test,y_test)=ka.datasets.mnist.load_data()
num_pixels=X_train.shape[1]*X_train.shape[2]
#将二维的数组拉成一维的向量
X_train=X_train.reshape(X_train.shape[0],num_pixels).astype('float32')
X_test=X_test.reshape(X_test.shape[0],num_pixels).astype('float32')
                            
X_train=X_train/255
X_test=X_test/255

y_train=ka.utils.to_categorical(y_train)
y_test=ka.utils.to_categorical(y_test)
num_classes=y_test.shape[1]

#多层全连接神经网络模型
model=ka.Sequential([
    ka.layers.Dense(num_pixels, input_shape=(num_pixels,),
                    kernel_initializer='normal',activation='relu'),
    ka.layers.Dense(784,kernel_initializer='normal',activation='relu'),
    ka.layers.Dense(num_classes,kernel_initializer='normal',activation='softmax')
    ]) 
model.summary()
model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy'])
startdate=datetime.datetime.now() #获取当前时间
model.fit(X_train,y_train,validation_data=(X_test,y_test),epochs=20,batch_size=200,verbose=2)
enddate=datetime.datetime.now()
# print("训练用时:"+str(enddate=startdate))
   

运行结果

在这里插入图片描述

第三种:调用sklearn

# -*- coding: utf-8 -*-
import numpy as np     #导入numpy工具包
from os import listdir #使用listdir模块,用于访问本地文件
from sklearn.neural_network import MLPClassifier 


#加载训练数据
def img2vector(fileName):    
    retMat = np.zeros([1024],int) #定义返回的矩阵,大小为1*1024
    fr = open(fileName)           #打开包含32*32大小的数字文件 
    lines = fr.readlines()        #读取文件的所有行
    for i in range(32):           #遍历文件所有行
        for j in range(32):       #并将01数字存放在retMat中     
            retMat[i*32+j] = lines[i][j]    
    return retMat

def readDataSet(path):    
    fileList = listdir(path)    #获取文件夹下的所有文件 
    numFiles = len(fileList)    #统计需要读取的文件的数目
    dataSet = np.zeros([numFiles,1024],int) #用于存放所有的数字文件
    hwLabels = np.zeros([numFiles,10])      #用于存放对应的one-hot标签
    for i in range(numFiles):   #遍历所有的文件
        filePath = fileList[i]  #获取文件名称/路径      
        digit = int(filePath.split('_')[0])  #通过文件名获取标签      
        hwLabels[i][digit] = 1.0        #将对应的one-hot标签置1
        dataSet[i] = img2vector(path +'/'+filePath) #读取文件内容   
    return dataSet,hwLabels

#读取训练集
train_dataSet, train_hwLabels = readDataSet('trainingDigits') 

#使用神经网络方法得到训练模型
clf = MLPClassifier(hidden_layer_sizes=(100,),    
                       activation='logistic', solver='adam',
                       learning_rate_init = 0.0001, max_iter=2000)
print(clf)    #输出模型的参数
clf.fit(train_dataSet,train_hwLabels)

# 测试集评价
dataSet,hwLabels = readDataSet('testDigits')    #读取测试集
res = clf.predict(dataSet)   #对测试集进行预测
error_num = 0                #统计预测错误的数目
num = len(dataSet)           #测试集的数目
for i in range(num):         #遍历预测结果
   #比较长度为10的数组,返回包含01的数组,0为不同,1为相同
   #若预测结果与真实结果相同,则10个数字全为1,否则不全为1
   if np.sum(res[i] == hwLabels[i])< 10:
      error_num += 1
print("Total num:",num," Wrong num:",error_num,"  WrongRate:",error_num / float(num))

运行结果

在这里插入图片描述

总结

(1)BP学习算法可分为前向传播预测与反向传播学习两个过程。要学习的各参数值一般先作随机初始化。取训练样本输入网络,逐层前向计算输出,在输出层得到预测值,此为前向传播预测过程。根据预测值与实际值的误差再从输出层开始逐层反向调节各层的参数,此为反向传播学习过程。经过多样本的多次前向传播预测和反向传播学习过程,最终得到网络各参数的最终值,这是BP神经网络训练模型中的核心部分。
(2)保存神经网络效果差,原因是没对进行优化。对一个特征提取的程序优化特征,有助于提高判断准确率。
(3)关于隐层的层数和神经元节点数,隐层的层数这里只有一层,因为理论上三层神经网络可以完成任意多种的分类,实际上隐层的数目在12层为好,不要太多。隐层的神经元节点数是个神奇的存在,目前没有任何理论能给出最佳节点数,只有经验公式可用。这里用的经验公式是sqr(输入节点数+输出节点数)+09之间的常数,也可以用log2(输入节点数),具体多少需要反复验证。如果隐层节点数太多,程序一定会过拟合,正确率很低。

文章来源:https://blog.csdn.net/weixin_45652976/article/details/135686502
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。