看完一節《機器學習實戰》,算是踏入ML的大門了吧!這裏就詳細講一下一個demo:使用kNN算法實現手寫字體的簡單識別git
kNN算法
先簡單介紹一下kNN,就是所謂的K-近鄰算法:app
【做用原理】:存在一個樣本數據集合、每一個樣本數據都存在標籤。輸入沒有標籤的新數據後,將新數據的每一個特徵與樣本集數據的對應特徵進行比較,而後算法提取樣本集中最類似的分類標籤。通常說來,咱們只選擇樣本數據集中前k個最類似的數據,最後,選擇這k個類似數據中出現次數最多的分類,做爲新數據的分類。
機器學習
通俗的說,舉例說明:有一羣明確國籍的人(樣本集合,好比1000個):中國人、韓國人、日本人、美國人、埃及人,如今有一個不知國籍的人,想要經過比較特徵來猜想他的國籍(固然,特徵具備可比較性和有效性),經過比較特徵,得出特徵與該人最相近的樣本集中的9我的(k),其中,1個是韓國人、2個是日本人,6個是中國人,那麼這我的是中國人的可能性就很大。
函數
這就是kNN的基本思想。學習
手寫體識別數據準備測試
kNN輸入須要特徵矩陣,通常是固定大小的二值圖像,這裏咱們使用書上提供的數據集:這個數據集使用32X32文本文件存儲數值圖像。例以下圖的'9'字體
這裏每一個文本文件存儲一個手寫體數據,而且文件名寫成"number_num.txt"這樣的形式,例如9_1.txt,方便後期提取標籤spa
咱們將樣本數據放在trainingDigits文件夾中,測試樣例存儲在testDigits文件夾中code
咱們在處理時將每一個手寫體數據(32x32)轉換成1X1024維的向量。
另外,kNN涉及到類似度計算。這裏咱們使用的是歐氏距離,因爲手寫體數據向量是規則的二值數據,所以不須要進行歸一化。
手寫體識別算法運行流程
(一)讀取手寫體txt文件,轉化爲1X1024向量
咱們建立一個kNN.py,添加模塊img2vector
1 #識別手寫字體模塊-圖像轉向量32x32 to 1x1024 2 def img2vector(filename): 3 returnVect = zeros((1,1024)) 4 fr = open(filename) 5 for i in range(32): 6 lineStr = fr.readline() 7 for j in range(32): 8 returnVect[0,32*i+j] = int(lineStr[j]) 9 return returnVect
咱們的樣本數據和測試數據都須要用到該函數
(二)比較測試數據和樣本數據集的距離,返回k近鄰中最類似的標籤
在kNN.py中添加classify0模塊,附上代碼註釋
1 #--------------------------------------------- 2 #分類模塊 3 #@params 4 # inX:輸入向量、手寫體識別的測試向量 5 # dataSet:訓練集樣本、手寫體識別的訓練集向量 6 # labels:訓練集對應的標籤向量 7 # k:最近鄰居數目、本實驗爲3 8 #--------------------------------------------- 9 def classifiy0(inX, dataSet, labels, k): 10 dataSetSize = dataSet.shape[0] #手寫體樣本集容量 11 #(如下三行)距離計算 12 diffMat = tile(inX, (dataSetSize,1)) - dataSet 13 sqDiffMat = diffMat**2 14 sqDistances = sqDiffMat.sum(axis=1) 15 distances = sqDistances**0.5 #歐氏距離開平方 16 sortedDistIndicies = distances.argsort() #距離排序的索引排序 17 classCount = {} 18 #(如下兩行)選擇距離最小的k個點 19 for i in range(k): 20 voteIlabel = labels[sortedDistIndicies[i]] 21 classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 22 sortedClassCount = sorted(classCount.items(), 23 #排序 24 key = operator.itemgetter(1), reverse = True) 25 return sortedClassCount[0][0]
注意,這裏使用了numpy的接口,在kNN.py的開頭要加上:from numpy import*
(三)比較標籤與測試結果,計算正確率
一樣,在kNN.py中添加handwritingClassTest模塊,綜合以上的兩個模塊,得到識別正確率
1 #手寫識別的測試代碼 2 def handwritingClassTest(): 3 hwLabels = [] 4 trainingFileList = listdir(path='trainingDigits') #獲取目錄內容 5 m = len(trainingFileList) 6 trainingMat = zeros((m,1024)) 7 for i in range(m): 8 #一下三行,從文件名解析分類數字 9 fileNameStr = trainingFileList[i] 10 fileStr = fileNameStr.split('.')[0] 11 classNumStr = int(fileStr.split('_')[0]) 12 13 hwLabels.append(classNumStr) 14 trainingMat[i,:] = img2vector('trainingDigits/%s'%fileNameStr) 15 testFileList = listdir(path='testDigits') 16 17 errorCount = 0.0 #錯誤個數計數器 18 mTest = len(testFileList) 19 20 #從測試數據中提取數據 21 for i in range(mTest): 22 fileNameStr = testFileList[i] 23 fileStr = fileNameStr.split('.')[0] 24 25 classNumStr = int(fileStr.split('_')[0]) 26 vectorUnderTest = img2vector('testDigits/%s'% fileNameStr) 27 classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3) 28 29 print("the classifier came back with:%d,the real answer is:%d"%(classifierResult,classNumStr)) 30 if(classifierResult != classNumStr): 31 errorCount += 1.0 32 #輸出結果 33 print("\nthe total number of errors is:%d"%errorCount) 34 print("\nthe total error rate is: %f"%(errorCount/float(mTest)))
注意,這裏使用到了os模塊listdir,在kNN開頭加入:from numpy import listdir
測試結果以下:
錯誤率爲1.16%,能夠看到,識別效果挺不錯。
後記
經過實驗咱們能夠看到,使用kNN要將訓練樣本一次性加載入內存、若是訓練集的規模很大,勢必對機器有很大的要求。另外,kNN不須要訓練算法、對異常值不敏感、在後期使用的時候要慎重選擇吧