k近邻法(k-NN)
k近邻法并没有显式的训练过程,对于某个新的实例,根据其k个最近邻的训练实例的类别,通过多数表决等方式进行预测。换句话说,其本质就是利用训练数据集对特征向量空间进行划分。
k近邻法有三个基本要素:k值的选择、距离度量、分类决策规则。
k值的选择
k值过小,则模型更复杂,容易过拟合;k值过大,则模型过于简单,几乎无法使用。比如k=1,那么我们将预测新的实例点的类别为与其距离最近的点的类别,这样做将导致模型学得噪声信息,如果距离新的实例点最近的点恰好为噪声点,那么预测就出错了;k=N,则不论输入实例是什么,都会预测该实例属于训练集中出现最多的类别。
我们通常采用交叉验证法来选取最优的k值。
距离度量
闵可夫斯基距离(Minkowski distance)定义为:
当p=2时,称为欧式距离,当p=1时,称为曼哈顿距离,当p= ∞ ∞ 时,则为各个坐标距离的最大值。
下图给出在二维空间中p取不同值时,与原点的距离 Lp L p =1的点的图形:
分类决策规则
通常我们使用多数表决规则作为k近邻的决策规则。
对于某个给定的实例
x∈
x
∈
X
,其最近邻的k个训练实例点构成集合
Nk(x)
N
k
(
x
)
。如果涵盖
Nk(x)
N
k
(
x
)
的区域类别为
cj
c
j
(也即实例
x
x
的类别),则误分类率为:
要使误分类率最小即经验风险最小,则等价于使得 ∑xi∈Nk(x)I(yi=cj) ∑ x i ∈ N k ( x ) I ( y i = c j ) 最大,所以多数表决规则等价于经验风险最小化。
kd树
如果现在要实现k近邻法,那我首先想到的方法就是计算给定实例与当前数据集中的各个实例的距离,然后取距离最小的k个实例中类别最多的类作为当前实例的类别,可是这样做的话是很耗时的,因此就有了kd树方法,这是一种对训练数据进行快速k近邻搜索的算法。kd树算法在特征空间维数大、训练数据容量大时很有效果,其平均计算复杂度是
Olog(N)
O
l
o
g
(
N
)
它更适用于训练实例数远大于空间维数的情形,如果空间维数接近训练实例数时,它的效率就会迅速下降,几乎接近于线性扫描。
kd树方法主要包括两个步骤:1)建树;2)搜索。
构造平衡kd树
kd树是一种对k维空间中的实例点进行存储以便对其进行快速检索的树形数据结构。它是二叉树,树中的每一个结点对应于一个k维的超矩形区域,每一次分支表示对于k维空间的一次划分。
给定一组点,为了对它们所在的空间进行切分,首先我们要选取一个切分点,这个点的选择与选定的坐标轴有关。一般来说,我们选择方差最大的轴作为切分轴,然后选择在这个轴上的中位数结点作为切分点,过该点引切分轴的垂线即可得到对于这块区域的一次切分。按照这种方法构造的树是平衡的,但是平衡树的搜索效率未必是最优的。
举个例子对kd树的构建进行说明,给定一个二维空间的数据集:
我们先计算得到数据沿着 x(1) x ( 1 ) 轴和 x(2) x ( 2 ) 轴方向的方差, var(x(1))=6.96 v a r ( x ( 1 ) ) = 6.96 , var(x(2))=5.36 v a r ( x ( 2 ) ) = 5.36 ,所以我们选择 x(1) x ( 1 ) 轴作为切分轴,并选择 x(1) x ( 1 ) 轴上中位数5或7的结点 (5,4) ( 5 , 4 ) 或 (7,2) ( 7 , 2 ) 作为切分点,切分完成之后,将得到左右两个子矩形(子结点)。接着对左矩形和右矩形迭代执行上述过程即可。
我们得到的kd树如下:
搜索kd树
假设我们要找结点A
(2.1,3.1)
(
2.1
,
3.1
)
的最近邻,那么我们先从根结点开始,递归地向下访问二叉树,在根结点
(7,2)
(
7
,
2
)
处,我们选择的划分轴为
x(1)
x
(
1
)
,所以我们判断结点A的
x(1)=2.1
x
(
1
)
=
2.1
要小于根结点的
x(1)=7
x
(
1
)
=
7
,因此向左子树进行递归;此时结点
(5,4)
(
5
,
4
)
代表的矩形区域是沿着
x(2)
x
(
2
)
轴进行划分的,因此比较两个点在
x(2)
x
(
2
)
轴上的值,
3.1<4
3.1
<
4
,继续向左进行递归;遇到叶子结点
(2,3)
(
2
,
3
)
,标记
(2,3)
(
2
,
3
)
为A点的“当前最近点”。注意这时候叫做“当前最近点”,因为它并不一定就是A点的最近邻,我们还得对它的身份进行确认。
上面的过程向下递归完成之后,我们向上进行回退,对经过的结点进行操作:如果该结点保存的实例点比当前最近点距离目标点更近,则以该实例点为“当前最近点”。同时我们检查该结点的父结点的另一子结点对应的区域是否有更近的点,换句话说,检测另一子结点对应的区域是否与以目标点为球心、以目标点与“当前最近点”的距离为半径的超球体相交。如果有,则可能在另一个子结点对应的区域内存在距离目标点更近的点,移动到另一个子结点,并递归进行最近邻搜索;如果不相交,则向上回退。回退到根结点,搜索结束。“当前最近点”即为最近邻点。
上面的例子中,我们并不用进入另一个结点进行最近邻搜索(因为圆并不与另一个结点对应的区域相交),所以一直回退到根节点,返回最近邻
(2,3)
(
2
,
3
)
。
大家可以自行尝试对于点
(2,4.5)
(
2
,
4.5
)
的查找,会比较有趣,具体请参考kd-tree。