看完一节《机器学习实战》,算是踏入ML的大门了吧!这里就详细讲一下一个demo:使用kNN算法实现手写字体的简单识别git
kNN算法
先简单介绍一下kNN,就是所谓的K-近邻算法:app
【做用原理】:存在一个样本数据集合、每一个样本数据都存在标签。输入没有标签的新数据后,将新数据的每一个特征与样本集数据的对应特征进行比较,而后算法提取样本集中最类似的分类标签。通常说来,咱们只选择样本数据集中前k个最类似的数据,最后,选择这k个类似数据中出现次数最多的分类,做为新数据的分类。
机器学习
通俗的说,举例说明:有一群明确国籍的人(样本集合,好比1000个):中国人、韩国人、日本人、美国人、埃及人,如今有一个不知国籍的人,想要经过比较特征来猜想他的国籍(固然,特征具备可比较性和有效性),经过比较特征,得出特征与该人最相近的样本集中的9我的(k),其中,1个是韩国人、2个是日本人,6个是中国人,那么这我的是中国人的可能性就很大。
函数
这就是kNN的基本思想。学习
手写体识别数据准备测试
kNN输入须要特征矩阵,通常是固定大小的二值图像,这里咱们使用书上提供的数据集:这个数据集使用32X32文本文件存储数值图像。例以下图的'9'字体
这里每一个文本文件存储一个手写体数据,而且文件名写成"number_num.txt"这样的形式,例如9_1.txt,方便后期提取标签spa
咱们将样本数据放在trainingDigits文件夹中,测试样例存储在testDigits文件夹中code
咱们在处理时将每一个手写体数据(32x32)转换成1X1024维的向量。
另外,kNN涉及到类似度计算。这里咱们使用的是欧氏距离,因为手写体数据向量是规则的二值数据,所以不须要进行归一化。
手写体识别算法运行流程
(一)读取手写体txt文件,转化为1X1024向量
咱们建立一个kNN.py,添加模块img2vector
1 #识别手写字体模块-图像转向量32x32 to 1x1024 2 def img2vector(filename): 3 returnVect = zeros((1,1024)) 4 fr = open(filename) 5 for i in range(32): 6 lineStr = fr.readline() 7 for j in range(32): 8 returnVect[0,32*i+j] = int(lineStr[j]) 9 return returnVect
咱们的样本数据和测试数据都须要用到该函数
(二)比较测试数据和样本数据集的距离,返回k近邻中最类似的标签
在kNN.py中添加classify0模块,附上代码注释
1 #--------------------------------------------- 2 #分类模块 3 #@params 4 # inX:输入向量、手写体识别的测试向量 5 # dataSet:训练集样本、手写体识别的训练集向量 6 # labels:训练集对应的标签向量 7 # k:最近邻居数目、本实验为3 8 #--------------------------------------------- 9 def classifiy0(inX, dataSet, labels, k): 10 dataSetSize = dataSet.shape[0] #手写体样本集容量 11 #(如下三行)距离计算 12 diffMat = tile(inX, (dataSetSize,1)) - dataSet 13 sqDiffMat = diffMat**2 14 sqDistances = sqDiffMat.sum(axis=1) 15 distances = sqDistances**0.5 #欧氏距离开平方 16 sortedDistIndicies = distances.argsort() #距离排序的索引排序 17 classCount = {} 18 #(如下两行)选择距离最小的k个点 19 for i in range(k): 20 voteIlabel = labels[sortedDistIndicies[i]] 21 classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 22 sortedClassCount = sorted(classCount.items(), 23 #排序 24 key = operator.itemgetter(1), reverse = True) 25 return sortedClassCount[0][0]
注意,这里使用了numpy的接口,在kNN.py的开头要加上:from numpy import*
(三)比较标签与测试结果,计算正确率
一样,在kNN.py中添加handwritingClassTest模块,综合以上的两个模块,得到识别正确率
1 #手写识别的测试代码 2 def handwritingClassTest(): 3 hwLabels = [] 4 trainingFileList = listdir(path='trainingDigits') #获取目录内容 5 m = len(trainingFileList) 6 trainingMat = zeros((m,1024)) 7 for i in range(m): 8 #一下三行,从文件名解析分类数字 9 fileNameStr = trainingFileList[i] 10 fileStr = fileNameStr.split('.')[0] 11 classNumStr = int(fileStr.split('_')[0]) 12 13 hwLabels.append(classNumStr) 14 trainingMat[i,:] = img2vector('trainingDigits/%s'%fileNameStr) 15 testFileList = listdir(path='testDigits') 16 17 errorCount = 0.0 #错误个数计数器 18 mTest = len(testFileList) 19 20 #从测试数据中提取数据 21 for i in range(mTest): 22 fileNameStr = testFileList[i] 23 fileStr = fileNameStr.split('.')[0] 24 25 classNumStr = int(fileStr.split('_')[0]) 26 vectorUnderTest = img2vector('testDigits/%s'% fileNameStr) 27 classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3) 28 29 print("the classifier came back with:%d,the real answer is:%d"%(classifierResult,classNumStr)) 30 if(classifierResult != classNumStr): 31 errorCount += 1.0 32 #输出结果 33 print("\nthe total number of errors is:%d"%errorCount) 34 print("\nthe total error rate is: %f"%(errorCount/float(mTest)))
注意,这里使用到了os模块listdir,在kNN开头加入:from numpy import listdir
测试结果以下:
错误率为1.16%,能够看到,识别效果挺不错。
后记
经过实验咱们能够看到,使用kNN要将训练样本一次性加载入内存、若是训练集的规模很大,势必对机器有很大的要求。另外,kNN不须要训练算法、对异常值不敏感、在后期使用的时候要慎重选择吧