前言
KNN一般用于有监督的分类场景,除此之外,KNN在异常检测场景中也有应用,下面主要介绍下KNN在这两面的应用原理。
KNN做分类的原理
计算步骤如下:
1)算距离:给定测试对象,计算它与训练集中的每个对象的距离
2)找邻居:圈定距离最近的k个训练对象,作为测试对象的近邻
3)做分类:根据这k个近邻归属的主要类别,来对测试对象分类
(看未知类别样本最近的K个样本的类别,那种类别多,样本就属于那种类别!)
优缺点
KNN优点:
- 理论成熟,思想简单,既可以用来做分类也可以用来做回归
- 可用于非线性分类
- 训练时间复杂度比支持向量机之类的算法低,仅为O(n)
- 和朴素贝叶斯之类的算法比,对数据没有假设,准确度高,对异常点不敏感
- 由于KNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属类别的,因此对于类域的交叉或重叠较多的待分样本集来说,KNN方法较其他方法更为适合
- 该算法比较适用于样本容量比较大的类域的自动分类,而那些样本容量较小的类域采用这种算法比较容易产生误分
KNN 缺点
- 计算量大,尤其是特征数非常多的时候
- 样本不平衡的时候,对稀有类别的预测准确率低
- KD树,球树之类的模型建立需要大量的内存
- 使用懒散学习方法,基本上不学习,导致预测时速度比起逻辑回归之类的算法慢
- 相比决策树模型,KNN模型可解释性不强(鸡蛋里挑骨头了)
适用场景
它的适用面很广,并且在样本量足够大的情况下准确度很高。不适用于特征维度比较多的场景(几十维即可)。
参数详解
class sklearn.neighbors.KNeighborsClassifier(n_neighbors=5, *, weights='uniform', algorithm='auto', leaf_size=30, p=2, metric='minkowski', metric_params=None, n_jobs=None, **kwargs)
常用参数:algorithm、n_neighbors、weights、p 等超参数
第一个超参数:algorithm(一般设为auto即可)
algorithm 即建立 kNN 模型时采用什么算法去搜索最近的 k 个点,有四个选项:
- brute(暴力搜索)
- kd_tree(KD树)
- ball_tree(球树)
- auto(默认值,自动选择上面三种速度最快的)
之前建立模型时没有设置这个参数,模型默认使用了 auto 方法,不过还是有必要了解一下这几种方法的区别。
首先,我们知道 KNN 模型的核心思想是计算大量样本点之间的距离。
第一种算法 brute (暴力搜索)。意思就是计算预测样本和全部训练集样本的距离,最后筛选出前 K 个最近的样本,这种方法很蛮力,所以叫暴力搜索。当样本和特征数量很大的时候,每计算一个样本距离就要遍历一遍训练集样本,很费时间效率低下。有什么方法能够做到无需遍历全部训练集就可以快速找到需要的 k 个近邻点呢?这就要用到 KD 树和球树算法。
第二种算法 KD 树(K-dimension tree缩写)。简单来说 KD 树是一种「二叉树」结构,就是把整个空间划分为特定的几个子空间,然后在合适的子空间中去搜索待预测的样本点。采用这种方法不用遍历全部样本就可以快速找到最近的 K 个点,速度比暴力搜索快很多(稍后会用实例对比一下)。至于什么是二叉树,这就涉及到数据结构的知识,稍微有些复杂,就目前来说暂时不用深入了解,sklearn 中可以直接调用 KD 树,很方便。
假设数据集样本数为 m,特征数为 n,则当样本数量 m 大于 2 的 n 次方时,用 KD 树算法搜索效果会比较好。比如适合 1000 个样本且特征数不超过 10 个(2 的 10 次方为 1024)的数据集。一旦特征过多,KD 树的搜索效率就会大幅下降,最终变得和暴力搜索差不多。通常来说 KD 树适用维数不超过 20 维的数据集,超过这个维数可以用球树这种算法。
第三种算法是球树(Ball tree)。对于一些分布不均匀的数据集,KD 树算法搜索效率并不好,为了优化就产生了球树这种算法。同样的,暂时先不用具体深入了解这种算法。
当数据集样本数量 m > 2 的 n 次方时,kd_tree 和 ball_tree 速度比 brute 暴力搜索快了一个量级,auto 采用其中最快的算法。
我们介绍了第一个超参数 algorithm,就 kNN 算法来说通常只需要设置 auto 即可,让模型自动为我们选择合适的算法。
第二个超参数:n_neighbors
n_neighbors 即要选择最近几个点,默认值是 5(等效 k )。
k值过大和过小造成的影响:
k值较小,就相当于用较小的领域中的训练实例进行预测,训练误差近似误差小(偏差小),泛化误差会增大(方差大),换句话说,K值较小就意味着整体模型变得复杂,容易发生过拟合;
k值较大,就相当于用较大领域中的训练实例进行预测,泛化误差小(方差小),但缺点是近似误差大(偏差大),换句话说,K值较大就意味着整体模型变得简单,容易发生欠拟合;一个极端是k等于样本数m,则完全没有分类,此时无论输入实例是什么,都只是简单的预测它属于在训练实例中最多的类,模型过于简单。
对于k值的选择,没有一个固定的经验(sklearn默认为5),一般根据样本的分布,选择一个较小的值,可以通过交叉验证选择一个合适的k值。
第三个超参数:距离权重 weights
刚才在划分黄色点分类时,最近的 3 个点中红绿色点比例是 2:1,所以我们就把黄色点划分为红色点了。
这样做忽略了点与点之间的距离问题:虽然红点数量多于绿点,但显然绿点距离黄点更近,把黄点划分为绿色类可能更合适。因此,之前的模型效果可能并不好。
所以,在建立模型时,还可以从距离出发,将样本权重与距离挂钩,近的点权重大,远的点权重小。怎么考虑权重呢,可以用取倒数这个简单方法实现。
以上图为例,绿点距离是 1,取倒数权重还为 1;两个红点的距离分别是 3 和 4,去倒数相加后红点权重和为 7/12。绿点的权重大于红点,所以黄点属于绿色类。
在 Sklearn 的 kNN 模型中有专门考虑权重的超参数:weights,它有两个选项:
Uniform:不考虑距离权重,默认值
Distance:考虑距离权重
第四个超参数:p
说到距离,还有一个重要的超参数 p。
如果还记得的话,之前的模型在计算距离时,采用的是欧拉距离:
除了欧拉距离,还有一种常用的距离,曼哈顿距离:
这两种距离很好理解,举个例子,从家到公司,欧拉距离就是二者的直线距离,但显然不可能以直线到公司,而只能按着街道线路过去,这就是曼哈顿距离,俗称出租车距离。
这两种格式很像,其实他们有一个更通用的公式:
这就是明可夫斯基距离(Minkowski Distance),曼哈顿距离和欧拉距离分别是 p 为 1 和 2 的特殊值。
使用不同的距离计算公式,点的分类很可能不一样导致模型效果也不一样。
在 Sklearn 中对应这个距离的超参数是 p,默认值是 2,也就是欧拉距离。
KNN用作异常点检测的原理
比较简单的做法是计算预测数据点与最近的K个样本点的距离和,然后根据阈值进行异常样本的判别(如若假设异常样本量占总样本量的1%,那么对数据集中每个数据点得到的距离和进行排序,挑选出Top1%即为异常值)。
K近邻距离有多种设置:
1.距离第k近的点的距离
2.距离最近的k个点的平均距离
3.距离最近的k个点中的中间距离
PS:
这类方法有一种明显的缺点,如果异常样本数据较多,并且单独成簇的话,最后得到的结果则不太乐观。
KNN用于回归的原理
1、计算已知点和未知点(待预测的样本)的距离;
2、将训练集样本数据按照距离升序排序;
3、取其中最近的topK个值;
4、取平均;
5、得到样本的预测值.