【发布时间】:2020-01-20 10:38:59
【问题描述】:
我快速检查了构建树并查询它与仅计算所有欧几里德距离的性能。如果我在这棵树中查询半径内的所有其他点,它是否应该大大优于蛮力方法?
有人知道为什么我的测试代码会产生这些不同的结果吗?我用错了吗?测试用例不适合 kd-trees 吗?
PS:这是我使用的代码的简化概念验证版本。可以在here 找到我还存储和转换结果的完整代码,但它会产生相同的结果。
进口
import numpy as np
from time import time
from scipy.spatial import KDTree as kd
from functools import reduce
import matplotlib.pyplot as plt
实现
def euclid(c, cs, r):
return ((cs[:,0] - c[0]) ** 2 + (cs[:,1] - c[1]) ** 2 + (cs[:,2] - c[2]) ** 2) < r ** 2
def find_nn_naive(cells, radius):
for i in range(len(cells)):
cell = cells[i]
cands = euclid(cell, cells, radius)
def find_nn_kd_seminaive(cells, radius):
tree = kd(cells)
for i in range(len(cells)):
res = tree.query_ball_point(cells[i], radius)
def find_nn_kd_by_tree(cells, radius):
tree = kd(cells)
return tree.query_ball_tree(tree, radius)
测试设置
min_iter = 5000
max_iter = 10000
step_iter = 1000
rng = range(min_iter, max_iter, step_iter)
elapsed_naive = np.zeros(len(rng))
elapsed_kd_sn = np.zeros(len(rng))
elapsed_kd_tr = np.zeros(len(rng))
ei = 0
for i in rng:
random_cells = np.random.rand(i, 3) * 400.
t = time()
r1 = find_nn_naive(random_cells, 50.)
elapsed_naive[ei] = time() - t
t = time()
r2 = find_nn_kd_seminaive(random_cells, 50.)
elapsed_kd_sn[ei] = time() - t
t = time()
r3 = find_nn_kd_by_tree(random_cells, 50.)
elapsed_kd_tr[ei] = time() - t
ei += 1
情节
plt.plot(rng, elapsed_naive, label='naive')
plt.plot(rng, elapsed_kd_sn, label='semi kd')
plt.plot(rng, elapsed_kd_tr, label='full kd')
plt.legend()
plt.show(block=True)
【问题讨论】:
-
剧透警告:您的幼稚实现不会返回任何内容
-
我知道,检查完整的代码,它确实返回了东西。我也没有在这个 PoC 中使用返回值。我只是让它计算
-
根据我的经验,
scipy.spatial.cKDTree比纯 python 实现要快得多。cKDTree具有完全相同的方法等,因此您只需要更改导入语句即可。你的计时结果在那种情况下也成立吗?此外,您能否分析您的函数以检查大部分时间是否用于构建树或查询它?通常可以通过指定更合适的leafsize参数来改进查询时间。 -
哇...与 cKDTree 的区别是惊人的!现在的情节是这样的:imgur.com/m5JXyGT
-
它很复杂,并且依赖于数据集,因此找到合适的叶子大小的唯一方法是对真实数据运行测试。当您在真实数据上运行这些测试时,请确保分别对树的构建和查询进行计时,至少在您将树用于多个查询的情况下。来自 sklearn 实现的作者之一的This article 触及了要点,但没有太技术化。可能也值得对 sklearn 实现进行基准测试。
标签: python-3.x scipy kdtree scipy-spatial