1. 程式人生 > >機器學習實戰k近鄰演算法(kNN)應用之手寫數字識別程式碼解讀

機器學習實戰k近鄰演算法(kNN)應用之手寫數字識別程式碼解讀

from numpy import *
from os import listdir
import operator
import time

#k-NN簡單實現函式
def classify0(inX,dataSet,labels,k):

    #求出樣本集的行數,也就是labels標籤的數目
    dataSetSize = dataSet.shape[0]

    #構造輸入值和樣本集的差值矩陣
    diffMat = tile(inX,(dataSetSize,1)) - dataSet

    #計算歐式距離
    sqDiffMat = diffMat**2
    sqDistances = sqDiffMat.sum(axis=1)
    distances = sqDistances**0.5

    #求距離從小到大排序的序號
    sortedDistIndicies = distances.argsort()

    #對距離最小的k個點統計對應的樣本標籤
    classCount = {}
    for i in range(k):
        #取第i+1近鄰的樣本對應的類別標籤
        voteIlabel = labels[sortedDistIndicies[i]]
        #以標籤為key,標籤出現的次數為value將統計到的標籤及出現次數寫進字典
        classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1

    #對字典按value從大到小排序
    sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)

    #返回排序後字典中最大value對應的key
    return sortedClassCount[0][0]

#----------------------------------------------------------------------------

#函式執行耗時統計函式
def time_me(fn):
  def _wrapper(*args, **kwargs):
    start = time.clock()
    fn(*args, **kwargs)
    print ("\n%s cost %s second"%(fn.__name__, time.clock() - start))
  return _wrapper

#----------------------------------------------------------------------------

#影象轉換函式(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

#----------------------------------------------------------------------------

#手寫數字識別測試函式
@time_me
def handwritingClassTest():

    #初始化類別標籤為空列表
    hwLabels = []

    #列出給定目錄下所有訓練資料的檔名
    trainingFileList = listdir('F:/machinelearninginaction/Ch02/trainingDigits')

    #求訓練資料數目
    m = len(trainingFileList)

    #初始化m個影象的訓練矩陣
    trainingMat = zeros((m,1024))
    
    #遍歷每一個訓練資料
    for i in range(m):

        #取出一個訓練資料的檔名
        fileNameStr = trainingFileList[i]

        #去掉該訓練資料的字尾名.txt
        fileStr = fileNameStr.split('.')[0]

        #取出代表該訓練資料類別的數字
        classNumStr = int(fileStr.split('_')[0])

        #將代表該訓練資料類別的數字存入類別標籤列表
        hwLabels.append(classNumStr)

        #呼叫影象轉換函式將該訓練資料的輸入特徵轉換為向量並存儲
        trainingMat[i,:] = img2vector('F:/machinelearninginaction/Ch02/trainingDigits/%s' % fileNameStr)

    #列出給定目錄下所有測試資料的檔名
    testFileList = listdir('F:/machinelearninginaction/Ch02/testDigits')

    #初始化測試犯錯的樣本個數
    errorCount = 0.0

    #求測試資料數目
    mTest = len(testFileList)

    #遍歷每一個測試資料
    for i in range(mTest):

        #取出一個測試資料的檔名
        fileNameStr = testFileList[i]

        #去掉該測試資料的字尾名.txt
        fileStr = fileNameStr.split('.')[0]

        #取出代表該測試資料類別的數字
        classNumStr = int(fileStr.split('_')[0])

        #呼叫影象轉換函式將該測試資料的輸入特徵轉換為向量
        vectorUnderTest = img2vector('F:/machinelearninginaction/Ch02/testDigits/%s' % fileNameStr)

        #呼叫k-NN簡單實現函式,並返回分類器對該測試資料的分類結果
        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 error is: %d" % errorCount)

    #輸出分類器錯誤率
    print("\nthe total error rate is: %f" % (errorCount/float(mTest)))
    

三.詳細解讀

仍然是在前面kNN模組的基礎上繼續新增新的函式或方法。 kNN的核心實現程式碼前面已給出: 機器學習實戰k-鄰近演算法(kNN)簡單實施程式碼解讀
為了防止程式碼看上去很冗長,刪除了一些無關的函式,新增了3個函式或方法,分別是函式執行耗時統計函式time_me,影象轉換函式img2vector和手寫數字識別測試函式handwritingClassTest。