scipy.spatial.ckdtree 运行缓慢

3
我一直在使用scipy中的spatial.cKDTree来计算点之间的距离。对于我的典型数据集(将约1000个点与约1e6个点的数组进行距离计算),它总是运行得非常快(约1秒)。
我在Ubuntu 14.10上的计算机上运行这段代码,使用的是Python 2.7.6。直到今天早上,我大多数Python包都是用apt-get管理的,包括scipynumpy。但我想要最新版本的几个包,所以我决定卸载由apt-get安装在/usr/lib/python2.7/中的包,并使用pip install重新安装所有包(如有必要,使用apt-get处理scipy依赖项,如liblapack-dev)。所有的包都已经安装好了,可以顺利导入。
import scipy
import cython
scipy.__version__
'0.16.0'
cython.__version__
'0.22.1'

现在,对于相同大小的数据集运行spatial.cKDTree速度非常慢。我看到的运行时间约为500秒,而不是1秒。我很难弄清楚出了什么问题。

有什么建议可以解释为什么使用pip安装而不是apt-get导致scipy.spatial.cKDTree运行如此缓慢吗?

2个回答

16
0.16.x中,我添加了一个选项来使用中位数或滑动中点规则构建cKDTree,并选择是否在kd树的每个节点重新计算边界超矩形。默认值是基于scipy.spatial.cKDTreesklearn.neighbors.KDTree性能的经验得出的。在某些刻意制造的情况下(具有高度拉伸的数据),可能会产生负面影响,但通常应该更快。尝试使用balanced_tree=False和/或compact_nodes=False构建cKDTree进行实验。将两者都设置为False可使您获得与0.15.x相同的行为。不幸的是,很难设置适合所有人的默认值,因为性能取决于数据。
还要注意,当balanced_tree=True时,我们在构建kd树时通过快速选择来计算中位数。如果数据由于某种原因被预排序,它将非常慢。在这种情况下,可以帮助打乱输入数据的行。或者您可以设置balanced_tree=False以避免部分快排。
还有一个新选项可以对最近邻查询进行多线程处理。尝试使用n_jobs=-1调用query,看看它是否对您有所帮助。 2020年6月更新: SciPy1.5.0将使用一种新算法(基于C++ STL的introselect部分排序),解决了这里报告的问题。

谢谢,将balanced_tree和compact_nodes都设置为False后,我得到了之前相同的结果。我的数据没有朝一个方向拉伸,所以我需要进一步阅读cKDTree来弄清楚为什么这些参数会导致我的问题减速。 - jdmcbr
你的数据点提前排序了吗?例如,它们来自网格吗?在这种情况下,用于拆分节点的部分排序可能会变成二次复杂度。 - Sturla Molden
啊,有趣。是的,我的数据来自一个网格,我错过了你回答的第二段的导入,因为我没有有意地对数据进行排序。尽管如此,数据已经被排序了。 - jdmcbr
1
通常情况下,您不需要使用kd-tree在网格中搜索k-NN,因为您可以通过分割和舍入来完成,这将是O(n)而不是O(n log n)。但这可能是cKDTree的一个更常见的(滥用)用途,超出了我的想象。如果发现这对很多人来说是个问题,我们可以将默认值更改回balanced_tree=False或执行其他技巧来检查或纠正此问题。 - Sturla Molden
在我的情况下,我根据一些选择条件保留大约1-2%来自网格的不规则间隔点。当我需要执行比您提到的基于网格坐标到数组坐标方法更复杂的操作时,我遇到了 cKDTree - jdmcbr
1
仍然是非常相关的答案!我在一台机器上看到我的cKDTree脚本完成时间为5秒,而在另一台机器上则需要8分钟 - 原来scipy==0.13默认设置比scipy==1.0.0默认设置更好地处理了我的数据(曲线坐标网格)。感谢您的提示! - danwild

1
在下一个SciPy版本中,使用introselect而不是quickselect创建平衡kd树,在结构化数据集上速度更快。如果您在结构化数据集(如图像或网格)上使用cKDTree,则可以期待性能大幅提升。如果您从GitHub的主分支构建SciPy,则已经可以使用它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接