一、kNN算法概述
K最近鄰(k-Nearest Neighbor,KNN)分類算法。它主要測量不同特徵值之間的距離方法進行分類。思想很簡單:如果一個樣本在特徵空間中的k個最相似(即特徵空間中最鄰近)的樣本中的大多數屬於某一個類別,則該樣本也屬於這個類別。
比如上面這個圖,我們有兩類數據,分別是藍色方塊和紅色三角形,他們分佈在一個上圖的二維中間中。那麼假如我們有一個綠色圓圈這個數據,需要判斷這個數據是屬於藍色方塊這一類,還是與紅色三角形同類。怎麼做呢?我們先把離這個綠色圓圈最近的幾個點找到,因爲我們覺得離綠色圓圈最近的纔對它的類別有判斷的幫助。那到底要用多少個來判斷呢?這個個數就是k了。如果k=3,就表示我們選擇離綠色圓圈最近的3個點來判斷,由於紅色三角形所佔比例爲2/3,所以我們認爲綠色圓是和紅色三角形同類。如果k=5,由於藍色四方形比例爲3/5,因此綠色圓被賦予藍色四方形類。從這裏可以看到,k的值還是很重要的。
KNN算法中,所選擇的鄰居都是已經正確分類的對象。該方法在定類決策上只依據最鄰近的一個或者幾個樣本的類別來決定待分樣本所屬的類別。由於KNN方法主要靠周圍有限的鄰近的樣本,而不是靠判別類域的方法來確定所屬類別的,因此對於類域的交叉或重疊較多的待分樣本集來說,KNN方法較其他方法更爲適合。
該算法在分類時有個主要的不足是,當樣本不平衡時,如一個類的樣本容量很大,而其他類樣本容量很小時,有可能導致當輸入一個新樣本時,該樣本的K個鄰居中大容量類的樣本佔多數。因此可以採用權值的方法(和該樣本距離小的鄰居權值大)來改進。該方法的另一個不足之處是計算量較大,因爲對每一個待分類的文本都要計算它到全體已知樣本的距離,才能求得它的K個最近鄰點。目前常用的解決方法是事先對已知樣本點進行剪輯,事先去除對分類作用不大的樣本。該算法比較適用於樣本容量比較大的類域的自動分類,而那些樣本容量較小的類域採用這種算法比較容易產生誤分[參考機器學習十大算法]。
總的來說就是我們已經存在了一個帶標籤的數據庫,然後輸入沒有標籤的新數據後,將新數據的每個特徵與樣本集中數據對應的特徵進行比較,然後算法提取樣本集中特徵最相似(最近鄰)的分類標籤。一般來說,只選擇樣本數據庫中前k個最相似的數據。最後,選擇k個最相似數據中出現次數最多的分類。其算法描述如下:
1)計算已知類別數據集中的點與當前點之間的距離;
2)按照距離遞增次序排序;
3)選取與當前點距離最小的k個點;
4)確定前k個點所在類別的出現頻率;
5)返回前k個點出現頻率最高的類別作爲當前點的預測分類。
二、Python代碼實現如下:
- from numpy import *
- import operator
- # create a dataset which contains 4 samples with 2 classes
- def createDataSet():
- # create a matrix: each row as a sample
- group = array([[1.0, 0.9], [1.0, 1.0], [0.1, 0.2], [0.0, 0.1]])
- labels = ['A', 'A', 'B', 'B'] # four samples and two classes
- return group, labels
- # classify using kNN
- def kNNClassify(newInput, dataSet, labels, k):
- numSamples = dataSet.shape[0] # shape[0] stands for the num of row
- ## step 1: calculate Euclidean distance
- # tile(A, reps): Construct an array by repeating A reps times
- # the following copy numSamples rows for dataSet
- diff = tile(newInput, (numSamples, 1)) - dataSet # Subtract element-wise
- squaredDiff = diff ** 2 # squared for the subtract
- squaredDist = sum(squaredDiff, axis = 1) # sum is performed by row
- distance = squaredDist ** 0.5
- ## step 2: sort the distance
- # argsort() returns the indices that would sort an array in a ascending order
- sortedDistIndices = argsort(distance)
- classCount = {} # define a dictionary (can be append element)
- for i in xrange(k):
- ## step 3: choose the min k distance
- voteLabel = labels[sortedDistIndices[i]]
- ## step 4: count the times labels occur
- # when the key voteLabel is not in dictionary classCount, get()
- # will return 0
- classCount[voteLabel] = classCount.get(voteLabel, 0) + 1
- ## step 5: the max voted class will return
- maxCount = 0
- for key, value in classCount.items():
- if value > maxCount:
- maxCount = value
- maxIndex = key
- return maxIndex