K近鄰算法

knn(k_nearest neighbor)是一種基本的分類與迴歸方法。KNN做分類預測時,一般是選擇多數表決法,即訓練集裏和預測的樣本特徵最近的K個樣本,預測爲裏面有最多類別數的類別。而KNN做迴歸時,一般是選擇平均法,即最近的K個樣本的樣本輸出的平均值作爲迴歸預測值。由於兩者區別不大,雖然本文主要是講解KNN的分類方法,

knn算法的三要素是:k值的選擇,距離度量,分類決策規則

k近鄰算法的流程:

(1)根據給定的距離度量,在訓練數據集中找出與目標樣本x最鄰近的k個點,涵蓋這k個點的鄰域稱爲Nk(x)。

   a .     計算測試數據x與各個訓練數據之間的距離,一般用歐式距離;

                   D(x, y)=\sqrt{\left(x_{1}-y_{1}\right)^{2}+\left(x_{2}-y_{2}\right)^{2}+\ldots+\left(x_{n}-y_{n}\right)^{2}}=\sqrt{\sum_{i=1}^{n}\left(x_{i}-y_{i}\right)^{2}}

               也有選擇Manhattan(曼哈頓距離)作爲距離度量規則的:

                                                 d(x, y)=\sqrt{\sum_{k=1}^{n}\left|x_{k}-y_{k}\right|}

   b.      按照距離的遞增關係進行排序,選取距離最小的K個點;

 

(2)確定前K個點所在類別的出現頻率,返回前K個點中出現頻率最高的類別作爲測試數據的預測分類。

     如下所示,爲多數表決的公式:

                                   \mathrm{y}=\operatorname{argmax} \sum_{x_{i} \in N_{k(x)}} I\left(y_{i}, c_{j}\right), i=1,2, \ldots, N ; j=1,2, \ldots, K

                                 I 爲指示函數(意思是:即當yi = cj時 I等於1,否則I等於0),j爲k的個數大小。

 

關於KD樹搜索:knn的普通方式會比較慢,用KD樹會加快速度

KD樹的三個步驟:第一步是建樹,第二部是搜索最近鄰,最後一步是預測

第一步建樹:

比如我們有二維樣本6個,{(2,3),(5,4),(9,6),(4,7),(8,1),(7,2)},構建kd樹的具體步驟爲:

    1)找到劃分的特徵。6個數據點在x,y維度上的數據方差分別爲6.97,5.37,所以在x軸上方差更大,用第1維特徵建樹。

    2)確定劃分點(7,2)。根據x維上的值將數據排序,6個數據的中值(所謂中值,即中間大小的值)爲7,所以劃分點的數據是(7,2)。這樣,該節點的分割超平面就是通過(7,2)並垂直於:劃分點維度的直線x=7;

    3)確定左子空間和右子空間。 分割超平面x=7將整個空間分爲兩部分:x<=7的部分爲左子空間,包含3個節點={(2,3),(5,4),(4,7)};另一部分爲右子空間,包含2個節點={(9,6),(8,1)}。

    4)用同樣的辦法劃分左子樹的節點{(2,3),(5,4),(4,7)}和右子樹的節點{(9,6),(8,1)}。最終得到KD樹。

 

    最後得到的KD樹如下

                                                                          :

 

第二步是搜索最近鄰:

 當我們生成KD樹以後,就可以去預測測試集裏面的樣本目標點了。對於一個目標點,我們首先在KD樹裏面找到包含目標點的葉子節點。以目標點爲圓心,以目標點到葉子節點樣本實例的距離爲半徑,得到一個超球體,最近鄰的點一定在這個超球體內部。然後返回葉子節點的父節點,檢查另一個子節點包含的超矩形體是否和超球體相交,如果相交就到這個子節點尋找是否有更加近的近鄰,有的話就更新最近鄰。如果不相交那就簡單了,我們直接返回父節點的父節點,在另一個子樹繼續搜索最近鄰。當回溯到根節點時,算法結束,此時保存的最近鄰節點就是最終的最近鄰。

    從上面的描述可以看出,KD樹劃分後可以大大減少無效的最近鄰搜索,很多樣本點由於所在的超矩形體和超球體不相交,根本不需要計算距離。大大節省了計算時間。

    我們用3.1建立的KD樹,來看對點(2,4.5)找最近鄰的過程。

    先進行二叉查找,先從(7,2)查找到(5,4)節點,在進行查找時是由y = 4爲分割超平面的,由於查找點爲y值爲4.5,因此進入右子空間查找到(4,7),形成搜索路徑<(7,2),(5,4),(4,7)>,但 (4,7)與目標查找點的距離爲3.202,而(5,4)與查找點之間的距離爲3.041,所以(5,4)爲查詢點的最近點; 以(2,4.5)爲圓心,以3.041爲半徑作圓,如下圖所示。可見該圓和y = 4超平面交割,所以需要進入(5,4)左子空間進行查找,也就是將(2,3)節點加入搜索路徑中得<(7,2),(2,3)>;於是接着搜索至(2,3)葉子節點,(2,3)距離(2,4.5)比(5,4)要近,所以最近鄰點更新爲(2,3),最近距離更新爲1.5;回溯查找至(5,4),直到最後回溯到根結點(7,2)的時候,以(2,4.5)爲圓心1.5爲半徑作圓,並不和x = 7分割超平面交割,如下圖所示。至此,搜索路徑回溯完,返回最近鄰點(2,3),最近距離1.5。

    對應的圖如下:

                                 

3.3 KD樹預測 

    有了KD樹搜索最近鄰的辦法,KD樹的預測就很簡單了,在KD樹搜索最近鄰的基礎上,我們選擇到了第一個最近鄰樣本,就把它置爲已選。在第二輪中,我們忽略置爲已選的樣本,重新選擇最近鄰,這樣跑k次,就得到了目標的K個最近鄰,然後根據多數表決法,如果是KNN分類,預測爲K個最近鄰里面有最多類別數的類別。如果是KNN迴歸,用K個最近鄰樣本輸出的平均值作爲迴歸預測值。

 

 

Python實現(普通的方法):

#!/usr/bin/python
# coding=utf-8
#########################################
# kNN: k Nearest Neighbors

#  輸入:      newInput:  (1xN)的待分類向量
#             dataSet:   (NxM)的訓練數據集
#             labels:     訓練數據集的類別標籤向量
#             k:         近鄰數

# 輸出:     可能性最大的分類標籤
#########################################

from numpy import *
import operator

# 創建一個數據集,包含2個類別共4個樣本
def createDataSet():
    # 生成一個矩陣,每行表示一個樣本
    group = array([[1.0, 0.9], [1.0, 1.0], [0.1, 0.2], [0.0, 0.1]])
    # 4個樣本分別所屬的類別
    labels = ['A', 'A', 'B', 'B']
    return group, labels

# KNN分類算法函數定義
def kNNClassify(newInput, dataSet, labels, k):
    numSamples = dataSet.shape[0]   # shape[0]表示行數

    # # step 1: 計算距離[
    # 假如:
    # Newinput:[1,0,2]
    # Dataset:
    # [1,0,1]
    # [2,1,3]
    # [1,0,2]
    # 計算過程即爲:
    # 1、求差
    # [1,0,1]       [1,0,2]
    # [2,1,3]   --   [1,0,2]
    # [1,0,2]       [1,0,2]
    # =
    # [0,0,-1]
    # [1,1,1]
    # [0,0,-1]
    # 2、對差值平方
    # [0,0,1]
    # [1,1,1]
    # [0,0,1]
    # 3、將平方後的差值累加
    # [1]
    # [3]
    # [1]
    # 4、將上一步驟的值求開方,即得距離
    # [1]
    # [1.73]
    # [1]
    #
    # ]
    # tile(A, reps): 構造一個矩陣,通過A重複reps次得到
    # the following copy numSamples rows for dataSet
    diff = tile(newInput, (numSamples, 1)) - dataSet  # 按元素求差值
    squaredDiff = diff ** 2  # 將差值平方
    squaredDist = sum(squaredDiff, axis = 1)   # 按行累加
    distance = squaredDist ** 0.5  # 將差值平方和求開方,即得距離

    # # step 2: 對距離排序
    # argsort() 返回排序後的索引值
    sortedDistIndices = argsort(distance)
    classCount = {} # define a dictionary (can be append element)
    for i in xrange(k):
        # # step 3: 選擇k個最近鄰
        voteLabel = labels[sortedDistIndices[i]]

        # # step 4: 計算k個最近鄰中各類別出現的次數
        # when the key voteLabel is not in dictionary classCount, get()
        # will return 0
        classCount[voteLabel] = classCount.get(voteLabel, 0) + 1

    # # step 5: 返回出現次數最多的類別標籤
    maxCount = 0
    for key, value in classCount.items():
        if value > maxCount:
            maxCount = value
            maxIndex = key

    return maxIndex

關於測試:

#!/usr/bin/python
# coding=utf-8
import KNN
from numpy import *
# 生成數據集和類別標籤
dataSet, labels = KNN.createDataSet()
# 定義一個未知類別的數據
testX = array([1.2, 1.0])
k = 3
# 調用分類函數對未知數據分類
outputLabel = KNN.kNNClassify(testX, dataSet, labels, 3)
print "Your input is:", testX, "and classified to class: ", outputLabel

testX = array([0.1, 0.3])
outputLabel = KNN.kNNClassify(testX, dataSet, labels, 3)
print "Your input is:", testX, "and classified to class: ", outputLabel

代碼轉載自:https://www.cnblogs.com/ahu-lichang/p/7151007.html

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章