Python 手寫數字識別-knn算法應用


  在上一篇博文中,我們對KNN算法思想及流程有了初步的了解,KNN是采用測量不同特征值之間的距離方法進行分類,也就是說對於每個樣本數據,需要和訓練集中的所有數據進行歐氏距離計算。這里簡述KNN算法的特點:

優點:精度高,對異常值不敏感,無數據輸入假定
缺點:計算復雜度高,空間復雜度高
適用數據范圍:數值型和標稱型(具有有窮多個不同值,值之間無序)

    knn算法代碼:

#-*- coding: utf-8 -*-
from numpy import *
import operator
import time
from os import listdir
def classify(inputPoint,dataSet,labels,k): dataSetSize = dataSet.shape[0] #已知分類的數據集(訓練集)的行數 #先tile函數將輸入點拓展成與訓練集相同維數的矩陣,再計算歐氏距離 diffMat = tile(inputPoint,(dataSetSize,1))-dataSet #樣本與訓練集的差值矩陣 sqDiffMat = diffMat ** 2 #差值矩陣平方 sqDistances = sqDiffMat.sum(axis=1) #計算每一行上元素的和 distances = sqDistances ** 0.5 #開方得到歐拉距離矩陣 sortedDistIndicies = distances.argsort() #按distances中元素進行升序排序后得到的對應下標的列表 #選擇距離最小的k個點 classCount = {} for i in range(k): voteIlabel = labels[ sortedDistIndicies[i] ] classCount[voteIlabel] = classCount.get(voteIlabel,0)+1 #按classCount字典的第2個元素(即類別出現的次數)從大到小排序 sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1), reverse = True) return sortedClassCount[0][0]

  下面介紹如何使用knn算法對手寫識別數據進行分類,這里構造的分類系統只能識別數字0到9,數字經圖形處理軟件處理成具有相同的色彩和大小,寬高為32x32像素,為了便於處理,已將圖像轉換為文本格式,其效果圖如下:

  數據集可在這里下載,解壓后有兩個目錄,其中目錄trainingDigits中包含了1934個例子,命名規則如 9_45.txt,表示該文件的分類是9,是數字9的第45個實例,每個數字大概有200個實例。testDigits目錄中包含946個例子。使用trainingDigits中的數據作為訓練集,使用testDigits中的數據作為測試集測試分類的效果。兩組數據沒有重疊。

  算法應用步驟如下:

  1. 數據准備:數字圖像文本向量化,這里將32x32的二進制圖像文本矩陣轉換成1x1024的向量。循環讀出文件的前32行,存儲在向量中。

#文本向量化 32x32 -> 1x1024
def
img2vector(filename): returnVect = [] fr = open(filename) for i in range(32): lineStr = fr.readline() for j in range(32): returnVect.append(int(lineStr[j])) return returnVect

  2. 構建訓練數據集:利用目錄trainingDigits中的文本數據構建訓練集向量,以及對應的分類向量

#從文件名中解析分類數字
def classnumCut(fileName): 
   fileStr = fileName.split('.')[0]  
    classNumStr = int(fileStr.split('_')[0]) 
    return classNumStr
#構建訓練集數據向量,及對應分類標簽向量
def trainingDataSet():
    hwLabels = []
    trainingFileList = listdir('trainingDigits')           #獲取目錄內容
    m = len(trainingFileList)
    trainingMat = zeros((m,1024))                          #m維向量的訓練集
    for i in range(m):
        fileNameStr = trainingFileList[i]
        hwLabels.append(classnumCut(fileNameStr))
        trainingMat[i,:] = img2vector('trainingDigits/%s' % fileNameStr)
    return hwLabels,trainingMat

  3. 測試集數據測試:通過測試testDigits目錄下的樣本,來計算算法的准確率。

#測試函數
def handwritingTest():
    hwLabels,trainingMat = trainingDataSet()    #構建訓練集
    testFileList = listdir('testDigits')        #獲取測試集
    errorCount = 0.0                            #錯誤數
    mTest = len(testFileList)                   #測試集總樣本數
    t1 = time.time()
    for i in range(mTest):
        fileNameStr = testFileList[i]
        classNumStr = classnumCut(fileNameStr)
        vectorUnderTest = img2vector('testDigits/%s' % fileNameStr)
     #調用knn算法進行測試
        classifierResult = classify(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 tests is: %d" % mTest               #輸出測試總樣本數
    print "the total number of errors is: %d" % errorCount           #輸出測試錯誤樣本數
    print "the total error rate is: %f" % (errorCount/float(mTest))  #輸出錯誤率
    t2 = time.time()
    print "Cost time: %.2fmin, %.4fs."%((t2-t1)//60,(t2-t1)%60)      #測試耗時

if __name__ == "__main__":
    handwritingTest()

  運行結果如下:

  利用knn算法識別手寫數字數據集,錯誤率為1.6%,算法的准確率還算可觀。也可以通過改變變量k的值,觀察錯誤率的變化,關於k值的選擇,一般取一個比較小的數值,例如采用交叉驗證法(簡單來說,就是一部分樣本做訓練集,一部分做測試集)來選擇最優的K值。

  通過運行以上代碼,我們會發現knn算法的執行效率並不高,因為算法需要為每個測試向量計算約2000次歐氏距離,每個距離計算包括1024個維度浮點運算,全部樣本要執行900多次,可見算法實際耗時長,另外,knn算法必須保存全部數據集,每次需為測試向量准備2MB的存儲空間(2個1024x1024矩陣的空間)。所以如何優化算法,減少存儲空間和計算時間的開銷,需要我們進一步深入學習。

參考資料:

  《機器學習實戰》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM