使用k-近邻分类器的手写识别系统
背景:利用k-近邻算法构造可以识别数字0到9的系统,需要处理的数字已经使用图形处理软件,处理成32*32像素的黑白图像,为了方便理解,数据以文本文件存储(虽然这不是一个好办法),数据格式如下:数据在这下载
00000000000001100000000000000000
00000000000011111100000000000000
00000000000111111111000000000000
00000000011111111111000000000000
00000001111111111111100000000000
00000000111111100011110000000000
00000001111110000001110000000000
00000001111110000001110000000000
00000011111100000001110000000000
00000011111100000001111000000000
00000011111100000000011100000000
00000011111100000000011100000000
00000011111000000000001110000000
00000011111000000000001110000000
00000001111100000000000111000000
00000001111100000000000111000000
00000001111100000000000111000000
00000011111000000000000111000000
00000011111000000000000111000000
00000000111100000000000011100000
00000000111100000000000111100000
00000000111100000000000111100000
00000000111100000000001111100000
00000000011110000000000111110000
00000000011111000000001111100000
00000000011111000000011111100000
00000000011111000000111111000000
00000000011111100011111111000000
00000000000111111111111110000000
00000000000111111111111100000000
00000000000011111111110000000000
00000000000000111110000000000000
以上为数字‘0’的一个训练样本。
步骤:
-
收集数据:提供文本文件。
-
准备数据:编写函数classify0(),将数据转换为分类器使用的list格式。
-
分析数据:在python命令中检查数据,确保它符合要求。
-
训练算法:此步骤不适用于k-近邻算法。
-
测试算法:编写函数,以部分样本作为测试样本,计算精度。
-
使用算法:构建完整的应用程序。
准备数据:将图像转换为测试向量
目录trainingDigits中包含了大约2000个例子,每个数字大约有200个样本,目录testDigits中包含大约900个测试数据。我们使用目录trainingDigits中的数据训练分类器,使用目录testDigits中的数据测试分类器的效果。
首先我们在上节的kNN模块中新增函数img2vector(),将图像转换为向量:该函数创建1*1024的numpy数组,然后打开文件,循环读出文件的前32行,将每行的前32个字符存储在numpy数组中,将32*32的矩阵转换为1*1024的向量:
代码如下:
def img2vector(filename): returnVect = 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
对函数进行测试:
如上获取了0_13.txt的前两行数据。
测试算法:使用k-近邻算法识别手写数字
kNN模块中建立handwritingClassTest()函数:
def handwritingClassTest(): hwLabels = [] trainingFileList = listdir('trainingDigits') #load the training set m = len(trainingFileList) trainingMat = zeros((m,1024)) for i in range(m): fileNameStr = trainingFileList[i] fileStr = fileNameStr.split('.')[0] #take off .txt classNumStr = int(fileStr.split('_')[0]) hwLabels.append(classNumStr) trainingMat[i,:] = img2vector('trainingDigits/%s' % fileNameStr) testFileList = listdir('testDigits') #iterate through the test set errorCount = 0.0 mTest = len(testFileList) for i in range(mTest): fileNameStr = testFileList[i] fileStr = fileNameStr.split('.')[0] #take off .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))
kNN中要先加入 from os import listdir 来调用函数listdir()列出给定目录的文件名,这段代码将训练样本的文件名的第一个数字作为样本的类标签,设训练样本的个数为m,首先构建了一个m*1024的训练矩阵,每一行存储一个样本数据向量。利用classify0()函数对测试样本的每一个数据进行分类,并计算错误率。
上边的测试结果错误率为1.2%
实际使用这个算法时,算法的执行效率并不高。因为算法需要为每个测试向量做2000次距离计算,每个距离计算包括了1024个维度浮点运算,总计要执行900次,此外,我们还需要为测试向量准备2MB的存储空间。k决策树就是k-近邻算法的优化版,可以节省大量的计算开销。
k-近邻算法是基于实例的学习,使用算法时我们必须有接近实际数据的训练样本数据,k-近邻算法必须保存全部数据集,如果训练数据集很大,必须使用大量的存储空间。此外,由于必须对数据集中的每个数据计算距离,实际使用时可能非常耗时。
k-近邻算法的另一个缺陷是它无法给出任何数据的基础结构信息,因此我们也无法知晓平均实例样本和典型实例样本具体有什么特征。
本章完!