使用kNN算法實現簡單的手寫文字識別

0. 介紹

kNN,即k-Nearest Neighbor(k近鄰算法), 簡介可參考KNN的一些總結. 本文是《機器學習實戰》一書第二章的例子, 主要利用kNN實現簡單的手寫文字識別.git

書中使用Python實現, 本文是使用R語言. 數據集中的圖片分辨率爲32*32, 而且該數據已經預處理成文本文件, 即相似點陣字體, 使用1表明有文字的像素, 0表示空白.算法

1. kNN算法實現

算法的步驟主要有:緩存

  1. 計算測試數據到全部訓練數據的距離機器學習

  2. 對1中計算的距離排序, 選出最小k個訓練數據函數

  3. 在2中選出的k個數據中選取出現概率最大的標籤, 此即算法對測試數據的分類學習

排序的時候, 利用的是order方法, 取出降序排序元素的索引, 這在numpy中對應的方法是argsort.測試

實現代碼以下:字體

classify0 <- function(inX, dataSet, labels, k){
    dataSetSize = length(dataSet[,1])
    #擴展測試向量inX
    oneMat = matrix(1, dataSetSize, 1)
    dataMat = oneMat %*% inX
    #計算距離
    dataMat = dataMat - dataSet
    sqDiffMat = dataMat ** 2
    sqDistances = rowSums(sqDiffMat)
    distances = sqDistances ** 0.5
    #選擇距離最小的k個點
    #按第一列升序排列獲取序號
    sortedDistIndicies = order(distances)
    voteLabelsCount = rep(0, length(labels))
    for(i in 1:k){
        #獲取第k小距離數據的標籤
        label = labels[sortedDistIndicies[i]]
        index = which(labels == label)
        voteLabelsCount[index[1]] = voteLabelsCount[index[1]] + 1
    }
    sortedVoteLabelsCount = order(-voteLabelsCount)
    return(labels[sortedVoteLabelsCount[1]])
}

2. 準備數據

本次實踐準備的數據在兩個文件目錄中,優化

  • trainingDigits -- 包含2000個例子, 每一個數字大概200個..net

  • testDigits -- 包含大約900個例子.

trainingDigits中的數據將用於訓練分類器, testDigits中的數據將用於測試分類器的效果.

因爲原始數據是32*32的矩陣, 如今須要將其轉化爲1*1024的向量. 程序以下:

img2vector <- function(filename){
    returnVect = matrix(0,1,1024)
    con = file(filename, "r")
    for(i in 0:31){
        line = readLines(con,n=1)
        for(j in 1:32){
            returnVect[1,(32*i+j)] = as.numeric(substr(line,j,j))
        }
    }
    close(con)
    return(returnVect)
}

3. 測試算法

主要的任務是從數據文件中提取全部的用例, 而後調用上面所述的classify0img2vector函數實現識別工做, 並計算錯誤率以供參考.

圖像文本文件的命名格式爲"a_b.txt", a表示當前文件的數字, b表示這是該數字的第b個例子. R對於文本的處理是比較弱的, 不過對於這點內容仍是能應付, 使用了一點正則替換搞定.

處理完數據調用核心的classify0函數便可. 具體代碼以下:

hardwritingTest <- function(){
    print("the test start.")
    print("read trainingDigits.")
    trainingFileList = Sys.glob("trainingDigits/*.txt")
    m = length(trainingFileList)
    hwLabels = rep(0, m)
    trainingMat = matrix(0,m,1024)
    for(i in 1:m){
        fileNameStr = trainingFileList[i]
        #提取數字
        fileStr = sub("trainingDigits/", "", fileNameStr)
        fileStr = sub("_[0-9]+.txt", "", fileStr)
        classNumStr = as.numeric(fileStr)
        hwLabels[i] = classNumStr
        trainingMat[i,] = img2vector(trainingFileList[i])
    }
    print("read testDigits.")
    testFileList = Sys.glob("testDigits/*.txt")
    errorCount = 0.0
    mTest = length(testFileList)
    for(i in 1:mTest){
        fileNameStr = testFileList[i]
        fileStr = sub("testDigits/", "", fileNameStr)
        fileStr = sub("_[0-9]+.txt", "", fileStr)
        classNumStr = as.numeric(fileStr)
        vectorUnderTest = img2vector(testFileList[i])
        print(paste0("classify the ", i, "th testDigit."))
        classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3)
        print(paste0("--the classifier came back with: ", classifierResult, ", the real answer is: ", classNumStr))
        if(classifierResult != classNumStr){
            errorCount = errorCount + 1.0
        }
    }
    print(paste0("the total number of errors is: ", errorCount))
    print(paste0("the total error rate is: ", (errorCount / mTest)))
}

4. 小結

kNN算法的分類思路是很簡單的, 實現起來也很方便. 在對數據集測試的時候, 錯誤率在1.27%, 這個結果仍是比較不錯的.

不足之處是這種即時訓練消耗了過多的時間和空間, 時間主要消耗在讀取文件創建數據集和計算距離的時候. 在實際過程當中, 前者能夠緩存數據, 達到一次讀取屢次使用; 後者便很難優化了, 這其中涉及到了高階矩陣的運算, 開銷較大. 所以該算法在大規模數據時不宜採用.

相關文章
相關標籤/搜索