目錄
什么是k近鄰算法
模型的三個基本要素
構造kd樹
kd樹的最近鄰搜索
kd樹的k近鄰搜索
Python代碼(sklearn庫)
什么是K近鄰算法(k-Nearest Neighbor,kNN) |
引例
假設有數據集,其中前6部是訓練集(有屬性值和標記),我們根據訓練集訓練一個KNN模型,預測最后一部影片的電影類型。
首先,將訓練集中的所有樣例畫入坐標系,也將待測樣例畫入
然后計算待測分類的電影與所有已知分類的電影的歐式距離
接着,將這些電影按照距離升序排序,取前k個電影,假設k=3,那么我們得到的電影依次是《He's Not Really Into Dudes》、《Beautiful Woman》和《California Man》。而這三部電影全是愛情片,因此我們判定未知電影是愛情片。
百度百科定義
鄰近算法,或者說K最近鄰(kNN,k-NearestNeighbor)分類算法是數據挖掘分類技術中最簡單的方法之一。所謂K最近鄰,就是k個最近的鄰居的意思,說的是每個樣本都可以用它最接近的k個鄰居來代表。
kNN算法的核心思想是如果一個樣本在特征空間中的k個最相鄰的樣本中的大多數屬於某一個類別,則該樣本也屬於這個類別,並具有這個類別上樣本的特性。該方法在確定分類決策上只依據最鄰近的一個或者幾個樣本的類別來決定待分樣本所屬的類別。 kNN方法在類別決策時,只與極少量的相鄰樣本有關。由於kNN方法主要靠周圍有限的鄰近的樣本,而不是靠判別類域的方法來確定所屬類別的,因此對於類域的交叉或重疊較多的待分樣本集來說,kNN方法較其他方法更為適合。
模型的三個基本要素 |
kNN模型的三個基本要素:(1)距離度量、(2)k值的選擇、(3)分類決策規則。接下來,我們一一介紹這三要素。
(1)距離度量
在引例中所畫的坐標系,可以叫做特征空間。特征空間中兩個實例點的距離是兩個實例點相似程度的反應(距離越近,相似度越高)。kNN模型使用的距離一般是歐氏距離,但也可以是其他距離如:曼哈頓距離
(2)k值的選擇
k值的選擇會對kNN模型的結果產生重大影響。選擇較大的k值,相當於用較大鄰域中的訓練實例進行預測,模型會考慮過多的鄰近點實例點,甚至會考慮到大量已經對預測結果沒有影響的實例點,會讓預測出錯;選擇較小的k值,相當於用較小鄰域中的訓練實例進行預測,會使模型變得敏感(如果鄰近的實例點恰巧是噪聲,預測就會出錯)。
在應用中,k值一般取一個比較小的數值。通常采用交叉驗證法來選取最優的k值。
(3)分類決策規則
kNN中的分類決策規則往往是多數表決,即由輸入實例的k個鄰近的訓練實例中的多數類決定待測實例的類。
構造kd樹 |
k近鄰法最簡單的實現方式是線性掃描,需要計算待測實例與每個實例的距離,在大數據上不可行。為了提高k近鄰搜索效率,考慮使用特殊的結構存儲訓練數據,以減少計算距離的次數,可以使用kd樹(kd tree)方法。kd樹分為兩個過程——構造kd樹(使用特殊結構存儲訓練集)、搜索kd樹(減少搜索計算量)。
下面用一個例子演示構造kd樹的過程:
給定一個二維空間的數據集(含有標記的一般叫訓練集,不含標記的一般叫數據集):,請畫出:特征空間的划分過程、kd樹的構造過程。
第一步:
選擇x(1)軸,6個數據點的x(1)坐標上的數字分別是2,5,9,4,8,7。取中位數7(這里不是嚴格意義的中位數,默認取較大的數),以x(1)=7將特征空間分為兩個矩形:
特征空間: kd樹:
第二步:
選擇x(2)軸,處理左子樹,3個數據點的x(2)坐標上的數字分別是3,4,7。取中位數4,以x(2)=4將左子樹對應的特征空間分為兩個矩形;處理右子樹,2個數據點的x(2)坐標上的數字分別是6,1。取中位數6(這里不是嚴格意義的中位數,默認取較大的數),以x(2)=6將右子樹對應的特征空間分為兩個矩形:
特征空間: kd樹:
第三步:
選擇x(循環數%特征數+1)軸,即x(3%2+1),即x(1)軸,分別處理所有待處理的節點:
特征空間: kd樹:
kd樹的最近鄰搜索 |
假設要對待測樣本點(2,4.5)進行類別預測,那么就要在kd樹里搜索它的k個鄰居,然后投票決定其類別。
先介紹:kd樹的最近鄰搜索(k=1)。即,已構造的kd樹、目標點x(2,4.5);輸出:x的最近鄰。
第一步:
在kd樹中找出包含目標點x的葉結點:從根結點出發,遞歸地向下訪問kd樹。若目標點x當前維的坐標小於切分點的坐標,則移動到左子結點,否則移動到右子結點。直到子結點為葉結點為止。由於2<7,進入左子樹;4.5>4,進入右子樹,因此當前到達的葉節點是(4,7)。
第二步:
先將以此葉結點(4,7)定為“當前最近點”。
第三步:
回退到(5,4),如果該結點(5,4)保存的實例點比當前最近點距離目標點更近,則以該實例點為“當前最近點”。計算發現確實(5,4)距(2,4.5)更近,因此將(5,4)定為“當前最近點”,將節點(5,4)到待測點的距離設為L(5,4)。
第四步:
檢查(5,4)的另一子節點(2,3)對應的區域是否與以待測點為球心、以待測點與“當前最近點”間的距離為半徑的超球體(紅色圓圈)相交(這里是相交的)。
如果不相交:向上回退到(7,2),轉入第三步。
如果相交:可能在(2,3)結點對應的區域內存在距目標點更近的點,移動到(2,3)結點。接着,遞歸地進行最近鄰搜索(執行結果:節點(2,3)是到待測點的距離是最近的,將這個距離設為L(2,3)),接着比較L(2,3) < L(5,4),因此,將(2,3)定為“當前最近點”,向上回退到(7,2),轉入第三步。
第五步(其實是第三步,這里是為了將搜索過程演示完畢):
回退到(7,2),如果該結點(7,2)保存的實例點比當前最近點距離目標點更近,則以該實例點為“當前最近點”。計算發現(7,2)距(2,4.5)更遠,因此,無需更新“當前最近點”。
第六步(其實是第四步,這里是為了將搜索過程演示完畢):
檢查(7,2)的另一子節點(9,6)對應的區域是否與以待測點為球心、以待測點與“當前最近點”間的距離為半徑的超球體(藍色圓圈)相交(這里是不相交的)。
如果不相交:向上回退,發現已經是根節點了,無法回退,程序停止,最終得到的最近鄰節點是(2,3)。
二娃:看你啰嗦了半天,kd樹的搜索,究竟是如何減少搜索計算量的呢?
其實,主要減少計算量的步驟在於:判斷超球體與某點對應區域是否相交。如果不相交,那就直接舍棄對那個節點及其子節點的搜索。
kd樹的k近鄰搜索 |
我們知道最近鄰搜索(k=1)中,我們首先存儲一個我們自認為是“當前最近點”的節點,然后在搜索過程中,找到更近的就替換掉。
其實這種思想在k近鄰搜索(k>1)中同樣適用,即,我們首先存儲k個我們自認為是“當前最近點”的節點集合,然后在搜索過程中,找到比集合中任何一個更近的,就取代集合中距離待測點位置最遠的節點。
Python代碼(sklearn庫) |
# -*- coding: utf-8 -*- import numpy as np from sklearn.datasets import load_iris from sklearn import neighbors iris = load_iris() trainX = iris.data trainY = iris.target clf=neighbors.KNeighborsClassifier(n_neighbors=6, weights='uniform', algorithm='auto', leaf_size=30, p=2, metric='minkowski', metric_params=None, n_jobs=1) ''' @param n_neighbors: 指定kNN的k值 @param weights: 'uniform': 本節點的所有鄰居節點的投票權重都相等 'distance': 本節點的所有鄰居節點的投票權重與距離成反比 @param algorithm: 懲罰項系數的倒數,越大,正則化項越小 'ball_tree': BallTree算法 'kd_tree': kd樹算法 'brute': 暴力搜索算法 'auto': 自動決定適合的算法 @param leaf_size: 指定ball_tree或kd_tree的葉節點規模。他影響樹的構建和查詢速度 @param p: p=1:曼哈頓距離; p=2:歐式距離 @param metric: 指定距離度量,默認為'minkowski'距離 @param n_jobs: 任務並行時指定使用的CPU數,-1表示使用所有可用的CPU @method fit(X,y): 訓練模型 @method predict(X): 預測 @method score(X,y): 計算在(X,y)上的預測的准確率 @method predict_proba(X): 返回X預測為各類別的概率 @method kneighbors(X, n_neighbors, return_distance): 返回樣本點的k近鄰點。如果return_distance=True,則也會返回這些點的距離 @method kneighbors_graph(X, n_neighbors, mode): 返回樣本點的連接圖 ''' clf.fit(trainX,trainY) print "訓練准確率:" + str(clf.score(trainX,trainY)) print "測試准確率:" + str(clf.score(trainX,trainY)) ''' 訓練准確率:0.973333333333 測試准確率:0.973333333333 '''