【機器學習】KNN(k-Nearest Neighbor)算法

K近鄰(k-Nearest Neighbor, 簡稱KNN)算法是一種非常簡單的機器學習監督算法。它的主要思想是:給定一個測試數據,如果離它最近的K個訓練數據大多都屬於某一個類別,則認爲該測試數據也屬於這個類別。以下圖爲例,圖中的綠色點表示測試數據,現在我們需要判斷這個點應該是紅色三角形還是藍色正方形。按照上述思想,如果K取3,則離綠色點最近的點中有兩個是紅色三角形,一個是藍色正方形,因此KNN判斷綠色點爲紅色三角形。
KNN例子

可見,KNN的思想確實十分簡單,實現也很容易。但是我們思考一下爲什麼可以判斷兩個數據之間的“遠近”?回到現實中的例子,當你判斷是一張桌子理離你近,還是椅子離你近時,我們的衡量標準其實就是看桌子和椅子和我們的距離。回想一下:計算一條直線上兩點的距離,我們可以直接將兩點的座標值相減後取絕對值得到;計算一個平面內兩個點的距離,我們可以將點的各個座標值帶入歐式距離計算公式得到距離;同樣地,計算三維空間中點的距離,也是將點的各個座標值帶入歐式距離計算公式得到。那麼推廣到d維空間中的點,我們也可以點的各個座標值帶入歐式距離計算公式得到。但是,各個點的座標是如何確定的呢?其實就是我們引入了一個空間座標系,d維空間中的一個點其實就是一個向量,這個向量的各個數值就對應了其在各個維度座標軸上的取值。其實我們也可以將機器學習中的每個樣本看作是d維空間中的一個點,這樣我們就可以採用空間中的距離計算方法來衡量樣本之間的距離,因此纔會有所謂的“遠近”。也就是說,KNN將每一個數據都看作是d維空間中的一個點,在這個空間中採用了座標系來描述這個點的位置,再通過某種距離計算公式來表示這兩個點之間的距離。

KNN分類

給定一個d維的測試數據 xRd ,KNN對於x的分類過程如下:

  • 計算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))

距離計算方法

衡量兩個樣本之間距離除了可以使用歐式距離以外,還可以採用閔可夫斯基距離, 曼哈頓距離,切比雪夫距離,餘弦距離等等。

閔可夫斯基距離也叫做明氏距離,計算公式爲:(ni=1|xiyi|p)1p 。當p=1時,就是曼哈頓距離;當p=2時,就是歐氏距離;當p取無窮時,就是切比雪夫距離:
limp(ni=1|xiyi|p)1p=maxni=1|xiyi|

K的取值

k值的不同預測結果也會很不一樣,因此選取一個合適的k值是很有必要的。最簡單的就是k=1,即只看與測試數據最近的那個訓練數據。但是很有可能比這個訓練數居遠一點點的那些數據,大多數是另外一個分類標籤,這樣就會導致預測錯誤。最複雜的就是k=N,即k等於訓練樣本的總個數。這雖然考慮了所有的文本,大大提高了預測結果的可靠性,但是會帶來大量的計算,降低算法的速度。而且,訓練數據只是無窮數據中的一部分,訓練數據集中樣本的標籤不能代表所有的數據類別標籤分佈。還可以將k取不同的值,選定訓練錯誤率最低的k值。但是這種方法同樣會花費大量的時間。 一個比較合理的取值是k=(N) ,這樣不僅可以提高預測準確率,還可以將時間控制在一個比較合理的範圍。

發佈了37 篇原創文章 · 獲贊 52 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章