算法原理
K最近鄰(k-Nearest Neighbor)算法是比較簡單的機器學習算法。它采用測量不同特征值之間的距離方法進行分類。它的思想很簡單:如果一個樣本在特征空間中的k個最近鄰(最相似)的樣本中的大多數都屬於某一個類別,則該樣本也屬於這個類別。第一個字母k可以小寫,表示外部定義的近鄰數量。
舉例說明
首先我們准備一個數據集,這個數據集很簡單,是由二維空間上的4個點構成的一個矩陣,如表1所示:
表1:訓練集
其中前兩個點構成一個類別A,后兩個點構成一個類別B。我們用Python把這4個點在坐標系中繪制出來,如圖1所示:
圖1:訓練集繪制
繪制所用的代碼如下:
# -*- encoding:utf-8-* - from numpy import * import matplotlib.pyplot as plt def createDataSet(): dataSet = array([[1.0, 1.1], [1.0, 1.0], [0, 0.2], [0, 0.1]]) # 數據集 labels = ['A', 'A', 'B', 'B'] # 數據集對應的類別標簽 return dataSet, labels dataSet, labels = createDataSet() # 顯示數據集信息 fig = plt.figure() ax = fig.add_subplot(111) indx = 0 for point in dataSet: if labels[indx] == 'A': ax.scatter(point[0], point[1], c='blue', marker='o', linewidths=0, s=300) plt.annotate("("+str(point[0])+","+str(point[1])+")", xy=(point[0], point[1])) else: ax.scatter(point[0], point[1], c='red', marker='^', linewidths=0, s=300) plt.annotate("("+str(point[0])+","+str(point[1])+")", xy=(point[0], point[1])) indx += 1 plt.show()
從圖形中可以清晰地看到由4個點構成的訓練集。該訓練集被分為兩個類別:A類——藍色圓圈,B類——紅色三角形。因為紅色區域內的點距比它們到藍色區域內的點距要小的多,這種分類也很自然。
下面我們給出測試集,只有一個點,我們把它加入到剛才的矩陣中去,如表2所示:
表2:加入了一個訓練樣本
我們想知道給出的這個測試集應該屬於哪個分類,最簡單的方法還是畫圖,我們把新加入的點加入圖中,圖2很清晰,從距離上看,它更接近紅色三角形的范圍,應該歸於B類,這就是KNN算法的基本原理。
圖2:加入一個訓練樣本后繪制
在上述代碼的基礎上,繪制測試樣本點的代碼如下:
# 顯示即將需要測試的數據信息 testdata = [0.2, 0.2] ax.scatter(testdata[0], testdata[1], c='green', marker='^', linewidths=0, s=300) plt.annotate("(" + str(testdata[0]) + "," + str(testdata[1]) + ")", xy=(testdata[0], testdata[1])) plt.show()
算法實現
綜上所述,KNN算法應由以下步驟構成。
第一階段:確定k值(就是指最近鄰居的個數)。一般是一個奇數,因為測試樣本有限,故取值為3。
第二階段:確定距離度量公式。文本分類一般使用夾角余弦,得出待分類數據點和所有已知類別的樣本點,從中選擇距離最近的k個樣本。
夾角余弦:
第三階段:統計這k個樣本點中各個類別的數量。上例中我們選定k值為3,則B類樣本(三角形)有2個,A類樣本(圓形)有 1個,那么我們就把這個方形數據點定位B類;即,根據k個樣本中數量最多的樣本是什么類別,我們就把這個數據點定為什么類別。
實現代碼如下:
from numpy import * import operator # 產生數據集 def createDataSet(): dataSet = array([[1.0, 1.1], [1.0, 1.0], [0, 0.2], [0, 0.1]]) # 數據集 labels = ['A', 'A', 'B', 'B'] # 數據集對應的類別標簽 return dataSet, labels # 夾角余弦距離公式 def cosdist(vector1, vector2): return dot(vector1, vector2) / (linalg.norm(vector1) * linalg.norm(vector2)) # KNN 分類器 # 測試集:testdata; 訓練集:trainSet;類別標簽:listClasses;k: k個鄰居數 def classify(testdata, trainSet, listClasses, k): dataSetSize = trainSet.shape[0] # 返回樣本集的行數 distances = array(zeros(dataSetSize)) for indx in xrange(dataSetSize): # 計算測試集與訓練集之間的距離:夾角余弦 distances[indx] = cosdist(testdata, trainSet[indx]) # 根據生成的夾角余弦按從小到大排序,結果為索引號 sortedDistIndicies = argsort(distances) classCount = {} for i in range(k): # 按排序順序返回樣本集對應的類別標簽 voteIlabel = listClasses[sortedDistIndicies[i]] # 為字典classCount賦值,相同的key,value加1 classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1 # sorted():按字典值進行排序,返回list sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True) return sortedClassCount[0][0] # dataSet:測試數據集 # labels:測試數據集對應的標簽 dataSet, labels = createDataSet() testdata = [0.2, 0.2] k = 3 # 選取最近的K個樣本進行類別判定 # 判定testdata類別,輸出類別結果 print "label is: " + classify(testdata, dataSet, labels, k)
輸出:“label is: B“