需求
利用一个手写数字“先验数据”集,使用knn算法来实现对手写数字的自动识别;
先验数据(训练数据)集:
- 数据维度比较大,样本数比较多。
- 数据集包括数字0-9的手写体。
- 每个数字大约有200个样本。
- 每个样本保持在一个txt文件中。
- 手写体图像本身的大小是32x32的二值图,转换到txt文件保存后,内容也是32x32个数字,0或者1,如下:
数据集压缩包解压后有两个目录:
- 目录trainingDigits存放的是大约2000个训练数据
- 目录testDigits存放大约900个测试数据。
模型分析
本案例看起来跟前一个案例几乎风马牛不相及,但是一样可以用KNN算法来实现。没错,这就是机器学习的魅力,不过,也是机器学习的难点:模型抽象能力!
思考:
1. 手写体因为每个人,甚至每次写的字都不会完全精确一致,所以,识别手写体的关键是“相似度” 2. 既然是要求样本之间的相似度,那么,首先需要将样本进行抽象,将每个样本变成一系列特征数据(即特征向量) 3. 手写体在直观上就是一个个的图片,而图片是由上述图示中的像素点来描述的,样本的相似度其实就是像素的位置和颜色之间的组合的相似度 4. 因此,将图片的像素按照固定顺序读取到一个个的向量中,即可很好地表示手写体样本 5. 抽象出了样本向量,及相似度计算模型,即可应用KNN来实现
python实现
新建一个kNN.py脚本文件,文件里面包含四个函数:
- 一个用来生成将每个样本的txt文件转换为对应的一个向量,
- 一个用来加载整个数据集,
- 一个实现kNN分类算法。
- 最后就是实现加载、测试的函数。
######################################### # kNN: k Nearest Neighbors # 参数: inX: vector to compare to existing dataset (1xN) # dataSet: size m data set of known vectors (NxM) # labels: data set labels (1xM vector) # k: number of neighbors to use for comparison # 输出: 多数类 ######################################### from numpy import * import operator import os # KNN分类核心方法 def kNNClassify(newInput, dataSet, labels, k): numSamples = dataSet.shape[0] # shape[0]代表行数 ## step 1: 计算欧式距离 # tile(A, reps): 将A重复reps次来构造一个矩阵 # the following copy numSamples rows for dataSet diff = tile(newInput, (numSamples, 1)) - dataSet # Subtract element-wise squaredDiff = diff ** 2 # squared for the subtract squaredDist = sum(squaredDiff, axis = 1) # sum is performed by row distance = squaredDist ** 0.5 ## step 2: 对距离排序 # argsort()返回排序后的索引 sortedDistIndices = argsort(distance) classCount = {} # 定义一个空的字典 for i in xrange(k): ## step 3: 选择k个最小距离 voteLabel = labels[sortedDistIndices[i]] ## step 4: 计算类别的出现次数 # when the key voteLabel is not in dictionary classCount, get() # will return 0 classCount[voteLabel] = classCount.get(voteLabel, 0) + 1 ## step 5: 返回出现次数最多的类别作为分类结果 maxCount = 0 for key, value in classCount.items(): if value > maxCount: maxCount = value maxIndex = key return maxIndex # 将图片转换为向量 def img2vector(filename): rows = 32 cols = 32 imgVector = zeros((1, rows * cols)) fileIn = open(filename) for row in xrange(rows): lineStr = fileIn.readline() for col in xrange(cols): imgVector[0, row * 32 + col] = int(lineStr[col]) return imgVector # 加载数据集 def loadDataSet(): ## step 1: 读取训练数据集 print "---Getting training set..." dataSetDir = 'E:/Python/ml/knn/' trainingFileList = os.listdir(dataSetDir + 'trainingDigits') # 加载测试数据 numSamples = len(trainingFileList) train_x = zeros((numSamples, 1024)) train_y = [] for i in xrange(numSamples): filename = trainingFileList[i] # get train_x train_x[i, :] = img2vector(dataSetDir + 'trainingDigits/%s' % filename) # get label from file name such as "1_18.txt" label = int(filename.split('_')[0]) # return 1 train_y.append(label) ## step 2:读取测试数据集 print "---Getting testing set..." testingFileList = os.listdir(dataSetDir + 'testDigits') # load the testing set numSamples = len(testingFileList) test_x = zeros((numSamples, 1024)) test_y = [] for i in xrange(numSamples): filename = testingFileList[i] # get train_x test_x[i, :] = img2vector(dataSetDir + 'testDigits/%s' % filename) # get label from file name such as "1_18.txt" label = int(filename.split('_')[0]) # return 1 test_y.append(label) return train_x, train_y, test_x, test_y # 手写识别主流程 def testHandWritingClass(): ## step 1: 加载数据 print "step 1: load data..." train_x, train_y, test_x, test_y = loadDataSet() ## step 2: 模型训练. print "step 2: training..." pass ## step 3: 测试 print "step 3: testing..." numTestSamples = test_x.shape[0] matchCount = 0 for i in xrange(numTestSamples): predict = kNNClassify(test_x[i], train_x, train_y, 3) if predict == test_y[i]: matchCount += 1 accuracy = float(matchCount) / numTestSamples ## step 4: 输出结果 print "step 4: show the result..." print 'The classify accuracy is: %.2f%%' % (accuracy * 100)
测试非常简单,只需要在命令行中输入:
import kNN kNN.testHandWritingClass()
输出结果如下:
step 1: load data... ---Getting training set... ---Getting testing set... step 2: training... step 3: testing... step 4: show the result... The classify accuracy is: 98.84%