K近鄰算法

K近鄰算法

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

  K 最近鄰 (k-Nearest Neighbor,KNN) 分類算法,是一個理論上比較成熟的方法,也是最簡單的機器學習算法之一,1968年由 Cover 和 Hart 提出。該方法的思路是:如果一個樣本在特徵空間中的 k 個最相似即特徵空間中最鄰近)的樣本中的大多數屬於某一個類別,則該樣本也屬於這個類別。KNN 算法中,所選擇的鄰居都是已經正確分類的對象。該方法在分類決策上只依據最鄰近的一個或者幾個樣本的類別來決定待分樣本所屬的類別。 K 最近鄰 (k-Nearest Neighbor,KNN) 分類算法,是一個理論上比較成熟的方法,也是最簡單的機器學習算法之一。該方法的思路是:如果一個樣本在特徵空間中的 k 個最相似即特徵空間中最鄰近)的樣本中的大多數屬於某一個類別,則該樣本也屬於這個類別。KNN 算法中,所選擇的鄰居都是已經正確分類的對象。該方法在分類決策上只依據最鄰近的一個或者幾個樣本的類別來決定待分樣本所屬的類別。

  KNN 算法本身簡單有效,它是一種 lazy-learning 算法,分類器不需要使用訓練集進行訓練,訓練時間複雜度爲0。KNN 分類的計算複雜度和訓練集中的文檔數目成正比,也就是說,如果訓練集中文檔總數爲 n,那麼 KNN 的分類時間複雜度爲O(n)。

  KNN 方法雖然從原理上也依賴於極限定理,但在類別決策時,只與極少量的相鄰樣本有關。由於 KNN 方法主要靠周圍有限的鄰近的樣本,而不是靠判別類域的方法來確定所屬類別的,因此對於類域的交叉或重疊較多的待分樣本集來說,KNN 方法較其他方法更爲適合。

  K 近鄰算法使用的模型實際上對應於對特徵空間的劃分。K 值的選擇,距離度量和分類決策規則是該算法的三個基本要素:

  1. K 值的選擇會對算法的結果產生重大影響。K值較小意味着只有與輸入實例較近的訓練實例纔會對預測結果起作用,但容易發生過擬合;如果 K 值較大,優點是可以減少學習的估計誤差,但缺點是學習的近似誤差增大,這時與輸入實例較遠的訓練實例也會對預測起作用,是預測發生錯誤。在實際應用中,K 值一般選擇一個較小的數值,通常採用交叉驗證的方法來選擇最有的 K 值。隨着訓練實例數目趨向於無窮和 K=1 時,誤差率不會超過貝葉斯誤差率的2倍,如果K也趨向於無窮,則誤差率趨向於貝葉斯誤差率。
  2. 該算法中的分類決策規則往往是多數表決,即由輸入實例的 K 個最臨近的訓練實例中的多數類決定輸入實例的類別
  3. 距離度量一般採用 Lp 距離,當p=2時,即爲歐氏距離,在度量之前,應該將每個屬性的值規範化,這樣有助於防止具有較大初始值域的屬性比具有較小初始值域的屬性的權重過大。

  該算法在分類時有個主要的不足是,當樣本不平衡時,如一個類的樣本容量很大,而其他類樣本容量很小時,有可能導致當輸入一個新樣本時,該樣本的 K 個鄰居中大容量類的樣本佔多數。 該算法在分類時有個主要的不足是,當樣本不平衡時,如一個類的樣本容量很大,而其他類樣本容量很小時,有可能導致當輸入一個新樣本時,該樣本的 K 個鄰居中大容量類的樣本佔多數。 

  該算法只計算“最近的”鄰居樣本,某一類的樣本數量很大,那麼或者這類樣本並不接近目標樣本,或者這類樣本很靠近目標樣本。無論怎樣,數量並不能影響運行結果。可以採用權值的方法(和該樣本距離小的鄰居權值大)來改進。該方法的另一個不足之處是計算量較大,因爲對每一個待分類的文本都要計算它到全體已知樣本的距離,才能求得它的 K 個最近鄰點。目前常用的解決方法是事先對已知樣本點進行剪輯,事先去除對分類作用不大的樣本。該算法比較適用於樣本容量比較大的類域的自動分類,而那些樣本容量較小的類域採用這種算法比較容易產生誤分。該算法只計算“最近的”鄰居樣本,某一類的樣本數量很大,那麼或者這類樣本並不接近目標樣本,或者這類樣本很靠近目標樣本。無論怎樣,數量並不能影響運行結果。可以採用權值的方法(和該樣本距離小的鄰居權值大)來改進。該方法的另一個不足之處是計算量較大,因爲對每一個待分類的文本都要計算它到全體已知樣本的距離,才能求得它的 K 個最近鄰點。目前常用的解決方法是事先對已知樣本點進行剪輯,事先去除對分類作用不大的樣本。該算法比較適用於樣本容量比較大的類域的自動分類,而那些樣本容量較小的類域採用這種算法比較容易產生誤分。

  KNN 算法不僅可以用於分類,還可以用於迴歸。通過找出一個樣本的 K 個最近鄰居,將這些鄰居的屬性的平均值賦給該樣本,就可以得到該樣本的屬性。更有用的方法是將不同距離的鄰居對該樣本產生的影響給予不同的權值(weight),如權值與距離成正比。

  實現 K 近鄰算法時,主要考慮的問題是如何對訓練數據進行快速  K 近鄰搜索,這在特徵空間維數大及訓練數據容量大時非常必要。

最近鄰搜索:Nearest Neighbor Search





在進行圖像搜索時,要求系統返回與查詢圖片相似的圖像集,這裏頭涉及高維空間的數據查詢問題。排序和尋找是計算機技術的基礎算法,最基本的是對一維數組排序和在一維數組中尋找元素。在圖像檢索系統中面對的是大規模的高維數據,對查詢算法的效率要求更高,這裏介紹是一種基本的查詢算法--最近鄰(Nearest Neighbor )或K近鄰(KNN)。最近鄰搜索(Nearest Neighbor Search):最近鄰查詢是最重要的空間查詢之一,也是文本着重考慮的類型。其定義是:給定一個查詢點q和一個距離度量(有歐幾里德距離、曼哈頓距離等),一個最近鄰查詢找出一個離查詢點q最近的空間數據對象。它的一般化形式爲k(k>=1)最近鄰查詢,定義爲:給定一個查詢點q,一個正整數k以及一個距離度量,則一個k最鄰近查詢找出k個離q最近的空間數據對象。如上圖所示,查詢對象q的k鄰近1NN(q)={a},4NN(q)={a,b,c,d}。

最近鄰規則也是一種分類方法,如上圖中,綠色圓要被決定賦予哪個類,是紅色三角形還是藍色四方形?如果K=3,由於紅色三角形所佔比例爲2/3,綠色圓將被賦予紅色三角形那個類,如果K=5,由於藍色四方形比例爲3/5,因此綠色圓被賦予藍色四方形類。

用原始的窮舉法可實現最近鄰查詢,結果如上圖(K=3),Matlab代碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
functionknn_2d_demo()
% KNN 2D Demo
dim = 2;
num = 40;
 
X = randi(100,num,dim);
Q = randi(100,1,dim);
K = 3;
[indexs distances] = KNNS(X,Q,K);
 
figure
hold on
plot(X(:,1),X(:,2),'b.')
plot(X(indexs,1),X(indexs,2),'ro')
plot(Q(1),Q(2),'g*')
plot(Q(1),Q(2),'co','MarkerSize',ceil(distances(K))*6)
hold off
 
function[indexs distances] = KNNS(X,Q,K)
% X -- 數據點集 N×D
% Q -- 查詢數據 N×D
% K -- 查詢近鄰數
% indexs -- KNN 點集索引
% distances -- 查詢點同KNN點的距離
[n,dim] = size(X);
query_mat = repmat(Q,n,1);
dist_list = sqrt(sum((X - query_mat).^2,2));
[value, index] = sort(dist_list);
indexs = index(1:K);
distances = value(1:K);

當數據點數目增大時,窮舉法的計算量會以指數級別增長。1977年Friedman提出的k-d樹快速搜索算法[Friedman & Bentley s.t. '77]可以快速找到最近鄰,但當特徵點描述符多於10維時其效率就很低,並不比窮舉法效率高。1997年Beis和Lowe提出了一種近似算法稱爲Best-Bin-First(BBF)[Beis & Lowe'99] ,這種近似算法能夠以很高的概率找到最近鄰點。

k-d樹(K-dimension tree)是二叉檢索樹的擴展,k-d tree的每一層將空間分成兩個。樹的頂層結點按一維進行劃分,下一層結點按另一維進行劃分,以此類推,各個維循環往復。劃分要使得在每個結點,存儲在子樹中大約一半的點落入一側,而另一半落入另一側。

參照上圖,用二維(K=2)數據的實例來說明K-D Tree的建立:

1. 根結點選擇:將數據按x維排序,以中值所在數據點(5,8)作爲根結點,然後以此結點爲參考作第一次分裂;

注意,這裏涉及兩種基本操作:

(1)選擇分裂(split)的維度,即在哪一維數據上將原數據分爲兩半,有兩種做法:

一是取  當前層數n mod 數據維數K:如第一層時,n = 0,n mod K = 0,即在第一維數據上尋找分裂點。

二是取 方差最大的那維數據,即在各維數據上分別計算方差,方差最大者數據越分散,故在該維數據上尋找分裂點。

(2)在分裂維度上選擇分裂點:通常是取該數據的中值點。

2. 分裂:(父)根結點p的分裂維split_dim = 0,即在x維上分裂,第split_dim分量小於p[split_dim]的放於左側,否之放在右側。對左右側分別確定分裂維、分裂點,循環,直至最後。

照以上步驟,Matlab中遞歸實現如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
functiontree_output = kdtree_create(X,parent_number,split_dimension)
 
global tree_cell;
global node_number;
 
ifnargin ==1
 
    % add the index values to the last column
    % easy way to keep track of them
    [n,d] = size(X);
    X(:,d+1)=1:n';
    node_number=1;
    split_dimension=0;
    parent_number=0;
 
    % intialize the node
    Node.type='node';
    Node.left=0;
    Node.right=0;
    Node.nodevector=zeros(1,d);
    Node.hyperrect=[zeros(1,d);zeros(1,d)];
    Node.numpoints=0;
    Node.index=0;
    Node.parent=0;
    Node.splitval=0;
 
    % initilaze the tree
    hold_cell_data(1:n)=Node;
    tree_cell=hold_cell_data;
    clearhold_cell_data;
 
else
 
    [n,d] = size(X(:,1:end-1));
    node_number=node_number+1;
    split_dimension=split_dimension+1;
 
end
 
ifn==0
    fprintf('Error: 0 points in node, causing endless loop, press ctrl-C.\n');
end
 
assigned_nn=node_number; % assigned node number forthis particular iteration
 
%setassignments to current node
tree_cell(assigned_nn).type='node';
tree_cell(assigned_nn).parent=parent_number;
 
%ifthere is 1 datapoint left, makea leaf out of it
ifn==1
    tree_cell(assigned_nn).left=[];
    tree_cell(assigned_nn).right=[];
    tree_cell(assigned_nn).nodevector=X(:,1:end-1);
    tree_cell(assigned_nn).hyperrect=[X(:,1:end-1);X(:,1:end-1)];
    tree_cell(assigned_nn).type='leaf';
    tree_cell(assigned_nn).numpoints=1;
    tree_cell(assigned_nn).index=X(:,end);
 
    tree_output = assigned_nn;
    return;
end
 
%ifthere are morethan 1 data points left calculate some of the node
% values
tree_cell(assigned_nn).numpoints = n;
a = min(X(:,1:end-1)); b = max(X(:,1:end-1));
tree_cell(assigned_nn).hyperrect = [a; b];
 
%ifthe feature vectors happen to be the same thenleaf again
ifa==b tree_cell(assigned_nn).type='leaf'; end
 
% recursively build rest of tree
%ifthe node is a leaf thenassign the index and node vector
if(strcmp(tree_cell(assigned_nn).type,'leaf'))
 
    tree_cell(assigned_nn).nodevector = mean(X(:,1:end-1));
    tree_cell(assigned_nn).index=X(:,end);
 
else
 
    %ifit is not a leaf
 
    % figure out whichdimension to split(going inorder)
    tree_cell(assigned_nn).splitdim=mod(split_dimension,d)+1;
 
    % figure out the median value to cutthis dimension
    median_value=median(X(:,tree_cell(assigned_nn).splitdim));
    %findout all the values that are lower than or equal to this median
    i=find(X(:, tree_cell(assigned_nn).splitdim)<=median_value);
 
    %ifthere are morethan 2 points
    if(tree_cell(assigned_nn).numpoints>2)
        % as there morethan two points
        [max_val,max_pos]=max(X(i, tree_cell(assigned_nn).splitdim));
        % recurse foreverything to the left of the median
        tree_cell(assigned_nn).left = kdtree_create(X([i(1:max_pos-1);i(max_pos+1:end)], <img src="http://blog.youtueye.com/wp-includes/images/smilies/icon_smile.gif"alt=":)"class="wp-smiley"> ,  assigned_nn,split_dimension);
        % recurse foreverything to the right of the median
        if(size(X,1)>size(i,1))
            tree_cell(assigned_nn).right = kdtree_create(X(find(~(X(:, tree_cell(assigned_nn).splitdim)<=median_value)), <img src="http://blog.youtueye.com/wp-includes/images/smilies/icon_smile.gif"alt=":)"class="wp-smiley"> , assigned_nn,split_dimension);
        else
            tree_cell(assigned_nn).right =[];
        end
    else
        %ifthere are only two data points left
        % choose the left value as the median
        %makethe right value as a leaf
        % leave the left leaf blank
        [max_val,max_pos]=max(X(i, tree_cell(assigned_nn).splitdim));
        if(i(max_pos)==1); min_pos=2; elsemin_pos=1; end
        tree_cell(assigned_nn).left = [];
        tree_cell(assigned_nn).right = kdtree_create(X(min_pos,:), assigned_nn,split_dimension);
    end
 
    % assign the median vector to this node
    tree_cell(assigned_nn).nodevector = X(i(max_pos),1:end-1);
    tree_cell(assigned_nn).index=X(i(max_pos),end);
    tree_cell(assigned_nn).splitval=X(i(max_pos), tree_cell(assigned_nn).splitdim);
 
end
 
% final clean up
ifnargin ==1
    % As all computation is complete thenreturn tree structure
    % and clearother data
    tree_output=tree_cell;
    clearglobal tree_cell;
else
    % otherwise output the assigned_nn forstorage inthe parent
    tree_output=assigned_nn;
end

其劃分2-D平面的過程如下:

k-d樹的數據結構決定了能夠減少查詢量,因爲很多結點在查詢時不參與比較。下面通過實例說明最近鄰(K=1)的查詢過程:

1. 以q(2,6)做爲查詢點,在KD Tree上自頂向下進行比較。

2. 在結點p處,如果q(split_dim)<=p(split_dim),q進入p的左子樹比較;否則,進入右子樹比較;比較及遊走規則同前,循環,直至最後。

如此,最終得最近鄰點爲(1,4),如下圖:

真實的最近鄰點應該爲(2,7),以上查詢步驟似乎還缺少一步:

3.  回溯搜索路徑:

將步驟(2)得到的近鄰點c加入隊列,並計算距離dist,記錄best_dist = dist;

判斷以結點c爲圓心,best_dist爲半徑的區域是否與其他父結點的邊界相交,無則終止,c即爲最近鄰;

否則,記錄這些相交的父結點,跳轉到相應區域比較,並計算dist,如果dist<best_dist,best_dist = dist,重複以上步驟。

修正後的結果如下:

另一個實例:

維數災難:

傳統的KNN查詢主要方法是基於空間劃分的算法——tree類似算法,如R-treeKd-treeSR-tree。這種算法返回的結果是精確的,但是這種算法在高維數據集上的時間效率並不高。實驗[Weber & Schek s.t '98]指出維度高於10之後,基於空間劃分的算法時間複雜度反而不如線性查找。

擴展: Approximate Nearest Neighbor

k nearest neighbors

r nearest neighbors

(c,r) nearest neighbors

參考資料:

http://www.cs.haifa.ac.il/~rita/ml_course/lectures/KNN.pdf

 KNN Demo

SIFT特徵點匹配與消除錯配:BBF,RANSAC

 k-d tree算法的研究

 KD Tree Demo

Introduction to kd-trees (推薦)

[Lee & Wong '77]  Lee, D. T.; Wong, C. K. (1977). "Worst-case analysis for region and partial region searches in multidimensional binary search trees and balanced quad trees". Acta Informatica 9 (1): 23–29.[PDF]

[Beis & Lowe'99]  Jeffrey S. Beis and David G. Lowe, "Shape indexing using approximate nearest-neighbour search in high-dimensional spaces," Conference on Computer Vision and Pattern Recognition, Puerto Rico (June 1997), pp. 1000-1006. [PDF];

[Weber & Schek s.t '98]  Roger Weber, Hans-Jörg Schek, Stephen Blott: A Quantitative Analysis and Performance Study for Similarity-Search Methods in High-Dimensional Spaces. VLDB 1998: 194-205.[PDF]

[Friedman & Bentley s.t. '77] J. H. Friedman, J. L. Bentley, and R.A. Finkel. Analgorithm for finding best matches in logarithmic ex-pected time. ACM Transactions on Mathematical Soft-ware, 3(3):209{226, September 1977.[PDF]

[PDF] Approximate Best Bin First k-d. Tree All Nearest Neighbor Search with Incremental Updates. Jan Kybic, Ivan Vnucko,2010

資源:

Kdtree implementation in matlab (本文代碼參考)

tutorial-KNN

http://www.cs.umd.edu/~mount/ANN/
http://www.cs.ubc.ca/~mariusm/index.php/FLANN/FLANN

https://kdtree.googlecode.com/svn-history/r18/kdtree/

 

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