机器学习——最邻近算法 Nearest neighbour method / K邻近算法(KNN)
最邻近算法 / K邻近算法 / KNN
-
找到离「当前点」最近的「K个数据点」,然后根据「少数服从多数」原则,对「当前点」进行分类。
-
如果K取值太小,可能导致过度拟合。即,如果邻近样本是「噪声」,则会对训练结果造成影响——训练结果在训练集中表现变好,但在测试集中表现变差——近似误差减少,估计误差增大
-
如果K值取值太大,将导致欠拟合。即,远处「不相似的数据」对训练结果产生影响——近似误差增大,估计误差减小
-
-
因为要计算所有点的距离,如果采用「欧氏距离」,则时间复杂度为
-
根据排列组合公式,从N个点中挑出2个进行组合:共要计次,所以时间复杂度为
-
K Dimensional Tree / KD Tree / KD树
Reference
k-d tree算法原理及实现
从K近邻算法、距离度量谈到KD树、SIFT+BBF算法
构建KD树
- 以轴作为分隔超平面的法向量,数据集的中位点作为根节点。于是根据根节点轴的值,能将集合左右两个子集,即左子树和右子树。
- 以轴作为分隔超平面的法向量,找到每个子集的中位点,则该中位点,分别作为根节点的左子树节点和右子树节点。这次根据左右子树节点的轴将剩余集合进行划分
- 中位点,相当于中位数的概念,但是在KD树中,所有节点必须是存在的数据点,因此这里用中位点表示
- 重复以上步骤:循环的选取坐标轴,作为分割超平面的法向量。找到集合中的中位点,将集合拆分,直到无法继续拆分为止。
详细说明:
- 对于一个由n维数据构成的数据集,我们首先寻找方差最大的那个维度,设这个维度是,然后找出在维度上所有数据项的中位数,按划分数据集,一分为二,记这两个数据子集为,。建立树节点,存储这次划分的情况(记录划分的维度以及中位数);
- 对,重复进行以上的划分,并且将新生成的树节点设置为上一次划分的左右孩子;
- 递归地进行以上两步,直到不能再划分为止(所谓不能划分是说当前节点中包含的数据项的数量小于了我们事先规定的阈值,不失一般性,我在此篇博客中默认这个阈值是2,也就是说所有叶子节点包含的数据项不会多于2条),不能再划分时,将对应的数据保存至最后的节点中,这些最后的节点也就是叶子节点。
——Kd-tree原理与实现
搜索KD树
现有查询点,目的是在KD树中找到点的最近邻点
-
向下搜索:首先从根节点开始查找,根据当前节点对应的维度,判断点属于根节点的左叶子还是右叶子,并将其加入查询路径。依次往下查找,直到搜索到最后一个节点,此时得到查询路径<根节点,中位点,中位点,最后叶子节点>,此时查询点与末尾节点处于同一个区域。
- 虽然是同一个区域,但不一定是距离最短的,最邻近肯定距离查询点更近,应该位于以查询点为圆心且通过叶子节点的圆域内
-
向上回溯:为了确定查询点的最邻近点,需要按照查询路径<根节点,中位点,中位点,最后叶子节点>,计算查询点与各点的距离,直到回溯到根节点
- 如果点距离叶子节点D的距离比较近,离上一个中位点比较远,则可以继续按照查询路径向上回溯,计算下一个中位点
- 如果点距离路径上的中位点比较近,则需要计算的距离和到中位点所在超平面的距离,如果,则说明在中位点C的另一侧还可能存在更近的点,因此需要重复【步骤1】,前往另一侧进行向下查询(即以两点的距离为半径画圆,看是否与超平面相交)
-
回溯到根节点后结束搜索,将记录的最邻近点取出
- 目前有如下图所示的KD树(2维KD树)
- 目前有如下图所示的KD树(2维KD树)
- 同样先进行二叉查找,先从(7,2)查找到(5,4)节点,在进行查找时是由y = 4为分割超平面的,由于查找点为y值为4.5,因此进入右子空间查找到(4,7),形成搜索路径<(7,2),(5,4),(4,7)>,但(4,7)与目标查找点的距离为3.202,而(5,4)与查找点之间的距离为3.041,所以(5,4)为查询点的最近点;
- 以(2,4.5)为圆心,以3.041为半径作圆,如下图所示。可见该圆和y = 4超平面交割,所以需要进入(5,4)左子空间进行查找,也就是将(2,3)节点加入搜索路径中得<(7,2),(2,3)>;于是接着搜索至(2,3)叶子节点,(2,3)距离(2,4.5)比(5,4)要近,所以最近邻点更新为(2,3),最近距离更新为1.5;
- 回溯查找至(5,4),直到最后回溯到根结点(7,2)的时候,以(2,4.5)为圆心1.5为半径作圆,并不和x = 7分割超平面交割,如下图所示。至此,搜索路径回溯完,返回最近邻点(2,3),最近距离1.5。