1、k近邻
k近邻算法是懒惰学习的代表算法。之所以说k近邻懒惰,是因为它没有显示的训练过程。它的思路很简单,手头有一些带标签的样本,现在给定一个新的样本,找出已有样本中距离新样本最近的k个样本点,然后取这k个样本点标签的投票结果。
k近邻算法本身并不复杂,但有几个细节需要注意:
(1)距离度量
不同的距离度量可能产生不同的k近邻,从而直接影响预测结果。至于度量的选取要结合应用场景,关键在于我们需要知道在特定场景中如何量化两样本之间的相似度。
(2)k值选择
k值的选择没有经验性的方法,一般只能通过交叉验证来选取合适的k值。
如果k值选择较小,就相当于用较小的邻域中的训练实例进行预测,“学习”的近似误差会减小,只有与输入实例较近的训练实例才会对预测起作用,但估计误差会增大,预测结果会对近邻的实例点非常敏感,如果近邻的实例点恰巧是噪声,预测就会出错。相反如果k值选择较大,就相当于用较大的领域中的训练实例进行预测,近似误差会增大,但估计误差会减小。
对于k近邻法来说,k越大模型越简单,这一点乍一看不容易理解,但其实我们可以考虑极端情况k=N(N为样本数),此时任何新样本都会被归入当前样本集中实例最多的类别,从而丢失大量信息。反之,k越小模型越复杂,模型将面临过拟合风险。
(3)投票法
k近邻使用多数表决的投票法看起来十分自然,但我们也可以从最小化经验误差的角度来理解,如果分类的损失函数为0-1损失函数,则误分类概率为:
对于给定实例,其最近邻的k个训练实例构成集合
,若涵盖
的区域的类别是
,则误分类概率为:
要使误分类率最小即经验风险最小,就要使最大,这正是投票法(多数表决)所做的事情。
2、kd树
kd树是一种对k维空间中的实例点进行储存以便对其进行快速检索的树形数据结构。kd树是二叉树,表示对k维空间的一个划分。构造kd树相当于不断用垂直于坐标轴的超平面将k维空间切分,构成一系列k维超矩形区域。
构造平衡kd树的流程如下:
输入:k维空间数据集
(1)构造根结点,根结点对应于包含的k维空间的超矩形区域。
(2)对深度为的结点,选择
为切分的坐标轴(
),以该结点区域中所有实例的
坐标的中位数为切分点,将该结点对应的超矩形区域划分为两个子区域,切分由通过切分点并与坐标轴
垂直的超平面实现。
(3)重复(2)直至划分得到的两个子区域中没有实例存在为止。
用kd树进行最近邻搜索的流程如下:
输入:构造好的kd树,目标节点x
(1)在kd树中找到包含目标节点的叶结点:从根结点出发,递归地向下访问kd树。若目标
当前维的坐标小于切分点坐标则移动到左子结点,否则移动到右子结点,直至子结点为叶结点为止。
(2)以此叶结点为“当前最近点”。
(3)递归地向上回退:
- 若该结点保存的实例点比当前最近点距离目标更近,则以该点为“当前最近点”。
- 当前最近点一定存在于该结点的一个子结点对应的区域。检查该子结点的父节点的另一个子结点对应的区域是否有更近的点,即检查另一子结点对应的区域是否与以目标节点为球心,以目标点到“当前最近点”的距离为半径的超球体相交。若相交,可能在另一个子结点对应区域中存在距离目标更近的点,移动到另一个子结点,继续递归地进行搜索。若不相交,向上回退。
(4)当回退到根结点,搜索结束,此时的“当前最近点”即为的最近邻点。
以上就是利用kd树进行最近邻搜索的过程,同样的方法可以推广到k近邻。现在我们来思考一下这个算法的本质。
其实我个人理解的是,kd树的构造就是二分法在k维的应用。但是不太相同的是,kd树算法并不是选定一个轴后不断二分直至结束,而是做一次二分换一个轴,这是因为如果我们只选定一个轴做二分得到的结果并不能反映各样本之间距离的远近,而兼顾各个轴则能一定程度上度量样本之间的距离。
我们可以想象3维的情况,kd树最终会形成一系列的小正方体,我们要想找距离一个新样本点最近的样本点,只需要找到新样本点所在的小正方体,然后check这个小正方体中的样本以及和这个小正方体相邻的小正方体中的样本哪个距离新样本最近即可(相邻这个概念是很重要的,为便于理解,考虑在一维的情况下,此过程为找到新样本所在区间,然后检查此区间以及左右相邻区间中的样本点即可)。而相邻这个概念刚好和kd树的构造过程相符,我们不断回退的过程其实就是检查各个不同方向上的相邻超矩形的过程。这个过程十分高效,不难看出,平衡kd树寻找最近邻的复杂度是。