K近鄰(k-Nearest Neighbor, 簡稱KNN)算法是一種非常簡單的機器學習監督算法。它的主要思想是:給定一個測試數據,如果離它最近的K個訓練數據大多都屬於某一個類別,則認爲該測試數據也屬於這個類別。以下圖爲例,圖中的綠色點表示測試數據,現在我們需要判斷這個點應該是紅色三角形還是藍色正方形。按照上述思想,如果K取3,則離綠色點最近的點中有兩個是紅色三角形,一個是藍色正方形,因此KNN判斷綠色點爲紅色三角形。
可見,KNN的思想確實十分簡單,實現也很容易。但是我們思考一下爲什麼可以判斷兩個數據之間的“遠近”?回到現實中的例子,當你判斷是一張桌子理離你近,還是椅子離你近時,我們的衡量標準其實就是看桌子和椅子和我們的距離。回想一下:計算一條直線上兩點的距離,我們可以直接將兩點的座標值相減後取絕對值得到;計算一個平面內兩個點的距離,我們可以將點的各個座標值帶入歐式距離計算公式得到距離;同樣地,計算三維空間中點的距離,也是將點的各個座標值帶入歐式距離計算公式得到。那麼推廣到d維空間中的點,我們也可以點的各個座標值帶入歐式距離計算公式得到。但是,各個點的座標是如何確定的呢?其實就是我們引入了一個空間座標系,d維空間中的一個點其實就是一個向量,這個向量的各個數值就對應了其在各個維度座標軸上的取值。其實我們也可以將機器學習中的每個樣本看作是d維空間中的一個點,這樣我們就可以採用空間中的距離計算方法來衡量樣本之間的距離,因此纔會有所謂的“遠近”。也就是說,KNN將每一個數據都看作是d維空間中的一個點,在這個空間中採用了座標系來描述這個點的位置,再通過某種距離計算公式來表示這兩個點之間的距離。
KNN分類
給定一個d維的測試數據
- 計算x與每一個訓練數據之間的距離
- 對計算得到的距離按照從小到大的順序排序
找到距離最小的k個訓練數據,採用多數投票的方式得到k個訓練數據中出現次數最多的類標籤,將x判定爲這個類標籤。
使用pyhton實現KNN如下:
def knn_cla(train_dat, train_label, test_dat, k = 1): ''' k-neighbour nearest ''' ntrain = train_dat.shape[0] pred_label = list() for i, test_item in enumerate(test_dat): dis = [] for train_item in train_dat: dis.append(distance.euclidean(test_item, train_item)) nearest_ind = range(ntrain) sorted(nearest_ind, key=lambda x:dis[x]) nearest_label = [train_label[nearest_ind[x]] for x in range(k)] pred_label.append(Counter(nearest_label).most_common()[0][0]) return np.array(pred_label)
KNN 迴歸
在分類任務中,KNN採取多數投票的方法判定測試數據的類別。在迴歸任務中可以採取多種方法判定。“平均法”將找出的K個最近測試樣本的實值輸出的平均值作爲預測結果;“加權平均”將權重的倒數作爲K個樣本的權重值,從而計算加權平均,距離越近的樣本其權重越大。
def knn_reg(train_dat, train_prob, test_dat, k = 1):
'''
k-neighbour nearest
'''
ntrain = train_dat.shape[0]
ntest = test_dat.shape[0]
pred_prob = np.zeros((ntest, 6))
for i, test_item in enumerate(test_dat):
dis = []
for train_item in train_dat:
dis.append(distance.euclidean(test_item, train_item))
nearest_ind = list(range(ntrain))
nearest_ind = sorted(nearest_ind, key=lambda x:dis[x])
weight = np.array(list(map(lambda x: 1/(x+0.001), [dis[x] for x in range(k)]))) # avoid divide by 0 error
weight = weight / weight.sum(keepdims=True) # noralize weight
for x in range(k):
pred_prob[i] += (train_prob[nearest_ind[x]] * weight[x])
return (pred_prob / pred_prob.sum(axis=1, keepdims=True))
距離計算方法
衡量兩個樣本之間距離除了可以使用歐式距離以外,還可以採用閔可夫斯基距離, 曼哈頓距離,切比雪夫距離,餘弦距離等等。
閔可夫斯基距離也叫做明氏距離,計算公式爲:
K的取值
k值的不同預測結果也會很不一樣,因此選取一個合適的k值是很有必要的。最簡單的就是k=1,即只看與測試數據最近的那個訓練數據。但是很有可能比這個訓練數居遠一點點的那些數據,大多數是另外一個分類標籤,這樣就會導致預測錯誤。最複雜的就是k=N,即k等於訓練樣本的總個數。這雖然考慮了所有的文本,大大提高了預測結果的可靠性,但是會帶來大量的計算,降低算法的速度。而且,訓練數據只是無窮數據中的一部分,訓練數據集中樣本的標籤不能代表所有的數據類別標籤分佈。還可以將k取不同的值,選定訓練錯誤率最低的k值。但是這種方法同樣會花費大量的時間。 一個比較合理的取值是