机器学习应用——监督学习(下)“手写数字识别”(神经网络实现&KNN实现)

前言

机器学习应用博客中,将核心介绍三大类学习,即:无监督学习、监督学习、强化学习。

本篇将简要介绍监督学习的“手写数字识别”实例

运用MNIST数据集和DBRHD数据集,使用神经网络实现和KNN实现识别功能

一、实例介绍

1.图像识别(lmage Recognition)

(1)定义是指利用计算机对图像进行处理、分析和理解,以识别各种不同模式的目标和对像的技术。
(2)图像识别的发展经历了三个阶段:文字识别、数字图像处理与识别、物体识别。(机器学习领域一般将此类识别问题转化为分类问题。)

2.手写识别

(1)手写识别是常见的图像识别任务。
(2)计算机通过手写体图片来识别出图片中的字,与印刷字体不同的是,不同人的手写体风格迥异,大小不一,造成了计算机对手写识别任务的一些困难。
(3)数字手写体识别由于其有限的类别(0~9共10个数字)成为了相对简单的手写识别任务。DBRHD和MNIST是常用的两个数字手写识别数据集。

3.MNIST数据集

(1)下载链接:http://yann.lecun.com/exdb/mnist/
(2)MNIST是一个包含数字0~9的手写体图片数据集,图片已归一化为以手写数字为中心的28*28规格的图片。
(3)MNIST由训练集与测试集两个部分组成,各部分规模如下:
1)训练集:60,000个手写体图片及对应标签
2)测试集:10,000个手写体图片及对应标签
(4)手写数字样例
在这里插入图片描述

1)MNIST数据集中的每一个图片由28*28个像素点组成。
2)每个像素点的值区间为0~255,0表示白色,255表示黑色。

4.DBRHD数据集

(1)下载链接:https://archive.ics.uci.edu/ml/datasets/Pen-Based+Recognition+of+Handwritten+Digits
(2)DBRHD (Pen-Based Recognition of HandwrittenDigits Data Set)是UCI的机器学习中心提供的数字手写体数据库

(3)DBRHD数据集包含大量的数字0~9的手写体图片,这些图片来源于44位不同的人的手写数字,图片已归一化为以手写数字为中心的32*32规格的图片。

(4)DBRHD的训练集与测试集组成如下:
1)训练集:7,494个手写体图片及对应标签,来源于40位手写者。
2)测试集:3,498个手写体图片及对应标签,来源于14位手写者。

(5)数据集特点
1)去掉了图片颜色等复杂因素,将手写体数字图片转化为训练数据为大小32*32的文本矩阵。
2)空白区域使用0代表,字迹区域使用1表示。
在这里插入图片描述

5.手写识别实例

(1)已有许多模型在MNIST或DBRHD数据集上进行了实验,有些模型对数据集进行了偏斜矫正,甚至在数据集上进行了人为的扭曲、偏移、缩放及失真等操作以获取更加多样性的样本,使得模型更具有泛化性。

(2)常用于数字手写体的分类器:
1)线性分类器
2)K最近邻分类器
3)非线性分类器
4)SVM
5)MLP多层感知器
6)卷积神经网络
(3)后续任务:利用全连接的神经网络实现手写识别的任务

二、神经网络实现手写识别

1.任务介绍

(1)手写数字识别是一个多分类问题,共有10个分类,每个手写数字图像的类别标签是0~9中的其中一个数。例如下片的标签分别是0,1,2。
在这里插入图片描述

(2)任务:利用sklearn来训练一个简单的全连接神经网络,即多层感知机(Multilayer perceptron,MLP)用于识别数据集DBRHD的手写数字。

2.MLP的输入

(1)DBRHD数据集的每个图片是一个由0或1组成的3232的文本矩阵;
(2)多层感知机的输入为图片矩阵展开的1
1024个神经元。

3.MLP的输出(one-hot vectors)

(1)一个one-hot向量除了某一位的数字是1以外其余各维度数字都是0。
(2)图片标签将表示成一个只有在第n维度(从0开始)数字为1的10维向量。
(3)比如,标签0将表示成[1,0,0,0,0,0,0,0,0,0]o即,MLP输出层具有10个神经元。

4.MLP结构

(1)MLP的输入与输出层,中间隐藏层的层数和神经元的个数设置都将影响该MLP模型的准确率。
(2)然而目前的隐藏层只能通过经验性的手动设置
(3)在本实例中,我们只设置一层隐藏层,在后续实验中比较该隐藏层神经元个数为50、100、200时的MLP效果。
在这里插入图片描述

5.MLP手写识别实例构建

(1)建立工程并导人sklearn包
(2)加载训练数据
1)定义img2vector函数,将加载的32*32的图片矩阵展开成—列向量
2)定义加载训练数据的函数readDataSet ,并将样本标签转化为one-hot向量
3)调用readDataSet和img2vector函数加载数据,将训练的图片存放在train_dataSet中,对应的标签则存在train_hwLabels中

(3)训练神经网络
1)在sklearnBP .py文件中,构建神经网络∶设置网络的隐藏层数、各隐藏层神经元个数、激活函数、学习率、优化方法、最大迭代次数。
2)使用训练数据训练构建好的神经网络

(4)测试集评价
1)加载测试集
2)使用训练好的MLP对测试集进行测试,并计算错误率

#建立工程并导人sklearn包
import numpy as np
from os import listdir # 使用listdir模块,用于访问本地文件
from sklearn.neural_network import MLPClassifier

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

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

# 调用readDataSet和img2vector函数加载数据,将训练的图片存放在train_dataSet中,对应的标签则存在train_hwLabels中。
tarin_dataSet,train_hwLables = readDataSet('trainingDigits')

# 训练神经网络
# 在sklearnBP .py文件中,构建神经网络∶设置网络的隐藏层数、各隐藏层神经元个数、激活函数、学习率、优化方法、最大迭代次数。
clf = MLPClassifier(hidden_layer_sizes=(100,), #设置含100个神经元的隐藏层,hidden_layer_sizes存放的是一个元组,表示第i层隐藏层里神经元的个数
                    activation='logistic',solver='adam',#用logistic激活函数和adam优化方法,并令初始学习率为0.0001,迭代2000次
                    learning_rate=0.0001,max_iter=2000)
# 使用训练数据训练构建好的神经网络
#fit函数能够根据训练集及对应标签集自动设置多层感知机的输入与输出层的神经元个数。 例如train_dataset为n*1024的矩阵,train_hwLabels为n*10的矩阵则fit函数将MLP的输入层神经元个数设为1024,输出层神经元个数为10 :
clf.fit(tarin_dataSet,train_hwLables)

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

6.实验效果

(1)隐藏层神经元个数影响:
运行隐藏层神经元个数为50、100、200的多层感知机,对比实验效果:
在这里插入图片描述

1)随着隐藏层神经元个数的增加,MLP的正确率持上升趋势;
2)大量的隐藏层神经元带来的计算负担与对结果的提升并不对等,因此,如何选取合适的隐藏神经元个数是一个值得探讨的问题。

(2)迭代次数影响分析:
设隐藏层神经元个数为100,初始学习率为0.0001,最大迭代次数分别为500、1000、1500、2000,结果如下:
在这里插入图片描述

1)过小的迭代次数可能使得MLP早停,造成较低的正确率。
2)当最大迭代次数>1000时,正确率基本保持不变,这说明MLP在第1000迭代时已收敛,剩余的迭代次数不再进行。
3)—般设置较大的最大迭代次数来保证多层感知机能够收敛,达到较高的正确率。

(3)学习率影响分析:
改用随机梯度下降优化算法即将MLPclassifer的参数( solver=‘sgd’,),设隐藏层神经元个数为100,最大迭代次数为2000,学习率分别为:0.1、0.01、0.001、0.0001。
在这里插入图片描述

1)较小的学习率带来了更低的正确率,这是因为较小学习率无法在2000次迭代内完成收敛
2)而步长较大的学习率使得MLP在2000次迭代内快速收敛到最优解
3)因此,较小的学习率一般要配备较大的迭代次数以保证其收敛。

三、KNN实现手写识别

1.任务介绍

(1)本实例利用sklearn来训练一个K最近邻(k-NearestNeighbor,KNN)分类器,用于识别数据集DBRHD的手写数字。
(2)比较KNN的识别效果与多层感知机的识别效果。

2.KNN的输入

(1)DBRHD数据集的每个图片是一个由0或1组成的32*32的文本矩阵;
(2)KNN的输入为图片矩阵展开的一个1024维的向量。

3.KNN手写识别实体构建

(1)建立工程并导入sklearn包
(2)加载训练数据
1)定义img2vector函数,将加载的32*32的图片矩阵展开成—列向量
2)定义加载训练数据的函数readDataSet
3)调用readDataSet和img2vector函数加载数据,将训练的图片存放在train_dataSet中

(3)构建KNN分类器
1)设置查找算法以及邻居点数量(k)值
2)KNN是一种懒得学习法,没有学习过程,只在预测时去查找最近邻的点,数据集的输入就是构建KNN分类器的过程
3)构建KNN时调用fit函数

(4)测试集评价
1)加载测试集
2)使用训练好的KNN对测试集进行测试,并计算错误率

# 建立工程并导入sklearn包
import numpy as np
from os import listdir #使用listdir模块,用于访问本地文件
from sklearn import neighbors

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

# 定义加载训练数据的函数readDataSet
def readDataSet(path):
    fileList = listdir(path)#获取文件夹下的所有文件
    numFiles = len(fileList)#统计需要读取的文件的数目
    dataSet = np.zeros([numFiles,1024],int)#用于存放所有的数字文件
    hwLables = np.zeros([numFiles])#用于存放对应的标签(与神经网络不同)
    for i in range(numFiles):#遍历所有的文件
        filePath = fileList[i]#获取文件路径
        digit = int(filePath.split('_')[0])#通过文件名获取标签
        hwLables[i] = digit#直接存放数字,并非one-hot向量
        dataSet[i] = img2vector(path+'/'+filePath)#读取文件内容
    return dataSet,hwLables

# 调用readDataSet和img2vector函数加载数据,将训练的图片存放在train_dataSet中,对应的标签则存在train_hwLabels中。
tarin_dataSet,train_hwLables = readDataSet('trainingDigits')

# 构建KNN分类器
knn = neighbors.KNeighborsClassifier(algorithm='kd_tree',n_neighbors=3)
knn.fit(tarin_dataSet,train_hwLables)

# 测试集评价
dataSet,hwLables = readDataSet('testDigits') #加载测试集
# 使用训练好的KNN对测试集进行测试,并计算错误率
res = knn.predict(dataSet) #对测试集进行预测
error_num = np.sum(res!=hwLables)#统计分类错误的数目
num = len(dataSet) #测试集的数目
print("Total num:",num,"Wrong num:",
      error_num,"WrongRate:",error_num/float(num))

4.实验效果

(1)邻居数量K影响分析:设置K为1、3、5、7的KNN分类器,对比他们的实验效果。
在这里插入图片描述

1)K=3时正确率最高,当K>3时正确率开始下降
2)这是由于当样本为稀疏数据集时(本实例只有946个样本),其第k个邻居点可能与测试点距离较远,因此投出了错误的一票进而影响了最终预测结果。

(2)对比实验(KNN分类器vs.多层感知机)
我们取在上节对不同的隐藏层神经元个数、最大迭代次数、学习率进行的各个对比实验中准确率最高(H)与最差(L)的MLP分类器来进行对比。
在这里插入图片描述

(3)结论
1)KNN的准确率远高于MLP分类器,这是由于MLP在小数据集上容易过拟合的原因。
2)MLP对于参数的调整比较敏感,若参数设置不合理,容易得到较差的分类效果,因此参数的设置对于MLP至关重要。

总结

关于监督学习,比较核心的就是分类和回归问题,在此仅用此实例说明两大典型方法,其余便不多做赘述。关于代码之中的一些改进问题,由于用到库中的其他方法,且本人能力有限,大家感兴趣可自行查阅官网API。

关于分类,k近邻分类器、决策树、朴素贝叶斯,SVM需熟练掌握
关于回归,比较常用的为线性回归,至于多元非线性拟合及岭回归,根据数据特征不同可适用于不同场所

两点问题:
(1)代码运行需要基础数据支撑,py的自带库中有些内含所需数据,有些则没有,本篇并未放上数据txt文件,只是为了展示无监督学习的体系流程以作演示
(2)在库的包导入若发生问题,看看版本更新问题,以及部分包在近年来命名和函数有所调整,各位客官可面向百度

代码非原创,内容乃网课课件整理所得。
如有问题,欢迎指正!

下一篇将介绍深度学习。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
)">
下一篇>>