1. 程式人生 > >K最近鄰演算法

K最近鄰演算法

設想你想了解一個陌生人的飲食風格,如果你對他所知無幾,那麼最容易想到的一個捷徑就是看看他生存的周圍人群的口味。但是如果你對他的資訊知道更多,例如知道他的年齡、收入等,那麼這個時候就最好從他周圍的人群中去挑選與他年齡、收入相近的人的飲食風格,這樣預測會更準確一點。這其中蘊含的演算法就是最近鄰演算法。

最近鄰演算法的思想很簡單,”距離“相近的事物總會具有更多的共性。其中涉及的數學知識並不深厚。

要想運用最近鄰演算法解決問題,得明確要個要點。一是設定一種距離的定義,這個距離可能是物理距離,也可能是實際屬性之間的抽象距離;二是要假定物以類聚人以群分,即距離相近的事物總是有更多的共性。

最近鄰演算法是一種預測型演算法,它在實際操作中會忽略很多要素,只是給出結論而並不會描述結論的具體推理。例如,預測一個人的飲食風格只是根據與他相近的人來預測的,而並沒有說明這個人的年齡、收入是如何影響他的飲食風格的。

為什麼要設定成K近鄰呢?這是因為在實際操作中,我們要首先確定一個合理的K值,假如我們需要預測一個事物的某項特徵,找出被預測事物的K個最近鄰,然後讓這K個最近鄰對預測結果進行投票,最終去投票最高的作為預測結果。

首先,我們先出投票決策的程式碼:

 from collections import Counter
  def vote(labels):
      votes=Counter(labels)
      winner,_=votes.most_common(1)[0]
      return winner
上述程式碼是找出投票中票數最高的那個標籤。但是這種決策有點死板,例如如果現在我們要給一部電影評分,這部電影的最相近的五部電影的評分分別為:A、A、B、B、C,其中A和B均出現了兩次,那麼此時我們對這部電影應該評A還是B呢?也許有人會想到以下幾種方案供選擇:

從A、B中隨機挑選一個作為最終評分;
對投票按距離設定一定權重,取最終的加權評分;
減小K(這裡是5)直到發現一個最佳評分;

這裡,我們實現一下第三種方案:
def vote(labels):
      votes=Counter(labels)
      winner,winner_count=votes.most_common(1)[0]
      num=len([count for count in votes.values() if count==winner_count])
      if num==1:
          return winner
      else:
          return vote(labels[:-1])
      return winner
上述程式碼實現了當有不同投票的數目一致時,將K值降低1來重新投票,假如labels是按照近鄰的距離由近到遠排序的,那麼此舉就是剔除了距離最遠的那個近鄰。這種方案肯定能有預測結果,最差的情況就是最後只剩一個近鄰了。來寫一下最終的K近鄰演算法的主體程式碼:
def knn(K,labeled_points,new_point):
      distance=sorted(labeled_points,key=lambda(point,):distance(point,new_point))
      k_nearest_labels=[label for label in distance[:K]]
      return vote(k_nearest_labels)
K近鄰演算法的應用很多,因為它操作起來相對簡單,適合對事物進行預測。K近鄰演算法事宜應用在低維特徵問題中,這是因為如果特徵點是高維的話,那麼在高維空間中,點與點之間靠的並不那麼鄰近,因此高維情形中使用K近鄰演算法來做預測似乎並不準確,這個時候,很有必要在應用K近鄰演算法之前進行特徵點降維。