本课程来自深度之眼,部分截图来自课程视频以及李航老师的《统计学习方法》第二版。
公式输入请参考:在线Latex公式
导入
实现k近邻法时,主要考虑的问题是如何对训练数据进行快速k近邻搜索。这点在特征空间的维数大及训练数据容量大时尤其必要。
原始k近邻法的实现方法是线性扫描(linear scan),也就是要计算输入实例与每一个训练实例的距离。例如训练集有10w个样本,新加入一个样本的时候,就要和之前的10w个样本分别计算距离,从而判别新样本的类别。为了提高k近邻搜索的效率,可以考虑使用特殊的结构存储训练数据,以减少计算距离的次数。具体方法很多,本节主要介绍其中的kd树(kdtree)方法(原书3.3)。
KD树
构造
输入:
k
k
k维(这里的k和k近邻的k不一样,这里指维度)空间数据集
T
=
x
1
,
x
2
,
…
,
x
N
T={x_1,x_2,…,x_N}
T=x1,x2,…,xN,其中
x
i
=
(
x
i
(
1
)
,
x
i
(
2
)
,
…
,
x
i
(
k
)
)
T
,
i
=
1
,
2
,
…
,
N
x_i=(x_i^{(1)},x_i^{(2)},…,x_i^{(k)})^T,i=1,2,…,N
xi=(xi(1),xi(2),…,xi(k))T,i=1,2,…,N;
输出:kd树。
1.开始:构造根结点,根结点对应于包含
T
T
T的
k
k
k维空间的超矩形区域(如果样本是2维,就是平面,三维就是三维坐标)。
选择
x
(
1
)
x^{(1)}
x(1)为坐标轴,以
T
T
T中所有实例的
x
(
1
)
x^{(1)}
x(1)坐标的中位数为切分点,将根结点对应的超矩形区域切分为两个子区域。切分由通过切分点并与坐标轴
x
(
1
)
x^{(1)}
x(1)垂直的超平面实现。
由根结点生成深度为1的左、右子结点:左子结点对应坐标
x
(
1
)
x^{(1)}
x(1)小于切分点的子区域,右子结点对应于坐标
x
(
1
)
x^{(1)}
x(1)大于切分点的子区域。
将落在切分超平面上的实例点保存在根结点。
这里就是把所有样本第一个维度数字按顺序排列后,找到中位数(一组数据按大小顺序排列起来,处在中间位置的一个数或最中间两个数的平均值。例如:12345的中位数是3,1234的中位数是2.5),过这个中位数,做一个垂直于第一维度的坐标轴直线,就是第一步,看实例,假如有三个样本点:
(1,2),(5,9),(2,3)
第一个维度的数字按顺序排列是:125,中位数是2,然后沿着x=2垂直于第一个维度的坐标(这里是x轴)做一个直线,小于2的是左子节点,大于2的右子节点
2.重复:对深度为
j
j
j的结点(这里的深度是指树的深度,根节点深度为0),选择
x
(
l
)
x^{(l)}
x(l)为切分的坐标轴,
l
=
j
(
m
o
d
k
)
+
1
l=j(\mod k)+1
l=j(modk)+1(例如上面2维样本深度为1的节点划分时,
l
=
1
m
o
d
2
+
1
=
2
l=1\mod 2 +1=2
l=1mod2+1=2,就是以第二个维度特征进行划分),以该结点的区域中所有实例的
x
(
l
)
x^{(l)}
x(l)坐标的中位数为切分点,将该结点对应的超矩形区域切分为两个子区域。切分由通过切分点并与坐标轴
x
(
l
)
x^{(l)}
x(l)垂直的超平面实现。
由该结点生成深度为
j
+
1
j+1
j+1的左、右子结点:左子结点对应坐标
x
(
l
)
x^{(l)}
x(l)小于切分点的子区域,右子结点对应坐标
x
(
l
)
x^{(l)}
x(l)大于切分点的子区域。
将落在切分超平面上的实例点保存在该结点。
意思就是将第一步划分的子空间再按第二个维度继续划分,例如上图中就变成:
当然,这里由于数据点比较少就没法看出来效果。
3.直到两个子区域没有实例存在时停止。从而形成kd树的区域划分。
构造实例3.2
给定一个二维空间的数据集:
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}
构造一个平衡kd树。
第一个维度
x
(
1
)
x^{(1)}
x(1)顺序排列为:245789,中位数取57的平均值为6,书上取的是较大者7,得到左右分别是:
(
2
,
3
)
T
,
(
4
,
7
)
T
,
(
5
,
4
)
T
(2,3)^T,(4,7)^T,(5,4)^T
(2,3)T,(4,7)T,(5,4)T和
(
8
,
1
)
T
,
(
9
,
6
)
T
(8,1)^T,(9,6)^T
(8,1)T,(9,6)T
然后左节点根据第二个维度
x
(
2
)
x^{(2)}
x(2)进行划分,先排序347,中位数为4
右节点根据第二个维度
x
(
2
)
x^{(2)}
x(2)进行划分,先排序89,中位数取9
第2层,然后再按
x
(
1
)
x^{(1)}
x(1),进行划分,此时的左节点只有
(
2
,
3
)
T
(2,3)^T
(2,3)T,中位数取2
右节点只有
(
4
,
7
)
T
(4,7)^T
(4,7)T,中位数取4
还有一个节点就不啰嗦了,最后的树如下图:
搜索
文字描述见原文算法3.3(用kd树的最近邻搜索),p56
输入:已构造的kd树,目标点
x
x
x;
输出:
x
x
x的最近邻。
在kd树中找出包含目标点x的叶结点,就是找树中的同父节点的兄弟节点,例如上图中如果要找S的最近邻点,就先找到D,然后计算S和D的距离d,然后以d为半径,再看有没有其他点存在,如果没有,那么D就最邻近的点,如有,那么在这些点中找最近的点。
如果没有兄弟节点,就找父节点,再以父节点的距离为半径进行判别是否有其他点存在。
小结
1.Kd树采用了特殊的结构存储训练数据。
2.Kd树可以减少计算距离的次数。
3.但当空间维数接近训练实例数时,它的效率会迅速下降。接近线性搜索
O
(
n
)
O(n)
O(n)