【ML-7】聚類算法--KNN


目錄

  1. KNN簡述
  2. KNN算法蠻力實現
  3. KNN算法之KD樹
  4. KNN算法之球樹
  5. KNN算法小結

一、KNN簡述

鄰近算法,或者說K最近鄰(kNN,k-NearestNeighbor)分類算法是數據挖掘分類技術中最簡單的方法之一。所謂K最近鄰,就是k個最近的鄰居的意思,說的是每個樣本都可以用它最接近的k個鄰居來代表。Cover和Hart在1968年提出了最初的鄰近算法。KNN是一種分類(classification)算法,它輸入基於實例的學習(instance-based learning),屬於懶惰學習(lazy learning)即KNN沒有顯式的學習過程,也就是說沒有訓練階段,數據集事先已有了分類和特征值,待收到新樣本后直接進行處理。與急切學習(eager learning)相對應。

在我們平常的生活中也會不自主的應用。"近朱者赤近墨者黑"也就是KNN思想的表現。KNN方法既可以做分類,也可以做回歸,這點和決策樹算法相同。KNN做回歸和分類的主要區別在於最后做預測時候的決策方式不同。

  1. KNN做分類預測時,一般是選擇多數表決法,即訓練集里和預測的樣本特征最近的K個樣本,預測為里面有最多類別數的類別。
  2. 而KNN做回歸時,一般是選擇平均法,即最近的K個樣本的樣本輸出的平均值作為回歸預測值。

由於兩者區別不大,雖然本文主要是講解KNN的分類方法,但思想對KNN的回歸方法也適用。由於scikit-learn里只使用了蠻力實現(brute-force),KD樹實現(KDTree)和球樹(BallTree)實現,本文只討論這幾種算法的實現原理。其余的實現方法比如BBF樹,MVP樹等,在這里不做討論。

例子說明一下:如下圖,綠色圓要被決定賦予哪個類,是紅色三角形還是藍色四方形?如果K=3,由於紅色三角形所占比例為2/3,綠色圓將被賦予紅色三角形那個類,如果K=5,由於藍色四方形比例為3/5,因此綠色圓被賦予藍色四方形類。

由此也說明了KNN算法的結果很大程度取決於K的選擇。

     在KNN中,通過計算對象間距離來作為各個對象之間的非相似性指標,避免了對象之間的匹配問題,在這里距離有多種,可以參考另一篇文章,一般使用歐氏距離或曼哈頓距離:

二、KNN算法蠻力實現

既然我們要找到k個最近的鄰居來做預測,那么我們只需要計算預測樣本和所有訓練集中的樣本的距離,然后計算出最小的k個距離即可,接着多數表決,很容易做出預測。這個方法的確簡單直接,在樣本量少,樣本特征少的時候有效。但是在實際運用中很多時候用不上,為什么呢?因為我們經常碰到樣本的特征數有上千以上,樣本量有幾十萬以上,如果我們這要去預測少量的測試集樣本,算法的時間效率很成問題。因此,這個方法我們一般稱之為蠻力實現。比較適合於少量樣本的簡單模型的時候用。

其算法的描述為:

1)計算測試數據與各個訓練數據之間的距離;

2)按照距離的遞增關系進行排序;

3)選取距離最小的K個點;

4)確定前K個點所在類別的出現頻率;

5)返回前K個點中出現頻率最高的類別作為測試數據的預測分類。

既然蠻力實現在特征多,樣本多的時候很有局限性,那么我們有沒有其他的好辦法呢?有!這里我們講解兩種辦法,一個是KD樹實現,一個是球樹實現。

三、KNN算法之KD

KD樹算法沒有一開始就嘗試對測試樣本分類,而是先對訓練集建模,建立的模型就是KD樹,建好了模型再對測試集做預測。所謂的KD樹就是K個特征維度的樹,注意這里的K和KNN中的K的意思不同。KNN中的K代表最近的K個樣本,KD樹中的K代表樣本特征的維數。為了防止混淆,后面我們稱特征維數為n。

   KD樹算法包括三步,第一步是建樹,第二部是搜索最近鄰,最后一步是預測。

3.1 KD樹的建立

  KD樹建樹采用的是從m個樣本的n維特征中,分別計算n個特征的取值的方差,用方差最大的第k維特征nk來作為根節點。對於這個特征,我們選擇特征nk的取值的中位數nkv對應的樣本作為划分點,對於所有第k維特征的取值小於nkv的樣本,我們划入左子樹,對於第k維特征的取值大於等於nkv的樣本,我們划入右子樹,對於左子樹和右子樹,我們采用和剛才同樣的辦法來找方差最大的特征來做更節點,遞歸的生成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樹。

3.2 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個最近鄰樣本輸出的平均值作為回歸預測值。

四、KNN算法之球樹

KD樹算法雖然提高了KNN搜索的效率,但是在某些時候效率並不高,比如當處理不均勻分布的數據集時,不管是近似方形,還是矩形,甚至正方形,都不是最好的使用形狀,因為他們都有角。一個例子如下圖:

如果黑色的實例點離目標點星點再遠一點,那么虛線圓會如紅線所示那樣擴大,導致與左上方矩形的右下角相交,既然相 交了,那么就要檢查這個左上方矩形,而實際上,最近的點離星點的距離很近,檢查左上方矩形區域已是多余。於此我們看見,KD樹把二維平面划分成一個一個矩形,但矩形區域的角卻是個難以處理的問題。為了優化超矩形體導致的搜索效率的問題,牛人們引入了球樹,這種結構可以優化上面的這種問題。我們現在來看看球樹建樹和搜索最近鄰的算法。

4.1 球樹的建立

球樹,顧名思義,就是每個分割塊都是超球體,而不是KD樹里面的超矩形體。

我們看看具體的建樹流程:

1) 先構建一個超球體,這個超球體是可以包含所有樣本的最小球體。

2) 從球中選擇一個離球的中心最遠的點,然后選擇第二個點離第一個點最遠,將球中所有的點分配到離這兩個聚類中心最近的一個上,然后計算每個聚類的中心,以及聚類能夠包含它所有數據點所需的最小半徑。這樣我們得到了兩個子超球體,和KD樹里面的左右子樹對應。

3)對於這兩個子超球體,遞歸執行步驟2). 最終得到了一個球樹。

可以看出KD樹和球樹類似,主要區別在於球樹得到的是節點樣本組成的最小超球體,而KD得到的是節點樣本組成的超矩形體,這個超球體要與對應的KD樹的超矩形體小,這樣在做最近鄰搜索的時候,可以避免一些無謂的搜索。

4.2 球樹搜索最近鄰

  使用球樹找出給定目標點的最近鄰方法是首先自上而下貫穿整棵樹找出包含目標點所在的葉子,並在這個球里找出與目標點最鄰近的點,這將確定出目標點距離它的最近鄰點的一個上限值,然后跟KD樹查找一樣,檢查兄弟結點,如果目標點到兄弟結點中心的距離超過兄弟結點的半徑與當前的上限值之和,那么兄弟結點里不可能存在一個更近的點;否則的話,必須進一步檢查位於兄弟結點以下的子樹。

檢查完兄弟節點后,我們向父節點回溯,繼續搜索最小鄰近值。當回溯到根節點時,此時的最小鄰近值就是最終的搜索結果。

從上面的描述可以看出,KD樹在搜索路徑優化時使用的是兩點之間的距離來判斷,而球樹使用的是兩邊之和大於第三邊來判斷,相對來說球樹的判斷更加復雜,但是卻避免了更多的搜索,這是一個權衡。

五、KNN算法小結

KNN的主要優點:

1) 理論成熟,思想簡單,既可以用來做分類也可以用來做回歸

2) 可用於非線性分類

3) 訓練時間復雜度比支持向量機之類的算法低,僅為O(n)

4) 和朴素貝葉斯之類的算法比,對數據沒有假設,准確度高,對異常點不敏感

5) 由於KNN方法主要靠周圍有限的鄰近的樣本,而不是靠判別類域的方法來確定所屬類別的,因此對於類域的交叉或重疊較多的待分樣本集來說,KNN方法較其他方法更為適合

6)該算法比較適用於樣本容量比較大的類域的自動分類,而那些樣本容量較小的類域采用這種算法比較容易產生誤分

KNN的主要缺點:

1)計算量大,尤其是特征數非常多的時候

2)樣本不平衡的時候,對稀有類別的預測准確率低

3)KD樹,球樹之類的模型建立需要大量的內存

4)使用懶散學習方法,基本上不學習,導致預測時速度比起邏輯回歸之類的算法慢

5)相比決策樹模型,KNN模型可解釋性不強

大部分來自 <https://www.cnblogs.com/pinard/p/6061661.html#!comments>

附件一:測試代碼

    1 	# coding:utf-8
    2 	
    3 	from numpy import *
    4 	import operator
    5 	##給出訓練數據以及對應的類別
    6 	def createDataSet():
    7 	    group = array([[1.0, 2.0], [1.2, 0.1], [0.1, 1.4], [0.3, 3.5]])
    8 	    labels = ['A', 'A', 'B', 'B']
    9 	    return group, labels
   10 	###通過KNN進行分類
   11 	def classify(input, dataSet, label, k):
   12 	    dataSize = dataSet.shape[0]
   13 	    ####計算歐式距離
   14 	    diff = tile(input, (dataSize, 1)) - dataSet
   15 	    sqdiff = diff ** 2
   16 	    squareDist = sum(sqdiff, axis=1)  ###行向量分別相加,從而得到新的一個行向量
   17 	    dist = squareDist ** 0.5
   18 	    ##對距離進行排序
   19 	    sortedDistIndex = argsort(dist)  #argsort()根據元素的值從大到小對元素進行排序,返回下標
   20 	    classCount = {}
   21 	    for i in range(k):
   22 	        voteLabel = label[sortedDistIndex[i]]
   23 	     ###對選取的K個樣本所屬的類別個數進行統計
   24 	        classCount[voteLabel] = classCount.get(voteLabel, 0) + 1
   25 	###選取出現的類別次數最多的類別
   26 	        maxCount = 0
   27 	    for key, value in classCount.items():
   28 	        if value > maxCount:
   29 	            maxCount = value
   30 	            classes = key
   31 	    return classes
   32 	
   33 	dataSet,labels = createDataSet()
   34 	input = array([1.1,0.3])
   35 	K = 3
   36 	output = classify(input,dataSet,labels,K)
   37 	print("測試數據為:",input,"分類結果為:",output)
   38 
   39 
   40  

結果:

測試數據為: [1.1 0.3] 分類結果為: A


免責聲明!

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



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