一、MNIST数据集和DBRHD数据集简介
MNIST数据集
MNIST数据集下载链接:http://yann.lecun.com/exdb/minst/
该数据集包含0-9的手写体图片数据集,并且图片已经归一化为以手写数字为中心的2828规格的图片。MNIST数据集由训练集和测试集组成,训练集由60000个手写体图片及对应标签,测试集有10000个手写体图片及对应标签。
1)MNIST数据集中的每个图片由2828个像素点组成。
2)每个像素点的值区间为0-255,0表示白色,255表示黑色。
DBRHD数据集
DBRHD数据集下载链接:
https://archive.ics.uci.edu/ml/datasets/Pen-Based+Recognition+of+Handwritten+Digits
DBRHD(Pen-Based Recognition of Handwritten Digits Data Set)是UCI的机器学习中心提供的数字手写体数据库。
DBRHD数据集包含0-9的手写体图片,这些图片来源于44位不同的人的手写数字,图片已归一化为以手写数字为中心的3232规格的图片。训练集有7494个手写体图片及对应标签,来源于40位手写者,测试集有3498个手写体图片及对应标签,来源于14位手写者。
DBRHD数据集特点:
1)去掉了图片颜色等复杂因素,将手写体数字图片转化我训练数据大小为3232的文本矩阵。
2)空白区域使用0代表,字迹区域使用1表示。
有许多模型对着两个数据集进行了实验,有些模型对数据集进行了偏斜矫正,甚至在数据集上进行了认为的扭曲、偏移、缩放即失真等操作以获取更加多样性的样本,使得模型更具有泛化性。
常用语数字手写体的分类器:线性分类器、K近邻分类器、非线性分类器、SVM、MLP多层感知器、卷积神经网络
二、神经网络实现手写识别
任务介绍
手写数字识别是一个多分类问题,有10个分类,每个图像的类别标签是0-9中的一个数。
任务:
利用sklearn来训练一个简单的全连接神经网络,即多层感知机(Multilayer perceptron,MLP)用于识别数据集DBRHD的手写数字。
1、MLP的输入:
1)DBRHD数据集的每个图片是一个由0或1组成的3232的文本矩阵;
2)多层感知机的输入为图片矩阵展开的11024个神经元
2、MLP的输出
MLP输出:“one-hot vectors”
1)一个one-hot向量除了某一位的数字是1以外其余各维度数字都是0。
2)图片标签将表示成一个只有在第n维度(从0开始)数字为1的10维向量。 比如,标签0表示成[1,0,0,0,0,0,0,0,0,0]。即MLP输出层具有10个神经元。
在实例中,设置一层隐藏层,比较该隐藏层神经元个数为50、100、200时的MLP效果。
MLP手写识别实例
1、建立工程并导入sklearn包
1)创建sklearnBP.py文件
2)在sklearnBP.py文件中导入sklearn相关包
#导入numpy工具包
import numpy as np
#使用listdir模块,用于访问本地文件
from os import listdir
from sklearn.neural_network import MLPClassifier
2、加载训练数据
1)在sklearnBP.py文件中,定义img2vector函数,将加载的32*32的图片矩阵展开成一列向量。
def img2vector(fileName)
#定义返回的矩阵,大小为1*1024
retMat = np.zeros([1024],int)
#打开包含32*32大小的数字文件
fr = open(fileName)
#读取文件的所有行
lines = fr.readlines()
#遍历文件所有行
for i in range(32):
#并将01数字存放在retMat中
for j in range(32):
retMat[i*32+j] = lines[i][j]
return retMat
2)在sklearnBP.py文件中定义加载训练数据的函数readDataSet,并将样本标签转化为one-hot向量。
def readDataSet(path)
#获取文件夹下的所有文件
fileList = listdir(path)
#统计需要读取的文件的数目
numFiles = len(fileList)
#用于存放所有的数字文件
dataSet = np.zeros([numFiles,1024],int)
#用于存放对应的标签one—hot
hwLabels = np.zeros([numFiles,10])
#遍历所有文件
for i in range(numFiles):
#获取文件名称/路径
filePath = fileList[i]
#通过文件名获取标签
digit = int(filePath.split('_')[0])
#将对应的one-hot标签置1
hwLabels[i][digit] = 1.0
#读取文件内容
dataSet[i] = img2vector(path + '/'+filePath)
return dataSet,hwLabels
3)在sklearnBP.py文件中,调用readDataSet和img2vector函数加载数据,将训练的图片存放在train_dataSet中,对应的标签则存在train_hwLabels中。
train_dataSet,train_hwLabels = readDataSet('trainingDigits')
3、训练神经网络
1)在sklearnBP.py文件中,构建神经网络:设置网络的隐藏层数、各隐藏层神经元个数、激活函数、学习率、优化方法、最大迭代次数。
clf = MLPClassifier(hidden_layer_sizes=(100,),
activation='logistic',solver='adam',learning_rate_init=0.0001,
max_iter=2000)
(1)设置含100个神经元的隐藏层。
(2)hidden_layer_sizes存放的是一个元祖,表示第i层隐藏层里神经元的个数。
(3)使用logistic激活函数和adam优化方法,并令初始学习率为0.0001,迭代2000次。
clf.fit(train_dataSet,train_hwLabels)
(1)fit函数能够根据训练集及对应标签集自动设置多层感知机的输入与输出层的神经元个数。
(2)例如:train_dataSet为n1024的矩阵,train_hwLabels为n10的矩阵,则fit函数将MLP的输入层神经元个数设为1024,输出层神经元个数为10。
4、测试集评价
1)在sklearnBP.py文件中,使加载测试集。
dataSet,hwLabels = readDataSet('testDigits')
2)使用训练好的MLP对测试集进行预测,并计算错误率。
#对测试集进行预测
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))
5、实验结论
1)隐藏层神经元个数带来的影响
(1)随着隐藏层神经元个数的增加,MLP的正确率持上升趋势
(2)大量的隐藏层神经元带来的计算负担与对结果的提升并不对等,因此, 如何选取合适的隐藏神经元个数是一个值得探讨的问题。
2)迭代次数带来的影响
(1)过小的迭代次数可能使得MLP早停,造成较低的正确率
(2)当最大迭代次数>1000时,正确率基本保持不变,这说明MLP在第1000迭代时已收敛,剩余的迭代次数不再进行。
(3)一般设置较大的迭代次数来保证多层感知机能够收敛,达到较高的正确率。
3)学习率带来的影响
改用随机梯度下降优化算法将MLPclassifer的参数(solver=‘sgd’),设隐藏层神经元个数为100,最大迭代次数为2000,学习率分别为:0.1、0.01、0.001、0.0001。
较小的学习率带来的更低的正确率,这是因为较小的学习率无法在2000次迭代内完成收敛,而步长较大的学习率使得MLP在2000次迭代内快速收敛到最优解。因此,较小的学习率一般要配备较大的迭代次数以保证其收敛。
三、KNN实现手写识别
任务介绍
1)本实例利用sklearn来训练一个K近邻(K-Nearnest Neighbor,KNN)分类器,用于识别数据集DBRHD的手写数字。
2)比较KNN的识别效果与多层感知机的识别效果。
KNN的输入
1)DBRHD数据集的每个图片是一个由0或1组成的32*32的文本矩阵;
2)KNN的输入为图片矩阵展开的一个1024维的向量。
KNN手写识别实体构建
1、建立工程并导入sklearn包
1)创建sklearnKNN.py文件
2)在sklearnKNN.py文件中导入sklearn相关包
#导入numpy工具包
import numpy as np
#使用listdir模块,用于访问本地文件
from os import listdir
form sklearn import neighbors
2、加载训练数据
1)在sklearnKNN.py文件中,定义img2vector函数,将加载的32*32的图片矩阵展开成一列向量。
def img2vector(fileName):
#定义返回的矩阵,大小为1*1024
retMat = np.zeros([1024],int)
#打开包含32*32大小的数字文件
fr = open(fileName)
#读取文件的所有行
lines = fr.readlines()
#遍历文件所有行
for i in range(32):
for j in range(32):
#将01数字存放在retMat中
retMat[i*32+j] = lines[i][j]
return retMat
2)在sklearnKNN.py文件中定义加载训练数据的函数readDataSet。
def readDataSet(path):
#获取文件夹下的所有文件
fileList = listdir(path)
#统计需要读取的文件的数目
numFiles = len(fileList)
#用于存放所有的数字文件
dataSet = np.zeros([numFiles,1024],int)
#用于存放对应的标签(与神经网络的不同)
hwLabels = np.zeros([numFiles])
#遍历所有的文件
for i in range(numFiles):
#获取文件名称/路径
filePath = fileList[i]
#通过文件名获取标签
digit = int(filePath.split('_')[0])
#直接存放数字,并非one-hot向量
hwLabels[i] = digit
#读取文件内容
dataSet[i] = img2vector(path +'/'+filePath)
return dataSet,hwLabels
3、构建KNN分类器
在sklearnKNN.py文件中,构建KNN分类器:设置查找算法以及邻居点数量(K)值。
1)KNN是一种懒惰学习法,没有学习过程,只在预测时去查找最近邻的点,数据集的输入就是构建KNN分类器的过程。
2)构建KNN时我们同时调用了fit函数。
knn = neighbors.KNeighborsClassifier(algorithm='kd_tree',n_neighbors=3)
knn.fit(train_dataSet,train_hwLabels)
4、测试集评价
1)加载测试集:
dataSet,hwLabels = readDataSet('testDigits')
2)使用构建好的KNN分类器对测试集进行预测,并计算预测的错误率
#对测试集进行预测
res = knn.predict(dataSet)
#统计分类错误的数目
error_num = np.sum(res != hwLabels)
#测试集的数目
num = len(dataSet)
print("Total num:",num,"Wrong num:",\
error_num,"WrongRate:",error_num/float(num))
实验效果
邻居数量K进行分析:设置K为1、3、5、7的KNN分类器,对比他们的实验效果。
当样本为稀疏数据集时(本实例只有946个样本),其第k个邻居点可能与测试点距离较远,因此投出了错误的一票进而影响了最终预测结果。
对比实验
KNN分类器与多层感知机对比
我们取在对不同的隐藏层神经元个数、最大迭代次数、学习率进行的各个对比实验中准确率最高(H)与最差(L)的MLP分类器来进行对比。
将效果最好的KNN分类器和效果最差的KNN分类器与各个MLP分类器作对比。
对比结论
1)KNN的准确率远高于MLP分类器,这是由于MLP在小数据集上容易过拟合的原因。
2)MLP对于参数的调整比较敏感,若参数设置不合理,容易得到较差的分类效果,因此参数的设置对于MLP至关重要。