机器学习基础(四十三)—— kd 树( k 近邻法的实现)

实现 k 近邻法时,主要考虑的问题是如何对训练数据进行快速 k 近邻搜索,这点在如下的两种情况时,显得尤为必要:

  • (1)特征空间的维度大
  • (2)训练数据的容量很大时

k 近邻法的最简单的实现是现行扫描(linear scan),这时需计算输入实例与每一个训练实例的距离,但训练集很大时,计算非常耗时,这种方法是不可行的。

为了提高 k 近邻搜索的效率,可以考虑使用特殊的结构存储训练数据,以减少计算距离的次数。如本文介绍的 kd 树(kd tree,k-dimensional tree)方法(这里的 k 表示样本集的维度,与 k近邻的 k 无关)。

构造 kd 树

kd 树是一种对 k 维空间中的实例进行存储以便对其进行快速检索的树形数据结构。kd树是一种二叉树,表示对 k 维空间的一次划分(partition)。构造 kd 树相当与不断地用垂直于坐标轴(沿着每一个属性列, d=1,2,,k )的超平面(hyperplane)将 k 维空间划分。

通常依次选择坐标轴对空间划分,选择训练实例点在选定坐标轴上的中位数(median)为切分点。这样得到的 kd 树是平衡的,平衡的 kd 树未必就是最优的。

class Node:
    def __init__(self, point):
                # point 表示切分点
        self.left = None
        self.right = None
        self.point = point
def median(l):
    m = len(l)/2
    return l[m], m

def build_kdtree(X, d, depth):
    k = len(X[0])
    X = sorted(X, key=lambda x: x[d])
    p, m = median(X)
    tree = Node(p)
    print p, depth
    if m > 0:
        tree.left = build_kdtree(X[:m], (d+1)%k, depth+1)
    if (m+1) < len(X):
        tree.right = build_kdtree(X[m+1:], (d+1)%k, depth+1)
    return tree

搜索 kd 树

class Node:
    def __init__(self, point):
        self.left = None
        self.right = None
        self.parent = None
        self.point = point
    def set_left(self, left):
        self.left = left
        left.parent = self
    def set_right(self, right):
        self.right = right
        right.parent = self

def search_kdtree(tree, d, target): 
    k = len(tree[0])
    if tree.point[d] < target[d]:
        if tree.right != None:
            return search_kdtree(tree.right, (d+1)%k, target)
    else:
        if tree.left != None:
            return search_kdtree(tree.left, (d+1)%k, target)

    def update_best(t, best):
        if t == None: return 
        t = t.point
        d = euclidean(t, target)
        if  d < best[1]:
            best[1] = d
            best[0] = t

    best = [tree.point, float('inf')]

    while tree.parent != None:
        update_best(tree.parent.left, best)
        update_best(tree.parent.right, best)
        tree = tree.parent
    return best[0]

分析

如果实例点是随机分布的,kd搜索树的平均时间复杂度为 O(logN) N <script id="MathJax-Element-5" type="math/tex">N</script> 表示训练实例数。kd 树搜索更适用于训练实例数远大于空间维数时的 k 近邻搜索,当空间维数接近训练实例数(非常畸形,也即接近线性的一颗不平衡的二叉树)时,它的效率会迅速下降,几乎接近线性扫描。

References

[1] k 近邻法

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

五道口纳什

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值