KNN 算法介绍

导引

在机器学习中,通过不断减少偏差来实现对“神秘函数”逼近的方法非常常见,但这可能会出现陷入“局部最优解“的问题,导致这个问题的根本原因在于这种训练方法总是”走一步看一步“,也就是通常所说的”缺乏大局观“。
想要有大局观也好办,只要设计一套机器学习算法,能够先获得某种全局性的统计值,然后在全局统计值的基础上完成分类等预测工作,就可以避免陷入局部最优解了。

回忆一下, Logistic回归的核心机制就是用损失函数和优化方法来不断调整线性函数的权值,从而拟合“神秘函数”,而偏差可谓是这套机制的动力来源。KNN算法要避免局部最优解,当然不可能再沿用这套依赖偏差的机制,但KNN作为有监督学习算法,必须设计一套参考标尺,以让标注信息发挥作用。

一种新的分类方法:用已经分好类的样本作为参照,看看待分类的样本与哪一类更近,就归为哪一类。多数表决距离是 KNN 算法中最重要的两个概念。

多数表决

我们知道,实际的样本是有许多维度的,如鸢尾花数据集,虽然它是一个非常小的数据集,但每个鸢尾花样本也包含了花萼的长度、宽度和花瓣的长度、宽度4个维度,从不同维度看,样本数据点的分布情况不相同。假设每次任意取鸢尾花数据集4个维度中的2个,分别作为图像的X轴和Y轴坐标,将得到16张图像。
【ML 入门】KNN (K-最邻近)分类算法 - 图1
可以看出,对于同样的样本,选取不同的维度之后,类与类之间呈现出了犬牙交错的更为复杂的关系,而同一类的“聚集”趋势也变得不太明显,类内样本的分布范围更广,与其他类的样本混杂在一起的可能性变得更大。

如何选取维度是KNN算法乃至机器学习都需要重点关注的问题,选取合适的维度可以事半功倍。不同的维度选择可能导致样本呈现不同的分布情况。

不同的机器学习算法对于维度的选取有着不同的办法, Logistic回归算法采用“加权”的办法,让做出正面贡献的维度输入增加权值,而NN算法则选择了更为直接和“文艺”的方法:多数表决。

“多数表决”这个词也许会让你联想到举手、表决器或者投票箱,但这都只是形式,其实质是数人数。物以类聚,人以群分,你是哪一类人,查查你的朋友圈也许就能知道答案。KNN分类算法所采用的方法同样也是“查朋友圈”。前面我们说过,训练集的样本已经按正负类分好了,现在过来一个待分类的新样本,只需要查一查“朋友圈”,也就是根据它各个维度的值,看看与临近的点都是什么类,按多数表决原则,哪些类占大多数,这个新样本就属于哪一类。

表决权问题

顾名思义,表决权就是谁可以参与表决,划定的可参与者的范围不同,哪一类占大多数可能也就随之发生改变。

可是,分类问题的训练集样本可没有什么朋友圈,需要我们设计一个方法来划定这个“圈”。对于KNN,这个圈就是由距离决定的。具体来说,根据样本各个维度的值可以作出一个个数据点,我们只需要度量点与点之间的距离,然后若想给某个点划分“朋友圈”,只需要以这个点为圆心,就可以找到与它临近的点有哪些,从而构成它的“朋友圈”。只有在圈子里的点才拥有对于这个点属于哪个类的表决权,而不是由全体样本进行表决。这是一个需要注意的细节。

再说说距离度量。在二维平面上,我们习惯用直尺来测量距离,所以也没有觉得这有难度。但是,数据点常常有多个维度,如鸢尾花的数据点就有4个维度,这就没法直接用尺子量了。怎么办呢?数学家已经为我们准备好了多种衡量距离的数学工具,根据需要选取其中一种,同样可以确定点与点之间的距离,以及指定的点与谁临近。至于为什么用距离确定“朋友圈”,其实也好理解,走得近的当然才称得上朋友嘛!

KNN 的具体含义

KNN 是 K-Nearest-Neighbor 的英文缩写,直译就是K个最近邻,有人干脆称之为“最近邻算法”。字母“K”也许看着新鲜,不过作用其实早在中学就接触过。在学习排列组合时,教材都喜欢用字母“n”来指代多个,譬如“求n个数的和”,这里面也没有什么秘密,就是约定俗成的用法。而KNN算法的字母K扮演的就是与n同样的角色。K的值是多少,就代表使用了多少个最近邻。机器学习总要有自己的约定俗成,没来由地就是喜爱用“K”而不是“n”来指代多个,类似的命名方法还有后面将要提到的K- means算法。

KNN的关键在于最近邻,光看名字似乎与分类没有什么关系,但前面我们介绍了,KNN的核心在于多数表决,而谁有投票表决权呢?就是这个“最近邻”,也就是以待分类样本点为中心,距离最近的K个点。这K个点中什么类别的占比最多,待分类样本点就属于什么类别。

KNN 分类的算法原理

基本思路

  1. 怎样确定“K”?
  2. 怎样确定“NN”?

1. 怎样确定 “K” ?

在KNN算法中,将K值设置为多少没有具体的计算公式,只能根据各位的实战经验确定了。
一般情况下,K的值会在 3~10 之间。

2. 怎样确定“NN”?

用什么方法度量“最近”,正是 KNN 以及相关衍生算法需要解决的首要问题,是难点,也是创新点。
感谢一位数学家,他不但真的整理了一组能用做距离度量的方法,还把这些方法提炼成了范式,这个范式就是闵可夫斯基距离(Minkowski Distance)。

KNN 分类算法的数学解析

闵可夫斯基距离,虽然它的名字包含“距离”,但实际上是对一类距离的统一定义:

【ML 入门】KNN (K-最邻近)分类算法 - 图2%20%3D%20(%5Csum%7Bi%3D1%7D%5En%20%7B%7C%20x_i%20-%20y_i%7C%20%5E%20P%7D)%20%5E%20%7B1%20%2F%20P%7D%0A#card=math&code=d_p%20%28x%2C%20y%29%20%3D%20%28%5Csum%7Bi%3D1%7D%5En%20%7B%7C%20x_i%20-%20y_i%7C%20%5E%20P%7D%29%20%5E%20%7B1%20%2F%20P%7D%0A)

  • 当 P = 1 时,称为曼哈顿距离

【ML 入门】KNN (K-最邻近)分类算法 - 图3%20%3D%20%5Csum%7Bk%3D1%7D%5En%20%7B%7Cx_k%20-%20y_k%7C%7D%0A#card=math&code=d%28x%2C%20y%29%20%3D%20%5Csum%7Bk%3D1%7D%5En%20%7B%7Cx_k%20-%20y_k%7C%7D%0A)

x、y表示n维空间中的两个点。右边表示对每一个维度进行计算并求和。、

  • 当 p = 2 时,称为欧几里得距离,最常用于度量两点之间的直线距离:

【ML 入门】KNN (K-最邻近)分类算法 - 图4%20%20%3D%20%5Csqrt%20%7B%20%5Csum%7Bi%3D1%7D%5En%20(x_i%20-%20y_i)%20%5E%202%7D%0A#card=math&code=d_2%20%28x.%20y%29%20%20%3D%20%5Csqrt%20%7B%20%5Csum%7Bi%3D1%7D%5En%20%28x_i%20-%20y_i%29%20%5E%202%7D%0A)

距离的度量方法没有好坏,选择什么方法主要是根据当前情况而定。如果其他样本点呈圆形分布,而待分类点正好处于圆心,这种情况下由于到各个点的欧式距离都一样, 也就无法使用欧式距离进行选择。曼哈顿距离相当于“数格子”,相比之下具有更高的稳定性,但同样存在丢失距离信息的问题,最终还是需要根据实际情况进行选择。

KNN 分类算法的具体步骤

  1. 找K个最近邻。KNN分类算法的核心就是找最近的K个点,选定度量距离的方法之后,以待分类样本点为中心,分别测量它到其他点的距离,找出其中的距离最近的“TOP K”,这就是K个最近邻。
  2. 统计最近邻的类别占比。确定了最近邻之后,统计出每种类别在最近邻中的占比。
  3. 选取占比最多的类别作为待分类样本的类别。

在 Python 中使用 KNN 分类算法

介绍

在 Scikit-Learn-机器学习库中,最近邻模型算法族都在 neighbors类库下当前版本一共有13个类,不过,看似有很多类,但都是由几种基本算法经过部分调整以及组合而成,具有代表性的几个类如下:

  • KNeighborsClassifier类:最经典的KNN分类算法。
  • KNeighborsRegressor类:利用KNN算法解决回归问题。
  • RadiusNeighborsClassifier:基于固定半径来查找最近邻的分类算法。
  • NearestNeighbors类:基于无监督学习实现KNN算法。
  • KDTree类:无监督学习下基于 KDTree来查找最近邻的分类算法。
  • BallTree类:无监督学习下基于 BallTree来查找最近邻的分类算法。

实现

  1. from sklearn.datasets import load_iris
  2. # 导入近邻模型的 KNN 分类算法
  3. from sklearn.neighbors import KNeighborsClassifier
  4. # 加载数据集
  5. X, y = load_iris(return_X_y=True)
  6. # 训练模型
  7. model = KNeighborsClassifier()
  8. clf = model.fit(X, y)
  9. # 预测
  10. clf.predict(X)
  11. # 使用默认的性能评估器进行评分
  12. clf.score(X, y)

输出结果如下:
【ML 入门】KNN (K-最邻近)分类算法 - 图5