數(shù)據(jù)下載
- 數(shù)據(jù)樣式(大小32*32)
手寫(xiě)數(shù)字 0
- 下載下面目錄下的digits.zip
手寫(xiě)數(shù)字文件下載地址
kNN算法介紹
- 存在一個(gè)訓(xùn)練樣本,且樣本的每一個(gè)數(shù)據(jù)都有對(duì)應(yīng)的標(biāo)簽
- 輸入沒(méi)有標(biāo)簽的新數(shù)據(jù)后根據(jù)算法提取樣本集中最相似即最近鄰的數(shù)據(jù),得到該數(shù)據(jù)的標(biāo)簽
- 一般來(lái)說(shuō)只選擇樣本數(shù)據(jù)前k個(gè)最相似的數(shù)據(jù)(k通常<=20)
公式
提取最近鄰數(shù)據(jù)的公式:
距離計(jì)算公式
- 根據(jù)此公式構(gòu)建分類器
偽代碼:
- 計(jì)算已知類別數(shù)據(jù)集中的點(diǎn)與當(dāng)前點(diǎn)之間的距離
- 按照距離遞增次序排序
- 選取與當(dāng)前點(diǎn)距離最小的k個(gè)點(diǎn)
- 確定前k個(gè)點(diǎn)所在類別的出現(xiàn)頻率
- 返回前k個(gè)點(diǎn)出現(xiàn)頻率最高的類別作為當(dāng)前點(diǎn)的預(yù)測(cè)分類
- 需要注意的點(diǎn)在代碼中都有標(biāo)注
kNN.py
import numpy as np
# 傳入的label和dataSet是一一對(duì)應(yīng)的關(guān)系
def classify(inX, dataSet, label, k):
dataSetSize = dataSet.shape[0]
# np.tile()是把矩陣在復(fù)制為指定大小
diffMat = np.tile(inX,(dataSetSize,1)) - dataSet
sqDiffMat = diffMat ** 2
# 將矩陣按行相加
sqDistance = sqDiffMat.sum(axis=1)
distance = sqDistance ** 0.5
# np.argsort() 返回排好序的矩陣索引值傀履,也就是數(shù)組下標(biāo)蠢甲,默認(rèn)按行
sortedIndex = distance.argsort()
classCount = {}
for i in range(k):
# sortedIndex排好序的索引用來(lái)取對(duì)應(yīng)的標(biāo)簽
voteLabel = label[sortedIndex[I]]
# 字典的get方法阀参,取字典里的值丽柿,如不存在則默認(rèn)為0
classCount[voteLabel] = classCount.get(voteLabel,0) + 1
# 將標(biāo)簽次數(shù)按大小排序恰聘,返回最接近的標(biāo)簽
sortedClassCount = sorted(classCount.keys(),reverse=True)
return sortedClassCount[0][0]
數(shù)據(jù)預(yù)處理
- 下面定義的所有函數(shù)在HWRecogSystem.py中
-
把數(shù)據(jù)集放到代碼文件夾下:
文件結(jié)構(gòu)大概這樣 - 在文件最底下寫(xiě)上這么幾行(程序執(zhí)行的入口),把文件目錄標(biāo)明
if __name__ == "__main__":
fileDir = 'digits/trainingDigits/'
testFileDir = 'digits/testDigits/'
將圖像轉(zhuǎn)化為向量
- 我們需要把每一個(gè)32*32的圖像轉(zhuǎn)化為一個(gè)1*1024的向量
- 打開(kāi)文件循環(huán)讀出前32行链患,存入向量
代碼
import kNN
import os
import numpy as np
def imageToVector(fileDir,fileName):
fileName = fileDir + fileName
# 由于數(shù)據(jù)存儲(chǔ)為32*32的二進(jìn)制圖像巧鸭,所以需要一個(gè)1行1024列的向量來(lái)存儲(chǔ)
returnVector = np.zeros((1,1024))
f = open(fileName)
for i in range(32):
lineStr = f.readline()
for j in range(32):
initIndex = 32*I+j
returnVector[0,initIndex] = int(lineStr[j])
return returnVector
從訓(xùn)練數(shù)據(jù)集中創(chuàng)建訓(xùn)練集和標(biāo)簽
- fileDir是存放訓(xùn)練數(shù)據(jù)的文件夾, 上面已經(jīng)說(shuō)明
def createDataSet(fileDir):
# 統(tǒng)計(jì)該目錄下的文件數(shù)量
for root,dirs,files in os.walk(fileDir):
fileLen = len(files)
# 初始化dataSet矩陣,足夠放入所有的二進(jìn)制圖像矩陣
dataSet = np.zeros((fileLen,1024))
label = []
for i in range(fileLen):
label.append(files[i][0])
vector = imageToVector(fileDir,files[I])
dataSet = np.insert(dataSet,i,vector,axis=0)
return label, dataSet
測(cè)試函數(shù)及模型
def accuracyTest(fileDir):
count = 0
for root,dirs,files in os.walk(fileDir):
fileLen = len(files)
recog = np.zeros((1,fileLen))
label,dataSet = createDataSet(fileDir)
for i in range(fileLen):
recog[0,i] = kNN.classify(imageToVector(fileDir,files[i]),dataSet,label,20)
print('識(shí)別結(jié)果:' + str(recog[0,i]) + '實(shí)際值:' + str(label[I]))
if recog[0,i] == int(label[I]):
count = count+1
return round(count/fileLen,3)
if __name__ == "__main__":
fileDir = 'digits/trainingDigits/'
testFileDir = 'digits/testDigits/'
accuracy = accuracyTest(testFileDir)
print('識(shí)別率:' + str(accuracy*100) + '%')
實(shí)現(xiàn)效果
- 代碼大概要跑20s
-
要更精確麻捻,可以進(jìn)行更多次迭代或者尋找更多數(shù)據(jù)集
運(yùn)行結(jié)果
參考
- 機(jī)器學(xué)習(xí)實(shí)戰(zhàn) [美]Peter Harrington 著 李銳 李鵬 曲亞?wèn)| 王斌 譯
- 書(shū)籍官網(wǎng)
官網(wǎng)纲仍,數(shù)據(jù)集和源碼也可以從這下載, 但是個(gè)人覺(jué)得他的源碼質(zhì)量一般