1、實戰(zhàn)背景
對于需要識別的數(shù)字已經(jīng)使用圖形處理軟件,處理成具有相同的色彩和大兄焕濉:寬高是32像素x32像素。盡管采用本文格式存儲圖像不能有效地利用內(nèi)存空間舅巷,但是為了方便理解羔味,我們將圖片轉(zhuǎn)換為文本格式
與此同時,這些文本格式存儲的數(shù)字的文件命名也很有特點钠右,格式為:數(shù)字的值_該數(shù)字的樣本序號
通過手寫knn的代碼寫在下面
from numpy import *
from matplotlib.font_manager import FontProperties
import matplotlib.lines as mlines
import matplotlib.pyplot as plt
import operator
import os
''''
函數(shù)說明:knn算法赋元,分類器,inx是輸入的向量飒房,dataset是數(shù)據(jù)集搁凸,labels是標簽
'''''
def classify0(inX, dataSet, labels, k):
#獲取dataSet的行數(shù) shape[0]是行數(shù) shape[1]是列數(shù)
dataSetSize = dataSet.shape[0]
diffMat = tile(inX, (dataSetSize,1))-dataSet
#diffMat的每一個元素都平方
sqDiffMat = diffMat**2
#所有列加起來
sqDistance = sqDiffMat.sum(axis=1)
distances = sqDistance**0.5
#升序排序
sortedDistIndicies = distances.argsort()
classCount={}
for i in range(k):
voteIlabel = labels[sortedDistIndicies[i]]
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
return sortedClassCount[0][0]
'''
函數(shù)說明:將文本中的32*32矩陣轉(zhuǎn)化為1*1024向量存儲
'''
def img2vector(filename):
returnVect =zeros((1,1024))
fr = open(filename)
for i in range(32):
#readline是讀取某一行的數(shù)據(jù)返回一個字符串對象
#readlines 是讀取文件的所有行,每一行作為一個元素狠毯,保存在一個list中
lineStr = fr.readline()
for j in range(32) :
#把第0行第32*i+j個元素賦值
returnVect[0,32*i+j] = lineStr[j]
return returnVect
def handwritingClassTest(k):
hwLabels = []
trainingFileList = os.listdir("trainingDigits")
m = len(trainingFileList)
trainingMat = zeros((m,1024))
for i in range(m):
fileNameStr = trainingFileList[i]
#首先把fileNameStr按照.分割坪仇,然后把列表的第一個元素賦值給fileStr
fileStr = fileNameStr.split(".")[0]
#首先把filestr按照——分割,然后把列表的第一個元素轉(zhuǎn)化為int類型之后賦值給classNumStr垃你,即該文件表示的數(shù)字值
classNumStr = int(fileStr.split("_")[0])
hwLabels.append(classNumStr)
# trainingMat[i,:]指的是這個矩陣第i行的所有元素
trainingMat[i,:] = img2vector("trainingDigits/%s" % fileNameStr)
testFileList = os.listdir("testDigits")
errorCount = 0.0
mTest = len(testFileList)
for i in range(mTest):
fileNameStr = testFileList[i]
fileStr = fileNameStr.split(".")[0]
classNumStr = int(fileStr.split("_")[0])
vectorUnderTest = img2vector("testDigits/%s" % fileNameStr)
classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, k)
print("預(yù)測的結(jié)果是%d,正確的結(jié)果是%d" %(classifierResult, classNumStr))
if(classifierResult != classNumStr):
errorCount += 1.0
print("\n錯誤率是%f" % (errorCount/float(mTest)))
上述代碼還是用到了我們自己寫的classfi0函數(shù)椅文,不過喂很,knn算法已經(jīng)實現(xiàn)好了,我們直接用就可以了皆刺,首先要導(dǎo)入sklearn庫少辣,下面是sklearn的實現(xiàn)函數(shù)的說明。
KNneighborsClassifier參數(shù)說明:
n_neighbors:默認為5羡蛾,就是k-NN的k的值漓帅,選取最近的k個點。
weights:默認是uniform痴怨,參數(shù)可以是uniform忙干、distance,也可以是用戶自己定義的函數(shù)浪藻。uniform是均等的權(quán)重捐迫,就說所有的鄰近點的權(quán)重都是相等的。distance是不均等的權(quán)重爱葵,距離近的點比距離遠的點的影響大施戴。用戶自定義的函數(shù),接收距離的數(shù)組萌丈,返回一組維數(shù)相同的權(quán)重赞哗。
algorithm:快速k近鄰搜索算法,默認參數(shù)為auto辆雾,可以理解為算法自己決定合適的搜索算法肪笋。除此之外,用戶也可以自己指定搜索算法ball_tree度迂、kd_tree藤乙、brute方法進行搜索,brute是蠻力搜索英岭,也就是線性掃描,當訓(xùn)練集很大時湿右,計算非常耗時诅妹。kd_tree,構(gòu)造kd樹存儲數(shù)據(jù)以便對其進行快速檢索的樹形數(shù)據(jù)結(jié)構(gòu)毅人,kd樹也就是數(shù)據(jù)結(jié)構(gòu)中的二叉樹吭狡。以中值切分構(gòu)造的樹,每個結(jié)點是一個超矩形丈莺,在維數(shù)小于20時效率高划煮。ball tree是為了克服kd樹高緯失效而發(fā)明的,其構(gòu)造過程是以質(zhì)心C和半徑r分割樣本空間缔俄,每個節(jié)點是一個超球體弛秋。
leaf_size:默認是30器躏,這個是構(gòu)造的kd樹和ball樹的大小。這個值的設(shè)置會影響樹構(gòu)建的速度和搜索速度蟹略,同樣也影響著存儲樹所需的內(nèi)存大小登失。需要根據(jù)問題的性質(zhì)選擇最優(yōu)的大小。
metric:用于距離度量挖炬,默認度量是minkowski揽浙,也就是p=2的歐氏距離(歐幾里德度量)。
p:距離度量公式意敛。在上小結(jié)馅巷,我們使用歐氏距離公式進行距離度量。除此之外草姻,還有其他的度量方法钓猬,例如曼哈頓距離。這個參數(shù)默認為2碴倾,也就是默認使用歐式距離公式進行距離度量逗噩。也可以設(shè)置為1,使用曼哈頓距離公式進行距離度量跌榔。
metric_params:距離公式的其他關(guān)鍵參數(shù)异雁,這個可以不管,使用默認的None即可僧须。
n_jobs:并行處理設(shè)置纲刀。默認為1,臨近點搜索并行工作數(shù)担平。如果為-1示绊,那么CPU的所有cores都用于并行工作。
代碼
from sklearn.neighbors import KNeighborsClassifier as kNN
from numpy import *
import os
def img2vector(filename):
returnVect =zeros((1,1024))
fr = open(filename)
for i in range(32):
#readline是讀取某一行的數(shù)據(jù)返回一個字符串對象
#readlines 是讀取文件的所有行暂论,每一行作為一個元素面褐,保存在一個list中
lineStr = fr.readline()
for j in range(32) :
#把第0行第32*i+j個元素賦值
returnVect[0,32*i+j] = lineStr[j]
return returnVect
def handwritingClassTest():
# 測試集的Labels
hwLabels = []
# 返回trainingDigits目錄下的文件名
trainingFileList = os.listdir('trainingDigits')
# 返回文件夾下文件的個數(shù)
m = len(trainingFileList)
# 初始化訓(xùn)練的Mat矩陣,測試集
trainingMat = zeros((m, 1024))
# 從文件名中解析出訓(xùn)練集的類別
for i in range(m):
# 獲得文件的名字
fileNameStr = trainingFileList[i]
# 獲得分類的數(shù)字
classNumber = int(fileNameStr.split('_')[0])
# 將獲得的類別添加到hwLabels中
hwLabels.append(classNumber)
# 將每一個文件的1x1024數(shù)據(jù)存儲到trainingMat矩陣中
trainingMat[i, :] = img2vector('trainingDigits/%s' % (fileNameStr))
# 構(gòu)建kNN分類器
neigh = kNN(n_neighbors=3, algorithm='auto')
# 擬合模型, trainingMat為訓(xùn)練矩陣,hwLabels為對應(yīng)的標簽
neigh.fit(trainingMat, hwLabels)
# 返回testDigits目錄下的文件列表
testFileList = os.listdir('testDigits')
# 錯誤檢測計數(shù)
errorCount = 0.0
# 測試數(shù)據(jù)的數(shù)量
mTest = len(testFileList)
# 從文件中解析出測試集的類別并進行分類測試
for i in range(mTest):
# 獲得文件的名字
fileNameStr = testFileList[i]
# 獲得分類的數(shù)字
classNumber = int(fileNameStr.split('_')[0])
# 獲得測試集的1x1024向量,用于訓(xùn)練
vectorUnderTest = img2vector('testDigits/%s' % (fileNameStr))
# 獲得預(yù)測結(jié)果
# classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3)
classifierResult = neigh.predict(vectorUnderTest)
print("分類返回結(jié)果為%d\t真實結(jié)果為%d" % (classifierResult, classNumber))
if (classifierResult != classNumber):
errorCount += 1.0
print("總共錯了%d個數(shù)據(jù)\n錯誤率為%f%%" % (errorCount, errorCount / mTest * 100))
if __name__ == '__main__':
handwritingClassTest()