设想你想了解一个陌生人的饮食风格,如果你对他所知无几,那么最容易想到的一个捷径就是看看他生存的周围人群的口味。但是如果你对他的信息知道更多,例如知道他的年龄、收入等,那么这个时候就最好从他周围的人群中去挑选与他年龄、收入相近的人的饮食风格,这样预测会更准确一点。这其中蕴含的算法就是最近邻算法。

最近邻算法的思想很简单,”距离“相近的事物总会具有更多的共性。其中涉及的数学知识并不深厚。

要想运用最近邻算法解决问题,得明确要个要点。一是设定一种距离的定义,这个距离可能是物理距离,也可能是实际属性之间的抽象距离;二是要假定物以类聚人以群分,即距离相近的事物总是有更多的共性。

最近邻算法是一种预测型算法,它在实际操作中会忽略很多要素,只是给出结论而并不会描述结论的具体推理。例如,预测一个人的饮食风格只是根据与他相近的人来预测的,而并没有说明这个人的年龄、收入是如何影响他的饮食风格的。

为什么要设置成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近邻算法之前进行特征点降维。