k 近邻法(K-Nearest Neighbor)
文章目录
k 近邻法简单的理解就是输入一个实例,在训练集中寻找与其相邻最近的 k 个实例,这 k 个实例中多数属于哪个类,则该输入的实例也属于这个类。当 k = 1 时,称为最近邻法。
k 近邻法没有显式的学习过程,只是将输入的全部实例点划分到不同的区域,是惰性学习(Lazy Learning )。在特征空间中,某些点距离某个实例点 x i x_i xi 的距离,比距离其他实例点之间的距离更小,这些点组成一个区域,称为单元,实例 x i x_i xi 的类 y i y_i yi 作为该单元所有点的类标记。
1. 三要素
1.1 距离度量
在 n 维特征空间中,不同实例点之间的距离反映了相似程度。
L p L_p Lp 距离: L p ( x i , x j ) = ( ∑ l = 1 n ∣ x i ( l ) − x j ( l ) ∣ p ) ( 1 p ) L_p(x_i,x_j)=(\sum_{l=1}^{n}|x_i^{(l)}-x_j^{(l)}|^p)^{(\frac{1}{p})} Lp(xi,xj)=(∑l=1n∣xi(l)−xj(l)∣p)(p1)
p = 1 p=1 p=1时即为 曼哈顿距离 : L 1 ( x i , x j ) = ∑ l = 1 n ∣ x i ( l ) − x j ( l ) ∣ L_1(x_i,x_j)=\sum_{l=1}^{n}|x_i^{(l)}-x_j^{(l)}| L1(xi,xj)=∑l=1n∣xi(l)−xj(l)∣
p = 2 p=2 p=2时即为 欧氏距离 : L 2 ( x i , x j ) = ( ∑ l = 1 n ∣ x i ( l ) − x j ( l ) ∣ 2 ) 1 2 L_2(x_i,x_j)=(\sum_{l=1}^{n}|x_i^{(l)}-x_j^{(l)}|^2)^{\frac{1}{2}} L2(xi,xj)=(∑l=1n∣xi(l)−xj(l)∣2)21
p = + ∞ p=+\infty p=+∞时,为 n 个距离中最大的那个值: L + ∞ = m a x l = ∣ x i ( l ) − x j ( l ) ∣ L_{+\infty}=max{\atop{l}}=|x_i^{(l)}-x_j^{(l)}| L+∞=maxl=∣xi(l)−xj(l)∣yi
一般使用欧氏距离。
1.2 k 值选择
k 是模型中唯一的超参数,要选择恰当的值,不能过大,也不能过小。
超参数是学习之前设置好的参数,不是通过训练过程得到的。一般需要优化超参数,选择一组最合适的超参数,以提高学习效果。
若 k 值过小,会对噪点过于敏感,且模型更加复杂,容易过拟合。若 k 值过大,则违背了“近邻”这一原则,远处与输入的实例点无关的实例也会影响结果,模型鲁棒性比较好,但会导致欠拟合。
通常情况下,k 值可以由工程经验得到,也可由 k 折交叉验证(这里的 k 与 k 近邻法的 k 无关)得到最优的 k 值。
一般取 5 折或者10 折,这里假设 k 为10,每次把训练集划分为10份,9份作为训练集,1份作为验证集(是验证集不是测试集,测试集不能用来更新模型和调整超参数),对 10 次验证的结果取均值。由于数据集的划分会影响训练结果,所以需要设置随机种子。
先给 k 一个比较小的值,然后逐渐增大 k 值,当执行完 n 次 k 折交叉验证后,最终在 n 个 k 值里选一个最优的。
1.3 决策规则
分类问题:“投票法”。以 k 个近邻点中占多数的类别为准。
回归问题:求平均。对 k 个近邻点的 y 求均值。
2. kd 树
每输入一个实例点就计算与特征空间里的所有点之间的距离,当训练实例很多时,这种方法的效率是极低的。kd 树的作用就是不用计算与所有的点之间的距离,实现快速 k 近邻搜索。
kd 树相当于二叉树,只不过是在 n 维空间里进行划分。
2.1 构造 kd 树
构造 kd 树就是不断用垂直于坐标轴的超平面将 k 维空间切分,构成了一系列 k 维超矩形区域,kd 树的每个结点对应于一个 k 维超矩形区域。
- 构造 kd 树首先要寻找根结点:有 n 个实例,每个实例有 m 个特征,即 n 个 m 维实例点。对每个特征的 n 个值求方差,以方差最大的那个特征所在的维度当作坐标轴,对此维度上的 n 个数排序,取中位数作为切分点,划分为两个深度为 1 子区域,根节点为中位数所在的实例点。左边实例在该维度上的值小于切分点,右边的实例点在该维度上的值大于切分点。
- 重复:对于深度为 j j j 的结点,取第 l l l 维特征作为坐标轴,寻找中位点进行切分, l = j ( m o d m ) + 1 l = j(mod \quad m)+1 l=j(modm)+1。
- 停止:直到左右两子区域没有实例时停止。
2.2 搜索 kd 树
搜索 kd 树主要分为两个环节:寻找当前最近点和回溯。
1. 寻找当前最近点
从根结点出发,递归向下访问,若目标点 x x x 所在维度的坐标小于切分点,则移向左结点,否则移向右结点。直到子结点为叶结点为止,此叶结点即为当前最近邻点。
2. 回溯
- 创建一个容器用于保存 k 个最近邻点**,**以当前最近邻点和目标点的连线作一个超球面,从当前最近邻点出发,回退到父节点。
- 若超球面和父结点的超平面不相交(此时兄弟结点和父结点与目标点的距离都不可能比当前最近邻点更近),则不讨论兄弟结点(这就是 kd 树快的原因),直接返回上一级父结点。
- 若超球面和父结点的超平面相交,需要计算目标结点与兄弟结点的距离,若与兄弟结点的距离小于当前最近距离,那么以兄弟结点作为当前最近邻点,以当前最近距离更新超球面,然后还要判断当前父结点距离目标点的距离。
- 回溯的过程中不断重复以上判断。
- 退回到根结点后停止搜索。
上面的搜索过程只是搜索最近邻点,若要搜索 k 近邻点只需以下改动:
- 若容器内保存的实例点数目小于 k,则不管距离远近都把实例点存入容器。
- 直到当容器存满后,把容器内距离目标点最远的点作为当前最近邻点,和沿途的点进行比较,若沿途有距离目标点更近的点,则把容器内距离目标点最远的点替换掉。