KNN算法

K近邻算法(k-nearest neighbor,k-NN)是一种分类与回归的方法。K近邻法的输入为实例,输出为实例的类别,这里的类别可以取多类。分类时,对于新的实例,根据k个最近邻的训练数据的类别,通过多数表决的方法进行预测。K近邻算法实际上是利用训练数据对输入空间进行划分,并作为其分类的模型。

前面我们也有提到,k邻近是一种分类与回归的方法,但是接下来我们要讨论的是分类的这一方面。而对于回归来说,是比较简单的。通过找出一个样本的k个最近的邻居,将这些邻居的属性的平均值赋给该样本,就可以得到该样本的属性。可以将不同距离的邻居对该样本产生的影响赋予不同的权值,常见的做法就是取距离的倒数。

算法过程

输入:训练数据集T={(x1,y1),(x2,y2),…,(xN,yN)},其中,xi∈Rn,yi∈Y={c1,c2,…,ck}为输入数据的类别,i=1,2,…,N。实例x。

输出:实例x所属的类y。

(1)根据给定的距离度量,在训练集T中找出与x最邻近的k个点,涵盖这k个点的x邻域记作Nk(x);

(2)在Nk(x)中根据分类决策规则决定x的类别y:


y=argmax∑xi∈Nk(x)I(yi=cj),i=1,2,…,N;j=1,2,…,k


上式中,

I为指示函数,当yi=cj时 I为1,否则为0。

当k=1时,即意味着将训练数据集中与x最近邻的点的类作为x的类别,称为最近邻算法。

算法的关键问题

在该算法中,主要有三个关键问题:距离度量、k值的选择以及分类决策规则。下面我们将分为这三个方面进行讨论。

K值的选择

K值选择的好坏会对模型的性能产生重大的影响。如果选择较小的k值,就相当于用较小的邻域中的训练实例进行预测。那么,只有与输入实例接近的训练实例才会对预测结果起作用。学习的近似误差(这里可以理解为训练误差)会减小,而估计误差(这里可以理解为测试误差)会增大。因为在这种情况下,预测结果会对近邻的实例点非常敏感。如果近邻范围内的点是噪声数据的话,预测结果就会出问题。K值得减小意味着模型整体上变得更为复杂,容易发生过拟合。如果选择过大的k值,那么情况则刚好相反。

在实际应用中,一般是使用交叉验证法来选择一个合适的k值。

分类决策规则

该规则用于如何根据Nk(x)中数据的标签来对输入的实例的标签进行判断。一般情况下使用的是多数表决规则,即由输入实例的k个近邻的多数类决定输入实例的类。多数表决规则等价于经验风险最小化。

还可以使用距离作为权值来对类别进行加权,从而得到输入实例的类别。

算法的实现

为了提高k近邻搜索的效率,可以考虑使用特殊的结构存储训练数据,以减少计算距离的次数。下面介绍其中的kd树方法。

kd树是二叉树,表示对k维空间的一个划分。构造kd树相当于不断用垂直于坐标轴的超平面将k维空间切分,构成一系列的k维超矩形区域。kd树的每个结点对应于一个k维超矩形区域。选择训练实例点在选定坐标轴上的中位数为切分点,这样得到的kd树是平衡的。注意平衡的kd树搜索时的效率未必是最优的。当然在选择时,同样可以选择具有最大方差的维度,意味着该维度具有较好的区分性。

构造平衡kd树

输入:k维空间数据集T={x1,x2,...,xN},

其中xi=(x(1)i,x(2)i,...,x(k)i)T,i=1,2,...,N;

输出:kd树

(1)开始:构造根结点,根结点对应于包含T的k维空间的超矩形区域。

选择x(1)为坐标轴,以T中所有实例的x(1)坐标的中位数为切分点,将根结点对应的超矩形区域切分为两个子区域。切分由通过切分点与坐标轴x(1)垂直的超平面实现。由根结点生成深度为1的左、右子结点:左子结点对应坐标x(1)小于切分点的子区域,右子结点对应于坐标x(1)大于切分点的子区域。

将落在切分超平面上的实例点保存在根结点。

(2)重复:对深度为j的结点,选择x(l)为切分的坐标轴,l=j(modk)+1,以该结点的区域中所有实例的x(l)坐标的中位数为切分点,将该结点对应的超矩形区域切分为两个子区域。切分由通过切分点并与坐标轴x(l)垂直的超平面实现。

下面举一个实例来表征该过程。给定一个二维空间的数据集:T={(2,3)T,(5,4)T,(9,6)T,(4,7)T,(8,1)T,(7,2)T}构造一个平衡kd树。

根结点对应包含数据集T的矩形,选择x(1)轴,6个数据点的x(1)坐标的中位数是7,以平面x(1)=7将空间分为左、右两个子矩形;接着,左矩形以x(2)=4分为两个子矩形,右矩形以x(2)=6分为两个子矩形,如此递归,最后得到的特征空间划分和kd树如下图所示:

搜索kd树

输入:已构造的kd树;目标点x;

输出:x的最近邻。

(1)在kd树中找出包含目标点x的叶结点:从根结点出发,递归地向下访问kd树。若目标点x当前维度的坐标小于切分点的坐标,则移动到左子结点,否则移动到右子结点。直到子结点为叶结点为止。

(2)以此叶结点为『当前最近点』。

(3)递归地向上回退,在每个结点进行以下操作:

(a)如果该结点保存的实例点比当前最近点距离目标点更近,则以该实例点为『当前最近点』。

(b)当前最近点一定存在于该结点一个子结点对应的区域。检查该子结点的父结点的另一子结点对应的区域是否有更近的点。具体地,检查另一子结点对应的区域是否与以目标点为球心,以目标点与『当前最近点』间的距离为半径的超球体相交。

如果相交,可能在另一个子结点对应的区域内存在距目标点更近的点,移动到另一个子结点。接着,递归地进行最邻近搜索;

如果不相交,向上回退。

(4)当回退到根结点时,搜索结束。最后的『当前最近点』即为x的最邻近点。

如果实例点是随机分布的,kd树搜索的平均计算复杂度是O(logN),这里N是训练实例数。kd树更适合用于训练实例数远大于空间维数时的最近邻搜索。当空间维数接近训练实例数时,它的搜索效率会迅速下降,几乎接近线性扫描。当输入的维数大于10时,kd树的效率不比线性搜索高。还有一种叫做Best-Bin-First(BBF)的方法,也可以提高搜索中心点的效率。

结合一个具体的例子来说明搜索一个kd树的具体过程。在之前构造出的kd树中搜索一个点S。

首先在kd树中找到包含点S的叶结点D,以点D作为最近邻。真正的最近邻一定在以点S为中心,通过点D的圆的内部。然后返回结点D的父结点B,在结点B的另一子结点F的区域内搜索最邻近。结点F的区域与圆不相交,不可能有最近邻点。继续返回上一级父结点A,在结点A的另一子结点C的区域内搜索最邻近。结点C的区域与圆相交;该区域在圆内的实例点有点E,点E比点D更近,成为新的最近邻近似。最后得到点E是点S的最近邻。

算法的优缺点

该算法主要的优点就在于简单、直观,且并不具有显式的学习过程。该算法在分类时有个主要的不足是,当样本不平衡时,如果一个类的样本容量很大,而其他样本容量很小时,有可能导致当输入一个新样本时,该样本的k个邻居中大容量的样本占多数,但实际上该实例并不属于这个类。

写在最后

以上内容部分参考《统计学习方法》一书,以及july的博客。一些内容源于电子科技大学计算智能实验室机器学习小组的讨论结果。