KNN學習(K-Nearest Neighbor algorithm,K最鄰近方法 )是一種統計分類器,對數據的特征變量的篩選尤其有效。
基本原理
KNN的基本思想是:輸入沒有標簽(標注數據的類別),即沒有經過分類的新數據,首先提取新數據的特征並與測試集中的每一個數據特征進行比較;然后從測試集中提取K個最鄰近(最類似)的數據特征標簽,統計這K個最鄰近數據中出現次數最多的分類,將其作為新的數據類別。
KNN的這樣的基本思想有點類似於生活中的“物以類聚。人以群分”。
在KNN學習中,首先計算待分類數據特征與訓練數據特征之間的距離並排序。取出距離近期的K個訓練數據特征。然后根據這K個相近訓練數據特征所屬類別來判定新樣本類別:假設它們都屬於一類,那么新的樣本也屬於這個類;否則,對每一個候選類別進行評分,依照某種規則確定新的樣本的類別。
筆者借用以下這個圖來做更形象的解釋:
如上圖,圖中最小的那個圓圈代表新的待分類數據。三角形和矩形分別代表已知的類型,如今須要推斷圓圈屬於菱形那一類還是矩形那一類。
可是我該以什么樣的根據來推斷呢?
- 看離圓形近期(K=1)的那個類型是什么,由圖可知,離圓形近期的是三角形,故將新數據判定為屬於三角形這個類別。
- 看離圓形近期的3個數據(K=3)的類型是什么,由圖可知離圓形近期的三個中間有兩個是矩形,一個是三角形,故將新數據判定為屬於矩形這個類別。
- 看離圓形近期的9個數據(K=9)的類型是什么,由圖可知離圓形近期的9個數據中間,有五個是三角形。四個是矩形。故新數據判定為屬於三角形這個類別。
上面所說的三種情況也能夠說成是1-近鄰方法、3-近鄰方法、9-近鄰方法。。。當然,K還能夠取更大的值,當樣本足夠多,且樣本類別的分布足夠好的話,那么K值越大,划分的類別就越正確。而KNN中的K表示的就是划分數據時。所取類似樣本的個數。
我們都知道,當K=1時,其抗干擾能力就較差。由於假如樣本中出現了某種偶然的類別,那么新的數據非常有可能被分錯。為了添加分類的可靠性,能夠考察待測數據的K個近期鄰樣本 。統計這K個近鄰樣本中屬於哪一類別的樣本最多,就將樣本X判屬於該類。
當然。假設在樣本有限的情況下,KNN算法的誤判概率和距離的詳細測度方法就有了直接關系。即用何種方式判定哪些數據與新數據近鄰。不同的樣本選擇不同的距離測量函數,這能夠提高分類的正確率。通常情況下,KNN能夠採用Euclidean(歐幾里得)、Manhattan(曼哈頓)、Mahalanobis(馬氏距離)等距離用於計算。
- Euclidean距離為:
d(x⃗ ,y⃗ )=[∑i=1n(xi−yi)2] x⃗ =(x1,x2,...,xn) y⃗ =(y1,y2,...,yn) - Manhattan距離為:
d(x⃗ ,y⃗ )=∑i=1n|xi−yi| - Mahalanobis距離為:
當中n為特征的維數,
d(x⃗ ,y⃗ )=(x⃗ −y⃗ )′V−1(x⃗ −y⃗ ) V 為x⃗ 和y⃗ 所在的數據集的協方差函數。
以下給出KNN學習的偽代碼:
Algorithm KNN(A[n],k,x)
Input:
A[n]為N個訓練樣本的特征,K為近鄰數,x為新的樣本;
Initialize:
取A[1]~A[k]作為x的初始近鄰。
計算測試樣本與x間的歐式距離d(x,A[i]),i=1,2...,k;
按d(x,A[i])升序排序。
計算最遠樣本與x間距離D。即max{d(x,A[i])};
for(i=k+1;i<=n;i++)
計算A[i]與x之間的距離d(x,A[i]);
if (d(x,A[i]))<D then 用A[i]取代最遠樣本。
依照d(x,A[i])升序排序;
計算最遠樣本與x間的距離D,即max{d(x,A[i])};
End for
計算前K個樣本A[i],i=1,2...,k所屬類別的概率。
具有最大概率的類別即為樣本x的類;
Output:x所屬的類別。
KNN的不足
1、添加某些類別的樣本容量非常大,而其它類樣本容量非常小,即已知的樣本數量不均衡。有可能當輸入一個和小容量類同樣的的新樣本時,該樣本的K個近鄰中,大容量類的樣本占多數,從而導致誤分類。
針對此種情況能夠採用加權的方法,即和該樣本距離小的近鄰所相應的權值越大,將權值納入分類的參考根據。
2、分類時須要先計算待分類樣本和全體已知樣本的距離。才干求得所需的K近鄰點,計算量較大,尤其是樣本數量較多時。
針對這樣的情況能夠事先對已知樣本點進行剪輯。去除對分類作用不大的樣本,這一處理步驟僅適用於樣本容量較大的情況,假設在原始樣本數量較少時採用這樣的處理。反而會添加誤分類的概率。
改進的KNN算法
KNN學習easy受噪聲影響,尤其是樣本中的孤立點對分類或回歸處理有非常大的影響。因此通常也對已知樣本進行濾波和篩選,去除對分類有干擾的樣本。
K值得選取也會影響分類結果。因此需根據每類樣本的數目和分散程度選取合理的K值,而且對不同的應用也要考慮K值得選擇。
基於組合分類器的KNN改進算法
經常使用的組合分類器方法有投票法、非投票法、動態法和靜態法等,比方簡單的投票法中全部的基分類器對分類採取同樣的權值;權值投票法中每一個基分類器具有相關的動態權重,該權重能夠隨時間變化。
首先隨機選擇屬性子集。構建多個K近鄰分類器;然后對未分類元組進行分類。最后把分類器的分類結果依照投票法進行組合,將得票最多的分類器作為終於組合近鄰分類器的輸出。
基於核映射的KNN改進算法
將原空間
首先進行非線性映射:
實踐代碼
以下給出一個簡單的KNN分類的MATLAB實踐代碼:
main.m文件
function main
trainData = [ 0.6213 0.5226 0.9797 0.9568 0.8801 0.8757 0.1730 0.2714 0.2523 0.7373 0.8939 0.6614 0.0118 0.1991 0.0648 0.2987 0.2844 0.4692 ];
trainClass = [ 1 1 1 2 2 2 3 3 3 ];
testData = [ 0.9883 0.5828 0.4235 0.5155 0.3340 0.4329 0.2259 0.5798 0.7604 0.5298 ];
% main
testClass = cvKnn(testData, trainData, trainClass);
% plot prototype vectors
classLabel = unique(trainClass);
nClass = length(classLabel);
plotLabel = {'r*', 'g*', 'b*'};
figure;
for i=1:nClass
A = trainData(:, trainClass == classLabel(i));
plot(A(1,:), A(2,:), plotLabel{i});
hold on;
end
% plot classifiee vectors
plotLabel = {'ro', 'go', 'bo'};
for i=1:nClass
A = testData(:, testClass == classLabel(i));
plot(A(1,:), A(2,:), plotLabel{i});
hold on;
end
legend('1: prototype','2: prototype', '3: prototype', '1: classifiee', '2: classifiee', '3: classifiee', 'Location', 'NorthWest');
title('K nearest neighbor');
hold off;
KNN.m文件
function [Class, Rank] = cvKnn(X, Proto, ProtoClass, K, distFunc)
if ~exist('K', 'var') || isempty(K)
K = 1;%默覺得K = 1
end
if ~exist('distFunc', 'var') || isempty(distFunc)
distFunc = @cvEucdist;
end
if size(X, 1) ~= size(Proto, 1)
error('Dimensions of classifiee vectors and prototype vectors do not match.');
end
[D, N] = size(X);
% Calculate euclidean distances between classifiees and prototypes
d = distFunc(X, Proto);
if K == 1, % sort distances only if K>1
[mini, IndexProto] = min(d, [], 2); % 2 == row%每列的最小元素
Class = ProtoClass(IndexProto);
if nargout == 2, % instance indices in similarity descending order
[sorted, ind] = sort(d'); % PxN
RankIndex = ProtoClass(ind); %,e.g., [2 1 2 3 1 5 4 1 2]'
% conv into, e.g., [2 1 3 5 4]'
for n = 1:N
[ClassLabel, ind] = unique(RankIndex(:,n),'first');
[sorted, ind] = sort(ind);
Rank(:,n) = ClassLabel(ind);
end
end
else
[sorted, IndexProto] = sort(d'); % PxN
clear d。
% K closest
IndexProto = IndexProto(1:K,:);
KnnClass = ProtoClass(IndexProto);
% Find all class labels
ClassLabel = unique(ProtoClass);
nClass = length(ClassLabel);
for i = 1:nClass
ClassCounter(i,:) = sum(KnnClass == ClassLabel(i));
end
[maxi, winnerLabelIndex] = max(ClassCounter, [], 1); % 1 == col
% Future Work: Handle ties somehow
Class = ClassLabel(winnerLabelIndex);
end
Eucdist.m文件
function d = cvEucdist(X, Y)
if ~exist('Y', 'var') || isempty(Y)
%% Y = zeros(size(X, 1), 1);
U = ones(size(X, 1), 1);
d = abs(X'.^2*U).'; return; end V = ~isnan(X); X(~V) = 0; % V = ones(D, N); %clear V; U = ~isnan(Y); Y(~U) = 0; % U = ones(D, P); %clear U; %d = abs(X'.^2*U - 2*X'*Y + V'*Y.^2);
d1 = X'.^2*U;
d3 = V'*Y.^2;
d2 = X'*Y;
d = abs(d1-2*d2+d3);
代碼效果例如以下:

