K近鄰(K Nearest Neighbor-KNN)原理講解及實現


算法原理


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“


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM