注:基於現有小案例攥寫
K 近鄰算法採用測量不同特徵值之間的距離方法進行分類:
優點:精度高、對異常值不敏感、無數據輸入假定。
缺點:計算複雜度高、空間複雜度高。
K 近鄰算法適用數據範圍爲:數值型和標稱型
標稱型:標稱型目標變量的結果只在有限目標集中取值,如真與假
數值型:數值型目標變量則可以從無限的數值集合中取值,如0.100,42.001等
工作原理:存在一個樣本數據集合,也稱作訓練樣本集,並且樣本集中每個數據都存在標籤,即我們知道樣本集中每一數據與所屬分類的對應關係。輸入沒有標籤的新數據後,將新數據的每個特徵與樣本集中數據對應的特徵進行比較,然後算法提取樣本集中特徵最相似數據(最近鄰)的分類標籤。一般來說,我們只選擇樣本數據集中前 kk 個最相似的數據,這就是 K 近鄰算法中 kk 的出處,通常 kk 是不大於 20 的整數。最後,選擇 kk個最相似數據中出現次數最多的分類,作爲新數據的分類。
k鄰近算法的一般流程:
- 收集數據:可以使用任何方法。
- 準備數據:距離計算所需要的數值,最好是結構化的數據格式。
- 分析數據:可以使用任何方法。
- 訓練算法:此步驟不適用於 K 近鄰算法。
- 測試算法:計算錯誤率。
- 使用算法:首先需要輸入樣本數據和結構化的輸出結果,然後運行K 近鄰算法判定輸入數據分別屬於哪個分類,最後應用對計算出的分類執行後續的處理。
準備數據集:
http://labfile.oss.aliyuncs.com/courses/777/digits.zip
trainingDigits
:訓練數據,1934 個文件,每個數字大約 200 個文件。
testDigits
:測試數據,946 個文件,每個數字大約 100 個文件。
全部代碼如下:
import numpy as np
import operator
from os import listdir
def handwritingclassTest():
hwLabels =[]
trainingFileList = listdir('digits/trainingDigits')
m = len(trainingFileList)
trainingMat = np.zeros((m,1024))
for i in range(m):
fileNameStr = trainingFileList[i]
fileStr = fileNameStr.split('.')[0]
classNumStr = int(fileStr.split('_')[0])
hwLabels.append(classNumStr)
trainingMat[i,:]=im2vector('digits/trainingDigits/%s' % fileNameStr)
testFileList = listdir('digits/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 = im2vector('digits/testDigits/%s' % fileNameStr)
classifierResult = classify0(vectorUnderTest,trainingMat,hwLabels,3)
print("測試樣本 %d,分類器預測: %d,真實類別:%d" % (i+1,classifierResult,classNumStr))
if(classifierResult != classNumStr):
errorCount += 1.0
print("\n錯誤分類計數:%d" % errorCount)
print("\n錯誤分類 比例:%f" % (errorCount/float(mTest)))
def createDataSet():
group = np.array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
labels = ['A','A','B','B']
return group,labels
def im2vector(filename):
#創建向量
returnVect = np.zeros((1,1024))
fr = open(filename)
for i in range(32):
lineStr = fr.readline()
for j in range(32):
returnVect[0,32*i+j]=int(lineStr[j])
return returnVect
#im2vector('digits/testDigits/0_1.txt')
def classify0(inX,dataSet,labels,k):
#獲取dataSet維度值
dataSetSize = dataSet.shape[0]
#矩陣運算,計算測試數據與每個 樣本數據對應數據項的差值
diffMat = np.tile(inX,(dataSetSize,1)) - dataSet
#進行平方運算
sqDiffMat = diffMat**2
#平方運算後以行 求和
sqDistances = sqDiffMat.sum(axis=1)
#取平方根,得到距離向量
distances = sqDistances**0.5
#按照距離進行排序,取出索引值
sortedDistIndicies = distances.argsort()
print(sortedDistIndicies)
classCount={}
#依次取出最近的樣本數據
for i in range(k):
#記錄改樣本所屬的類別
voteIlabel = labels[sortedDistIndicies[i]]
classCount[voteIlabel]=classCount.get(voteIlabel,0)+1
#對類別出現的頻次進行排序,從高到低,確定前 k 個點所在類別的出現頻率
sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
#返回前 k 個點出現頻率最高的類別作爲當前點的預測分類。
return sortedClassCount[0][0]
#group,labels = createDataSet()
#print(classify0([0,0],group,labels,3))
handwritingclassTest()
最後運行情況:
解釋:
函數 img2vector
,將圖像轉換爲向量:該函數創建 1x1024
的 NumPy 數組,然後打開給定的文件,循環讀出文件的前 32
行,並將每行的頭 32
個字符值存儲在 NumPy 數組中,把一個 32x32
的二進制圖像矩陣轉換爲 1x1024
的向量,最後返回數組。
函數classify0,通過歐式距離公式,計算臨近點,選取與當前點距離最小的 k 個點,確定前 k 個點所在類別的出現頻率,返回前 k 個點出現頻率最高的類別作爲當前點的預測分類。
函數handwritingClassTest(),打開 兩個數據集,帶入上面兩個函數進行計算,進行分類,並與真實分類結果進行對比,得到最後 的錯誤分類比例。
上面的代碼中的錯誤分類比例是0.0105,可以通過修改k值,改變訓練樣本進行測試,進一步調進錯誤分類 比例。