前言:本作者书写的博文中所包含的所有文件,读者均可从作者提供的链接中下载,创作不易,多谢理解!
2.3手写识别系统
本节我们将一步步地构建使用k-近邻分类器的手写识别系统,
老规矩,先附上文件下载链接:https://download.csdn.net/download/qq_38172282/11232509
链接文件中的材料如下图所示:
准备数据:将图像转换为测试向量
digits.zip文件为压缩文件,里面包含testDigits+trainingDigits文件
testDigits文件为测试用的手写数字数据,包含大约900个测试数据,
trainingDigits文件为训练用的手写数字数据,包含大约2000个测试数据,每个数字约含有200个样本。
上述文件中:文件名 a_b.txt 中,a代表的是文件中展示的数字,b代表的是数字的编号,内容是32*32的0和1组成的图案。
本节代码较为简单,首先创建一个1*1024的Numpy数组,然后打开给定的文件,循环读出文件的前32行,并将每行的头32个字符值存储在Numpy数组中,最后返回数组!
话不多说,可直接运行代码如下所示:
import numpy as np
#将32*32的二进制图像矩阵转换为1*1024的向量
def img2vector(filename):
returnVect = np.zeros((1,1024))
fr = open(filename)
for i in range(32):
lineStr = fr.readline()
for j in range(32):
returnVect[0,32*i+j] = int(lineStr[j])
return returnVect
testVector = img2vector('testDigits/0_13.txt')
print(testVector[0, 0:31])
print(testVector[0, 32:63])
运行结果如下:
测试算法:使用K-近邻算法识别手写数字
1、对训练集,获取目录下的txt文件名字,组成列表。在for循环中,通过对文件名的字符操作可以得到Label(标签),然后打开文件,将32*32转成1*1024,最后得到训练集的特征矩阵和标签向量
2、对测试集,一样得到标签和特征向量,然后将特征向量、训练集特征矩阵和标签向量、k=3输入def classify0,返回预测标签,与实际标签比对。
在下面的代码中,为了读者能理解程序清单2-6,我特意添加了较为详细的备注!
可直接运行代码如下所示:
import numpy as np
import operator
from os import listdir #用于获取文件夹下的文件名
# #对目标元素进行分类
# #程序清单2-1
# def classify0(inX, dataSet, labels, k):
# dataSet_size = dataSet.shape[0]
# # 在列方向上重复inX,1次,行方向上重复inX,dataSet_size次
# diff_mat = np.tile(inX, (dataSet_size, 1)) - dataSet
# # diff_mat:输入样本与每个训练样本的差值,然后对其每个x和y的差值进行平方运算
# sq_diff_mat = diff_mat ** 2
# # 按行进行累加,axis=1表示按行。
# sq_distances = sq_diff_mat.sum(axis=1)
# # 开方运算,求出距离
# distances = sq_distances ** 0.5
# # 返回distances中元素从小到大排序后的索引值
# sorted_dist_indices = distances.argsort()
# # 定一个字典:统计类别次数
# class_count = {}
# for i in range(k):
# # 取出前k个元素的类别
# vote_index_label = labels[sorted_dist_indices[i]]
# # 统计类别次数
# class_count[vote_index_label] = class_count.get(vote_index_label, 0) + 1
# # 把分类结果进行降序排序,然后返回得票数最多的分类结果
# sorted_class_count = sorted(class_count.items(), key=operator.itemgetter(1), reverse=True)
# return sorted_class_count[0][0]
#与书中程序清单相同,不过是改进后的K-近邻算法
def classify0(X, dataset, labels, k):
size = dataset.shape[0]
dis = np.sqrt(np.sum(np.square(dataset-X), axis=1)) #差值后平方、求和、开方
sort_index = np.argsort(dis) #按照距离排序,返回的是排序后的索引
label_count = {}
for i in range(k):
label = labels[sort_index[i]]
label_count[label] = label_count.get(label, 0)+1
sort_label = sorted(label_count.items(), key=lambda d: d[1], reverse=True) #按照标签出现次数进行排序
return sort_label[0][0]
#将32*32的二进制图像矩阵转换为1*1024的向量
def img2vector(filename):
returnVect = np.zeros((1, 1024))
fr = open(filename)
for i in range(32):
lineStr = fr.readline()
for j in range(32):
returnVect[0,32*i+j] = int(lineStr[j])
return returnVect
def handwritingClassTest():
# 初始化我们所需要的数据
hwLabels = []
# 这里需要我们提前导入os模块,listdir可以列出给定目录下的文件名
trainingFileList = listdir('trainingDigits') # 加载训练集
m = len(trainingFileList) # 获得训练样本数目
trainingMat = np.zeros((m,1024)) # 构造m×1024的矩阵
# 循环遍历训练集中的每个文件,生成每个数字的向量信息,保存在trainingMat中
for i in range(m):
fileNameStr = trainingFileList[i] # 获得文件名
fileStr = fileNameStr.split('.')[0] # 分离.txt
classNumStr = int(fileStr.split('_')[0]) # 获得该文件所代表的数字
hwLabels.append(classNumStr) # 将文件所代表的数字其存放在类别标签中
trainingMat[i,:] = img2vector('trainingDigits\\%s' % fileNameStr)
# 遍历测试数据文件夹,使用KNN进行测试。
testFileList = listdir('testDigits') # 遍历测试集
errorCount = 0.0
mTest = len(testFileList) # 获得测试样本数目
for i in range(mTest):
fileNameStr = testFileList[i]
fileStr = fileNameStr.split('.')[0] # 分离.txt
classNumStr = int(fileStr.split('_')[0]) # 获得该文件所代表的数字
vectorUnderTest = img2vector('testDigits\\%s' % fileNameStr)
classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3) # 分类
print ("the classifier came back with: %d, the real answer is: %d" % (classifierResult, classNumStr))
if (classifierResult != classNumStr): errorCount += 1.0
print("\nthe total number of errors is: %d" % errorCount)
print("\nthe total error rate is: %f" % (errorCount/float(mTest)))
print(handwritingClassTest())
运行结果如下图所示:
K-近邻算法识别手写数字数据集,错误率为1.06%,改变变量k的值、修改函数handwritingClassTest()随机选取训练样本、改变训练样本的数目,都会对k-近邻算法的错误率产生影响,感兴趣的话可以改变这些变量值,观察错误率的变化。
K-近邻算法小结
- k-近邻算法是分类数据最简单最有效的算法。
- k-近邻算法是基于实例的学习,使用算法时我们必须有接近实际数据的训练样本数据。
- k-近邻算法必须保存全部数据集,如果训练数据集的很大,必须使用大量的存储空间。此外, 由于必须对数据集中的每个数据计算距离值,实际使用时可能非常耗时。
- k-近邻算法的另一个缺陷是它无法给出任何数据的基础结构信息,因此我们也无法知晓平均实例样本和典型实例样本具有什么特征。