其他机器学习系列文章见于专题:机器学习进阶之路——学习笔记整理,欢迎大家关注。
k近邻法(K-Nearest neighbor,kNN)是一种常用的监督学习方法,其工作机制为:给定测试样本,基于某种距离度量找出训练集中与其最靠近的k个训练样本,然后基于这k个“邻居”的信息来进行预测。通常,在分类任务中使用投票法计算最终预测结果,在回归任务中使用平均法,还可基于距离远近进行加权平均或加权投票。
kNN是懒惰学习(lazy learning)的典型代表,不具有显式的学习过程。懒惰学习技术在训练阶段仅仅将样本保存起来,训练开销为0,等收到测试样本时再进行处理,相应的,那些在训练阶段就对样本进行学习处理的方法,称为“急切学习(eager learning)”。
kNN使用的模型实际上对应于对特征空间的划分。kNN模型由三个及基本要素组成:
kNN中使用的距离度量可以是欧式距离、曼哈顿距离、切比雪夫距离或者一般的闵可夫斯基距离。具体定义可参见上一篇博客【机器学习】LP距离、欧氏距离、曼哈顿距离、切比雪夫距离。
如果k值较小,则训练误差减少,只有与输入实例相似的训练实例才会对于预测结果起作用,但泛化误差提高了,预测结果会对近邻实例点非常敏感。k值较小意味着模型变得复杂,容易发生过拟合。
如果k值较大,可以减少泛化误差,但训练误差会增加,这时与输入实例相差较远的训练实例也会对预测结果起作用。k值较大意味着模型变得简单,容易发生欠拟合。
当 k = 1 k=1 k=1时,k近邻算法就是最近邻算法。k值一般采用交叉验证法选取最优值。
通常,在分类任务中使用投票法计算最终预测结果,在回归任务中使用平均法,还可基于距离远近进行加权平均或加权投票。
下面以分类任务为例,介绍KNN算法,回归任务与此类似,区别不大。
输入:训练数据集 D = { ( x i , y i ) } i = 1 m D = \left\{ \left( x _ { i } , y _ { i } \right) \right\} _ { i = 1 } ^ { m } D={(xi,yi)}i=1m,其中, x i ∈ X ⊆ R n x _ { i } \in \mathcal { X } \subseteq \mathbf { R } ^ { n } xi∈X⊆Rn, y i ∈ Y = { c 1 , c 2 , ⋯   , c K } y _ { i } \in \mathcal { Y } = \left\{ c _ { 1 } , c _ { 2 } , \cdots , c _ { K } \right\} yi∈Y={c1,c2,⋯,cK}是实例的类别。
过程:
(1)根据给定的距离度量,在训练集 D D D中找出与 x x x最邻近的 k k k个点,涵盖着 k k k个点的领域记为 N k ( x ) N _ { k } ( x ) Nk(x);
(2)在 N k ( x ) N _ { k } ( x ) Nk(x)中根据分类决策规则决定 x x x的类别 y y y:
y = arg max c j ∑ x i ∈ N k ( x ) I ( y i = c j ) , i = 1 , 2 , ⋯   , N ; j = 1 , 2 , ⋯   , K y = \arg \max _ { c _ { j } } \sum _ { x _ { i } \in N _ { k } ( x ) } I \left( y _ { i } = c _ { j } \right) , \quad i = 1,2 , \cdots , N ; \quad j = 1,2 , \cdots , K y=argcjmaxxi∈Nk(x)∑I(yi=cj),i=1,2,⋯,N;j=1,2,⋯,K
输出:测试样本 x x x所属的类别 y y y。
KNN最简单的实现方法是线性扫描,这是一种暴力实现方法,然而当训练集很大时,搜索k个最近邻居的计算非常耗时。
在实现kNN时,主要考虑的问题就是如何对训练数据进行快速k近邻搜索。这样的方法有kd树和球树,本文只讨论kd树,如对球树感兴趣,可参见参考文献2。
kd树使用了特殊的结构存储训练数据,减少计算目标值与训练实例的距离的次数。
kd树算法包括两个主要步骤:
下面介绍kd树算法的原理,可结合参考文献3的案例来理解。
kd树是一种对k维空间中的实例点进行存储,以便对其进行快速检索的二叉树结构。构造kd树相当于不断地用垂直于坐标轴的超平面将k维空间切分,构成一系列的k维超矩形区域,kd树的每个结点对应一个k维超矩形区域。如图1所示:
构造平衡kd树的算法如下:
输入: k k k维空间训练数据集 D = { x 1 , x 2 , ⋯   , x m } D= \left\{ x _ { 1 } , x _ { 2 } , \cdots , x _ { m } \right\} D={x1,x2,⋯,xm},其中, x i = ( x i ( 1 ) , x i ( 2 ) , ⋯   , x i ( k ) ) T , i = 1 , 2 , ⋯   , m x _ { i } = \left( x _ { i } ^ { ( 1 ) } , x _ { i } ^ { ( 2 ) } , \cdots , x _ { i } ^ { ( k ) } \right) ^ { \mathrm { T } } , \quad i = 1,2 , \cdots , m xi=(xi(1),xi(2),⋯,xi(k))T,i=1,2,⋯,m;
过程:
(1)构建根结点,根结点对应于包含所有实例点的k维空间的超矩形区域,并置根结点的 l = 1 l=1 l=1;
(2)选择 x ( l ) x^{(l)} x(l)为坐标轴,以 D D D中所有实例的 x ( l ) x^{(l)} x(l)坐标的中位数为切分点,得到通过切分点并与坐标轴 x ( l ) x^{(l)} x(l)垂直的超平面,将该结点对应的超矩形区域划分为两个子区域,对应生成左右子结点,左子结点对应坐标 x ( l ) x^{(l)} x(l)小于切分点的区域,右子结点对应坐标 x ( l ) x^{(l)} x(l)大于切分点的区域,并将落在切分超平面上的实例点保存在该结点;
(3)对深度为 j j j的节点,令 l = j ( m o d k ) + 1 l=j(mod k)+1 l=j(modk)+1,即依次取特征作为切分坐标轴。重复(2)(3),直到两个子区域没有实例存在时停止(这意味着最后所有训练实例都对应一个叶结点或内部结点),从而形成kd树的区域划分。
输出:kd树。
标准kNN算法的切分特征选择是按顺序的,后来对kd树的一个重大改进是选择方差最大的特征,方差越大,不同实例点区分越明显,更方便进行划分。
给定一个目标点,首先找到包含目标点的叶结点,该叶结点对应包含目标点的最小超矩形区域。以此叶结点的实例点作为当前最近点,以目标点为中心,并通过当前最近点,可以得到一个超球体,目标点的最近邻一定是在该超球体内部。然后返回当前结点的父结点,如果父结点的另一子结点的超矩形区域与超球体相交,那么在相交的区域内寻找与目标点更近的实例点,如果存在这样的点,将此点作为新的当前最近点;否则不查找另一子结点的超矩形区域。之后,算法转到更上一级的父结点,继续上述过程。如图2所示:
kd树最近邻搜索算法如下:
输入:已构造的kd树;目标点 x x x;
过程:
(1)在kd树中找到包含目标点 x x x的叶结点:从根结点出发,递归地向下访问kd树,直到叶结点为止;
(2)以此叶结点为“当前最近点”,把目标点与此叶结点的实例之间的距离记为“当前最近距离”;
(3)递归地向上回退,对每个父结点:
(a)如果该父结点保存的实例点与目标点的距离比当前最近距离更近,则以该实例点为“当前最近点”,以该距离为“当前最近距离”;
(b)检查该父结点的另一子结点对应的区域是否与以目标点为球心、以“当前最近距离”为半径的超球体相交。如果相交,则递归地对该父结点的另一子结点进行最近邻搜索;如果不相交,则向上回退。
(4)当回退到根结点时,搜索结束。最后的“当前最近点”就是 x x x的最近邻点。
该算法针对最近邻,k近邻在搜索最近邻的基础上,忽略之前找到的最近邻实例,重新选择最近邻,重复k次,得到目标点的k个最近邻。
优点:
1. 结构简单;
2. 无数据输入假定,准确度高,对异常点不敏感。
缺点:
1. 计算复杂度高、空间复杂度高;
2. 样本不平衡时,对稀有类别预测准确度低;
3. 使用懒惰学习,预测速度慢。
参考文献:
- 《统计学习方法》第三章k近邻法——李航
- K近邻法(KNN)原理小结
- KD树