kNN
,即k-Nearest Neighbor(k近鄰算法), 簡介可參考KNN的一些總結. 本文是《機器學習實戰》一書第二章的例子, 主要利用kNN實現簡單的手寫文字識別.git
書中使用Python實現, 本文是使用R語言. 數據集中的圖片分辨率爲32*32, 而且該數據已經預處理成文本文件, 即相似點陣字體, 使用1
表明有文字的像素, 0
表示空白.算法
算法的步驟主要有:緩存
計算測試數據到全部訓練數據的距離機器學習
對1中計算的距離排序, 選出最小
的k
個訓練數據函數
在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]]) }
本次實踐準備的數據在兩個文件目錄中,優化
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) }
主要的任務是從數據文件中提取全部的用例, 而後調用上面所述的classify0
和img2vector
函數實現識別工做, 並計算錯誤率以供參考.
圖像文本文件的命名格式爲"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))) }
kNN算法的分類思路是很簡單的, 實現起來也很方便. 在對數據集測試的時候, 錯誤率在1.27%, 這個結果仍是比較不錯的.
不足之處是這種即時訓練消耗了過多的時間和空間, 時間主要消耗在讀取文件創建數據集和計算距離的時候. 在實際過程當中, 前者能夠緩存數據, 達到一次讀取屢次使用; 後者便很難優化了, 這其中涉及到了高階矩陣的運算, 開銷較大. 所以該算法在大規模數據時不宜採用.