文章目录
前言
k k k近邻法是一种基本的分类方法。 k k k近邻法的输入为实例的特征向量,对应于特征空间的点;输出为实例的类别。 k k k近邻法实际上利用训练数据集对特征向量空间进行划分,并作为其分类的“模型”,所以说不具有显示的学习过程。 k k k值得选择、距离度量及分类决策规则是 k k k近邻法得三个基本要素。
一、 k k k近邻算法
输入:训练数据集
T
=
{
(
x
1
,
y
1
)
,
(
x
2
,
y
2
)
,
⋯
,
(
x
N
,
y
N
)
}
T =\{(x_1,y_1),(x_2,y_2),\cdots,(x_N,y_N)\}
T={(x1,y1),(x2,y2),⋯,(xN,yN)}
其中,
x
i
∈
χ
⊆
R
n
x_i\in \chi \sube \bm{R}^n
xi∈χ⊆Rn为实例的特征向量,
y
i
∈
Y
=
{
c
1
,
c
2
,
⋯
,
c
K
}
y_i\in Y= \{c_1,c_2,\cdots, c_K\}
yi∈Y={c1,c2,⋯,cK}为实例的类别,
i
=
1
,
2
,
⋯
,
N
i=1,2,\cdots,N
i=1,2,⋯,N。
输出:实例
x
x
x所属的类
y
y
y。
- 根据给定的距离度量,在训练集 T T T中找出与 x x x最邻近的 k k k个点,涵盖这 k k k个点的 x x x的邻域记作 N k ( x ) N_k(x) Nk(x);
- 在
N
k
(
x
)
N_k(x)
Nk(x)中根据分类决策规则(如多数表决)决定
x
x
x的类别
y
y
y:
y = arg max c j ∑ x i ∈ N k ( x ) I ( y i = c j ) , i = 1 , 2 , ⋯ , N , j = 1 , 2 , ⋯ , K y=\text{arg}\max_{c_j}\sum_{x_i\in N_k(x)}I(y_i=c_j),i=1,2,\cdots,N,j=1,2,\cdots,K y=argcjmaxxi∈Nk(x)∑I(yi=cj),i=1,2,⋯,N,j=1,2,⋯,K
其中, I I I为指示函数,即当 y i = c j y_i=c_j yi=cj时 I I I为1,否则 I I I为0。
k k k近邻法的特殊情况是 k = 1 k=1 k=1的情形,称为最邻近算法。
二、三个基本要素
1.距离度量
k
k
k近邻模型的特征空间一般是
n
n
n维实数向量空间
R
n
\bm{R}^n
Rn。使用的是欧氏距离,但也可是其他距离。如更一般的
L
p
L_p
Lp距离。定义空间中两点
x
1
=
(
x
1
1
,
x
1
2
,
⋯
,
x
1
n
)
T
,
x
2
=
(
x
2
1
,
x
2
2
,
⋯
,
x
2
n
)
T
x_1=(x_1^1,x_1^2,\cdots,x_1^n)^T,x_2=(x_2^1,x_2^2,\cdots,x_2^n)^T
x1=(x11,x12,⋯,x1n)T,x2=(x21,x22,⋯,x2n)T的
L
p
L_p
Lp距离定义为
L
p
(
x
1
,
x
2
)
=
(
∑
l
=
1
n
∣
x
1
l
−
x
2
l
∣
p
)
1
p
L_p(x_1,x_2)=\left(\sum_{l=1}^{n}|x_1^l-x_2^l|^p\right)^\frac{1}{p}
Lp(x1,x2)=(l=1∑n∣x1l−x2l∣p)p1
当
p
=
2
p=2
p=2时,称为欧氏距离,当
p
=
1
p=1
p=1时,称为曼哈顿距离。
2. k k k值的选择
k
k
k值得选择会对
k
k
k近邻法的结果产生重大影响。
如果选择较小的
k
k
k值,就相当于用较小的邻域中的训练实例进行预测,预测结果会对近邻的实例点非常敏感。如果临近的实例点恰巧是噪声,预测就会出错。换句话说,
k
k
k值得减小就意味着整体模型变得复杂,容易发生过拟合。
如果选择较大的
k
k
k值,就相当于用较大邻域中的训练实例进行预测,这时与输入实例较远的(不相似的)训练实例也会对预测起作用,使预测发生错误。
k
k
k值的增大就意味着整体模型变得简单。
在应用中,
k
k
k值一般取一个比较小的数值。通常采用交叉验证法来选取最优的
k
k
k值。
3.分类决策规则
这里的分类决策规则就是多数表决,即由输入实例的 k k k个邻近的训练实力中的多数类决定输入实力的类。
三、 k d kd kd树
通过上面的学习,我们就已经明白了
k
k
k近邻算法的具体步骤,这时你可以采用python等语言的编程实现
k
k
k近邻算法。
但是如果训练集中的实例的维数很大以及训练数据容量很大时,我们如果一一计算训练集中的实例点与预测点之间的欧氏距离,并且找出
k
k
k个近邻时计算非常耗时,这种方法是不可行的。为了提高
k
k
k近邻搜索的效率,可以考虑使用特殊的结构存储训练数据,以减少计算距离的次数。具体方法很多,这里介绍其中一种
k
d
kd
kd树的方法。
1.构造平衡 k d kd kd树
构造平衡 k d kd kd树。
- 开始:构造根节点,根节点包含
T
T
T的
k
k
k维空间的超矩形区域。 选择
x
1
x^1
x1为坐标轴,以
T
T
T中所有实例的
x
1
x^1
x1坐标的中位数为切分点,将根节点对应的超矩形区域切分为两个子区域。
由根结点生成深度为1的左、右子节点:左子结点对应坐标 x 1 x^1 x1小于切分点的子区域,右子节点对应坐标 x 1 x^1 x1大于切分点的子区域。
将落在切分超平面的实例点保存在根结点。 - 重复:对深度为
j
j
j的结点,选择
x
l
x^l
xl为切分轴,
l
=
j
(
mod
k
)
+
1
l=j(\text{mod}k)+1
l=j(modk)+1,以该结点的区域中所有实例的
x
l
x^l
xl坐标的中位数为切分点,将该结点对应的超矩形区域切分为两个子区域。
由该结点生成深度为 j + 1 j+1 j+1的左、右子结点:左子节点对应坐标 x l x^l xl小于切分点的子区域,右子结点对应坐标 x l x^l xl大于切分点的子区域。
将落在切分超平面上的实例点保存在该结点。 - 指导两个子区域内没有实例存在是停止,从而形成 k d kd kd树的区域划分。
我们直接使用一个例子来展示如何构造
k
d
kd
kd树。
给定一个二维空间的训练数据集
T
=
{
(
2
,
3
)
T
,
(
5
,
4
)
T
,
(
9
,
6
)
T
,
(
4
,
7
)
T
,
(
8
,
1
)
T
,
(
7
,
2
)
T
}
T=\{(2,3)^T,(5,4)^T,(9,6)^T,(4,7)^T,(8,1)^T,(7,2)^T\}
T={(2,3)T,(5,4)T,(9,6)T,(4,7)T,(8,1)T,(7,2)T}
构造一个平衡
k
d
kd
kd树。
- 第一步,以
x
1
x^1
x1为切分的坐标轴,
x
1
x^1
x1维度上的中位数为7,所以我们以7为切分点对数据进行切分。如下图所示:
此时将 ( 7 , 2 ) (7,2) (7,2)点保存在根结点(深度为0),左区域有三个点,右区域有两个点。 - 第二步,此时结点的深度为1,
l
=
1
(
m
o
d
2
)
+
1
=
2
l=1(mod 2)+1=2
l=1(mod2)+1=2,选择
x
2
x^2
x2为切分的坐标轴。左区域中
x
2
x^2
x2维度上的中位数为4,右区域中
x
2
x^2
x2维度上的中位数为6,据此对左右区域进行切分。如下图所示:
此时的 k d kd kd树如下所示:
- 第三步,此时的结点的深度为2,
l
=
2
(
m
o
d
2
)
+
1
=
1
l=2(mod 2)+1=1
l=2(mod2)+1=1,选择
x
1
x^1
x1为切分的坐标轴。切分后如下图所示:
kd树如下图所示:
至此我们发现所有的区域中已经没有实例存在(也就是说没有实例点可以进行划分),所以停止 k d kd kd树的区域划分。
2.用 k d kd kd树进行最近邻搜索
上一节我们已经介绍了如何构造
k
d
kd
kd树,本节我们介绍如何使用
k
d
kd
kd树进行最近邻搜索。
算法如下:
输入:已构造的
k
d
kd
kd树,目标点
x
x
x。
输出:
x
x
x的最近邻。
- 从根结点出发,递归地向下访问 k d kd kd树。若目标点 x x x当前维的坐标小于切分点的坐标,则移动到左子节点,否则移动到右子结点,直到子结点为叶结点为止。
- 令此叶节点为“当前最近点”(计算点 x x x与此叶结点的距离)。
- 递归地向上回退,在每个结点(记为
o
o
o)进行以下操作:
- (a)如果该结点 o o o保存的实例点与点 x x x的距离比当前最近点距离点 x x x更近,则以该实例点 o o o作为“当前最近点”。
- (b)当前最近点一定存在于该结点 o o o一个子结点对应的区域。检查该子结点的父结点 o o o的另一子结点对应的区域是否有更近的点。具体地,检查另一子节点对应的区域是否与以目标点 x x x为球心,以目标点与“当前最近点”间的距离为半径的超球体相交。如果相交,可能在另一个子节点对应的区域内存在距目标点更近的点,移动到另一个子节点。接着,递归地进行最近邻搜索,如果不相交,向上回退。
- 当回退到根结点时,搜索结束。最后的“当前最近点”即为 x x x的最近邻点。
我们用一个实例来演示
k
d
kd
kd树进行最近邻搜索的过程。目标点是
x
(
2
,
4.5
)
x(2,4.5)
x(2,4.5),要找到
x
x
x的最近邻。
按照算法流程,第一步结束后,我们移动到
(
4
,
7
)
(4,7)
(4,7)点。移动路径是:
(
7
,
2
)
−
(
5
,
4
)
−
(
4
,
7
)
(7,2)-(5,4)-(4,7)
(7,2)−(5,4)−(4,7)。
第二步:令叶节点
(
4
,
7
)
(4,7)
(4,7)为当前最近点
o
o
o。目标点与最近点
o
o
o的距离是为3.202。
第三步:递归向上回退,回退到
(
5
,
4
)
(5,4)
(5,4)点。(a)目标点与(5,4)之间的距离为:3.041,所以
(
5
,
4
)
(5,4)
(5,4)点比当前最近点
o
o
o距离目标点
x
x
x更近,则更新当前最近点
o
o
o为
(
5
,
4
)
(5,4)
(5,4)。(b)以目标点
x
x
x为圆心,以目标点与“当前最近点
o
o
o”间的距离为半径的圆与当前最近点
o
(
5
,
4
)
o(5,4)
o(5,4)的另一个子节点
(
2
,
3
)
(2,3)
(2,3)对应的区域相交。所以我们移动到
(
2
,
3
)
(2,3)
(2,3)点,
(
2
,
3
)
(2,3)
(2,3)点距离目标点比当前的最近点
o
(
5
,
4
)
o(5,4)
o(5,4)要近,所以最近点o更新为
(
2
,
3
)
(2,3)
(2,3)。接着继续往上回退为
(
7
,
2
)
(7,2)
(7,2)点,
(
7
,
2
)
(7,2)
(7,2)点距离目标点比
(
2
,
3
)
(2,3)
(2,3)点距离目标点要远。
第四步:回退到根结点,搜索结束。此时最近点
o
(
2
,
3
)
o(2,3)
o(2,3)为目标点
x
(
2
,
4.5
)
x(2,4.5)
x(2,4.5)的最近邻点。
整体的思路大概就是;
- 先进行二分查找到叶结点
- 回溯搜索路径
- 检查另一子结点区域是否有更近的
- 搜索另一个子空间
- 最后回溯到根结点
四、参考链接
代码链接:https://github.com/zgaxddnhh/lihang-code
参考书籍:机器学习方法.李航