python中最快的成对距离度量
Posted
技术标签:
【中文标题】python中最快的成对距离度量【英文标题】:Fastest pairwise distance metric in python 【发布时间】:2013-12-15 04:35:16 【问题描述】:我有一个一维数字数组,并且想要计算所有成对的欧几里得距离。我有一种方法(感谢 SO)通过广播来做到这一点,但它效率低下,因为它计算每个距离两次。而且它不能很好地扩展。
这是一个示例,它使用包含 1000 个数字的数组为我提供了我想要的东西。
import numpy as np
import random
r = np.array([random.randrange(1, 1000) for _ in range(0, 1000)])
dists = np.abs(r - r[:, None])
什么是 scipy/numpy/scikit-learn 中最快的实现,我可以使用它来执行此操作,因为它必须扩展到一维数组具有 >10k 值的情况。
注意:矩阵是对称的,所以我猜测通过解决这个问题至少可以获得 2 倍的加速,我只是不知道如何。
【问题讨论】:
有一个函数:scipy.spatial.distance.pdist
。我不知道这是否是最快的选择,因为它需要检查多维数据、非欧几里得规范和其他东西,但它是内置的。
您需要多快?它永远不会比 O(n^2) 更好地扩展,因为您必须填充 n^2 个输出条目。您现有的解决方案是 O(n^2),似乎没有太大的优化空间。
当我尝试它时,这似乎已经足够好地扩展到 >10k 值了。请记住,您需要填充 1 亿个输出条目。这几乎是成对距离的半 GB。
@askewchan 我不认为它...如果你按照源代码,最后this 是被调用的函数。不仅没有花哨的优化,而且对于一维向量,它是平方并取平方根来计算绝对值。可能比他特定用例的 OP 代码更糟糕。
@CTZhu 如果我没记错的话,scipy
总是用 BLAS 编译的,它不像 numpy
那样是可选的。
【参考方案1】:
其他答案都没有完全回答这个问题 - 1 在 Cython 中,一个较慢。但两者都提供了非常有用的提示。跟进他们表明scipy.spatial.distance.pdist
是要走的路。
这里有一些代码:
import numpy as np
import random
import sklearn.metrics.pairwise
import scipy.spatial.distance
r = np.array([random.randrange(1, 1000) for _ in range(0, 1000)])
c = r[:, None]
def option1(r):
dists = np.abs(r - r[:, None])
def option2(r):
dists = scipy.spatial.distance.pdist(r, 'cityblock')
def option3(r):
dists = sklearn.metrics.pairwise.manhattan_distances(r)
使用 IPython 计时:
In [36]: timeit option1(r)
100 loops, best of 3: 5.31 ms per loop
In [37]: timeit option2(c)
1000 loops, best of 3: 1.84 ms per loop
In [38]: timeit option3(c)
100 loops, best of 3: 11.5 ms per loop
我没有尝试 Cython 实现(我不能将它用于这个项目),但是将我的结果与其他答案进行比较,看起来 scipy.spatial.distance.pdist
比 Cython 实现慢了大约三分之一(通过对 np.abs 解决方案进行基准测试来考虑不同的机器)。
【讨论】:
我认为这和:scikit-learn.org/stable/modules/generated/… 一样快? sklearn 中的版本? 在 scipy.spatial.distance.pdist 的情况下,应该是 c 而不是 r? @learner 我想是的【参考方案2】:这是一个 Cython 实现,它在我的计算机上为这个示例提供了 3 倍以上的速度提升。对于更大的数组来说,这个时间应该被审查,因为 BLAS 例程可能比这个相当幼稚的代码可以更好地扩展。
我知道您在 scipy/numpy/scikit-learn 中提出了一些要求,但也许这会为您打开新的可能性:
文件my_cython.pyx
:
import numpy as np
cimport numpy as np
import cython
cdef extern from "math.h":
double abs(double t)
@cython.wraparound(False)
@cython.boundscheck(False)
def pairwise_distance(np.ndarray[np.double_t, ndim=1] r):
cdef int i, j, c, size
cdef np.ndarray[np.double_t, ndim=1] ans
size = sum(range(1, r.shape[0]+1))
ans = np.empty(size, dtype=r.dtype)
c = -1
for i in range(r.shape[0]):
for j in range(i, r.shape[0]):
c += 1
ans[c] = abs(r[i] - r[j])
return ans
答案是包含所有非重复计算的一维数组。
要导入 Python:
import numpy as np
import random
import pyximport; pyximport.install()
from my_cython import pairwise_distance
r = np.array([random.randrange(1, 1000) for _ in range(0, 1000)], dtype=float)
def solOP(r):
return np.abs(r - r[:, None])
使用 IPython 计时:
In [2]: timeit solOP(r)
100 loops, best of 3: 7.38 ms per loop
In [3]: timeit pairwise_distance(r)
1000 loops, best of 3: 1.77 ms per loop
【讨论】:
你的意思是fabs
-- abs
是int
的变体。【参考方案3】:
使用一半的内存,但比np.abs(r - r[:, None])
慢6倍:
triu = np.triu_indices(r.shape[0],1)
dists2 = abs(r[triu[1]]-r[triu[0]])
【讨论】:
以上是关于python中最快的成对距离度量的主要内容,如果未能解决你的问题,请参考以下文章