網上介紹K-近鄰算法的樣例非常多。其Python實現版本號基本都是來自於機器學習的入門書籍《機器學習實戰》,盡管K-近鄰算法本身非常easy,但非常多剛開始學習的人對其Python版本號的源碼理解不夠,所以本文將對其源碼進行分析。
什么是K-近鄰算法?
簡單的說,K-近鄰算法採用不同特征值之間的距離方法進行分類。所以它是一個分類算法。
長處:無數據輸入假定,對異常值不敏感
缺點:復雜度高
好了,直接先上代碼,等會在分析:(這份代碼來自《機器學習實戰》)
def classify0(inx, dataset, lables, k): dataSetSize = dataset.shape[0] diffMat = tile(inx, (dataSetSize, 1)) - dataset sqDiffMat = diffMat**2 sqDistance = sqDiffMat.sum(axis=1) distances = sqDistance**0.5 sortedDistances = distances.argsort() classCount={} for i in range(k): label = lables[sortedDistances[i]] classCount[label] = classCount.get(label, 0) + 1 sortedClassCount = sorted(classCount.iteritems(),key=operator.itemgetter(1), reverse=True) return sortedClassCount[0][0]
該函數的原理是:
存在一個樣本數據集合,也稱為訓練集,在樣本集中每一個數據都存在標簽。在我們輸入沒有標簽的新數據后,將新數據的每一個特征與樣本集中相應的特征進行比較,然后提取最相似(近期鄰)的分類標簽。
一般我們僅僅選樣本數據集中前K 個最相似的數據。最后。出現次數最多的分類就是新數據的分類。
classify0函數的參數意義例如以下:
inx : 是輸入沒有標簽的新數據,表示為一個向量。
dataset: 是樣本集。
表示為向量數組。
labels:相應樣本集的標簽。
k:即所選的前K。
用於產生數據樣本的簡單函數:
def create_dataset(): group = array([[1.0, 1.1], [1.0, 1.1], [0, 0], [0, 0.1]]) labels = ['A', 'A', 'B', 'B'] return group, labels
注意,array是numpy里面的。
我們須要實現import進來。
from numpy import * import operator
我們在調用時。
group,labels = create_dataset() result = classify0([0,0], group, labels, 3) print result
顯然,[0,0]特征向量肯定是屬於B 的,上面也將打印B。
知道了這些。剛開始學習的人應該對實際代碼還是非常陌生。不急,正文開始了!
源代碼分析
dataSetSize = dataset.shape[0]
shape是array的屬性,它描寫敘述了一個數組的“形狀”,也就是它的維度。比方,
In [2]: dataset = array([[1.0, 1.1], [1.0, 1.1], [0, 0], [0, 0.1]]) In [3]: print dataset.shape (4, 2)
所以,dataset.shape[0] 就是樣本集的個數。
diffMat = tile(inx, (dataSetSize, 1)) - dataset
tile(A,rep)函數是基於數組A來構造數組的,詳細怎么構造就看第二個參數了。其API介紹有點繞,但簡單的使用方法相信幾個樣例就能明確。
我們看看tile(inx, (4, 1))的結果,
In [5]: tile(x, (4, 1)) Out[5]: array([[0, 0], [0, 0], [0, 0], [0, 0]])
你看。4擴展的是數組的個數(本來1個。如今4個),1擴展的是每一個數組元素的個數(原來是2個,如今還是兩個)。
為證實上面的結論,
In [6]: tile(x,(4,2)) Out[6]: array([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]])
和。
In [7]: tile(x,(2,2)) Out[7]: array([[0, 0, 0, 0], [0, 0, 0, 0]])
關於,tile的詳細使用方法。請自行查閱API DOC。
得到tile后,減去dataset。
這類似一個矩陣的減法。結果仍是一個 4 * 2的數組。
In [8]: tile(x, (4, 1)) - dataset Out[8]: array([[-1. , -1.1], [-1. , -1.1], [ 0. , 0. ], [ 0. , -0.1]])
結合歐式距離的求法,后面的代碼就清晰些,對上面結果平方運算,求和。開方。
我們看看求和的方法,
sqDiffMat.sum(axis=1)
當中。
In [14]: sqDiffmat Out[14]: array([[ 1. , 1.21], [ 1. , 1.21], [ 0. , 0. ], [ 0. , 0.01]])
求和的結果是對行求和,是一個N*1的數組。
假設要對列求和,
sqlDiffMat.sum(axis=0)
argsort()是對數組升序排序的。
classCount是一個字典,key是標簽。value是該標簽出現的次數。
這樣。算法的一些詳細代碼細節就清楚了。