需求:
利用一個手寫數字“先驗數據”集,使用knn算法來實現對手寫數字的自動識別;
先驗數據(訓練數據)集:
♦數據維度比較大,樣本數比較多。
♦ 數據集包括數字0-9的手寫體。
♦每個數字大約有200個樣本。
♦每個樣本保持在一個txt文件中。
♦手寫體圖像本身的大小是32x32的二值圖,轉換到txt文件保存后,內容也是32x32個數字,0或者1,如下:

♦目錄trainingDigits存放的是大約2000個訓練數據
♦目錄testDigits存放大約900個測試數據。
trainingDigits文件夾中為訓練數據,里面存儲的都是32*32的txt格式的數字圖像數值矩陣。testDigits文件夾中為測試數據,存儲格式與trainingDigits中相同。文件格式名例如:0_1.txt,0為數字的標簽(即數字本身),1為表示數字0的第一個文件。訓練數據是多張32*32手寫圖像的二維矩陣,所謂二維矩陣就是整個圖像空白的地方使用0描述,寫字的地方使用1描述,
代碼python:https://github.com/kongxiaoshuang/KNN
#-*- coding: utf-8 -*- from numpy import * import operator import matplotlib import matplotlib.pyplot as plt def createDataSet(): group = array([[1.0, 1.1], [1.0, 1.0], [0, 0], [0, 0.1]]) labels = ['A', 'A', 'B', 'B'] return group, labels def classify0(inX, dataSet, labels, k): #inX為用於分類的輸入向量,dataSet為輸入的訓練樣本集, labels為訓練標簽,k表示用於選擇最近的數目 dataSetSize = dataSet.shape[0] #dataSet的行數 diffMat = tile(inX, (dataSetSize, 1)) - dataSet #將inX數組復制成與dataSet相同行數,與dataSet相減,求坐標差 sqDiffMat = diffMat**2 #diffMat的平方 sqDistances = sqDiffMat.sum(axis=1) #將sqDiffMat每一行的所有數相加 distances = sqDistances**0.5 #開根號,求點和點之間的歐式距離 sortedDistIndicies = distances.argsort() #將distances中的元素從小到大排列,提取其對應的index,然后輸出到sortedDistIndicies classCount = {} #創建字典 for i in range(k): voteIlabel = labels[sortedDistIndicies[i]] #前k個標簽數據 classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1 #判斷classCount中有沒有對應的voteIlabel, # 如果有返回voteIlabel對應的值,如果沒有則返回0,在最后加1。為了計算k個標簽的類別數量 sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True) #生成classCount的迭代器,進行排序, # operator.itemgetter(1)以標簽的個數降序排序 return sortedClassCount[0][0] #返回個數最多的標簽 def file2matrix(filename): fr = open(filename) arrayOLines = fr.readlines() #讀入所有行 numberOfLines = len(arrayOLines) #行數 returnMat = zeros((numberOfLines, 3)) #創建數組,數據集 classLabelVector = [] #標簽集 index = 0 for line in arrayOLines: line = line.strip() #移除所有的回車符 listFromLine = line.split('\t') #把一個字符串按\t分割成字符串數組 returnMat[index,:] = listFromLine[0:3] #取listFromLine的前三個元素放入returnMat classLabelVector.append(int(listFromLine[-1])) #選取listFromLine的最后一個元素依次存入classLabelVector列表中 index += 1 return returnMat, classLabelVector def autoNorm(dataSet): minVals = dataSet.min(0) #0表示從列中選取最小值 maxVals = dataSet.max(0) #選取最大值 ranges = maxVals-minVals normDataSet = zeros(shape(dataSet)) #創建一個與dataSet大小相同的零矩陣 m = dataSet.shape[0] #取dataSet得行數 normDataSet = dataSet - tile(minVals, (m, 1)) #將minVals復制m行 與dataSet數據集相減 #歸一化相除 normDataSet = normDataSet/tile(ranges, (m, 1)) #將最大值-最小值的值復制m行 與normDataSet相除,即歸一化 return normDataSet, ranges, minVals #normDataSet為歸一化特征值,ranges為最大值-最小值 def datingClassTest(): hoRatio = 0.10 #測試數據占總數據的百分比 datingDataMat, datingLabels = file2matrix('datingTestSet2.txt') #將文本信息轉成numpy格式 #datingDataMat為數據集,datingLabels為標簽集 normMat, ranges, minVals = autoNorm(datingDataMat) #將datingDataMat數據歸一化 #normMat為歸一化數據特征值,ranges為特征最大值-最小值,minVals為最小值 m = normMat.shape[0] #取normMat的行數 numTestVecs = int(m*hoRatio) #測試數據的行數 errorCount = 0.0 #錯誤數據數量 for i in range(numTestVecs): classifierResult = classify0(normMat[i,:], normMat[numTestVecs:m, :], datingLabels[numTestVecs:m], 3) #classify0為kNN分類器,normMat為用於分類的輸入向量,normMat為輸入的訓練樣本集(剩余的90%) #datingLabels為訓練標簽,3表示用於選擇最近鄰居的數目 print("the classifier came back with: %d, the real answer is: %d" %(classifierResult, datingLabels[i])) if (classifierResult != datingLabels[i]):errorCount += 1.0 #分類器結果和原標簽不一樣,則errorCount加1 print("the total error rate is : %f" %(errorCount/float(numTestVecs))) # datingClassTest() # datingDataMat, datingLabels = file2matrix('datingTestSet2.txt') # # normDataSet, ranges, minVals = autoNorm(datingDataMat) # fig = plt.figure() # ax = fig.add_subplot(111) #一行一列一個 # ax.scatter(datingDataMat[:,1], datingDataMat[:,2], # 15.0*array(datingLabels), 15.0*array(datingLabels)) #scatter畫散點圖,使用標簽屬性繪制不同顏色不同大小的點 # plt.show() # #測試分類器 # group, labels = createDataSet() # label = classify0([1,1], group, labels, 3) # print(label) from os import listdir def img2vector (filename): returnVect = zeros((1, 1024)) #創建一個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 = [] #標簽集 trainingFileList = listdir('E:/digits/trainingDigits') #listdir獲取訓練集的文件目錄 m = len(trainingFileList) #文件數量 trainingMat = zeros((m, 1024)) #一個數字1024個字符,創建m*1024的數組 for i in range(m): fileNameStr = trainingFileList[i] #獲取文件名 fileStr = fileNameStr.split('.')[0] #以'.'將字符串分割,並取第一項,即0_0.txt取0_0 classNumStr = int(fileStr.split('_')[0]) #以'_'將字符串分割,並取第一項 hwLabels.append(classNumStr) #依次存入hwLabels標簽集 trainingMat[i, :] = img2vector('E:/digits/trainingDigits/%s' % fileNameStr) #將每個數字的字符值依次存入trainingMat testFileList = listdir('E:/digits/testDigits') #讀入測試數據集 errorCount = 0.0 #測試錯誤數量 mTest = len(testFileList) #測試集的數量 for i in range(mTest): fileNameStr = testFileList[i] fileStr = fileNameStr.split('.')[0] classNumStr = int(fileStr.split('_')[0]) #測試數據標簽 vectorUnderTest = img2vector('E:/digits/testDigits/%s' % fileNameStr) #讀入測試數據 classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3) #分類器kNN算法,3為最近鄰數目 print("the calssifier came back with: %d, the real answer is : %d" %(classifierResult, classNumStr)) if (classifierResult != classNumStr): errorCount +=1.0 print("\nthe total number of errors is : %f" % errorCount) print("\nthe total error rate is :%f" % (errorCount/float(mTest))) handwritingClassTest()

