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 -- absint 的变体。【参考方案3】:

使用一半的内存,但比np.abs(r - r[:, None])慢6倍:

triu = np.triu_indices(r.shape[0],1)
dists2 = abs(r[triu[1]]-r[triu[0]])

【讨论】:

以上是关于python中最快的成对距离度量的主要内容,如果未能解决你的问题,请参考以下文章

评估向量距离度量

模型评估(度量)

处理 NaN 的成对距离

将 K 平均聚类距离度量更改为堪培拉距离或 python 上的任何其他距离度量

非常大的数据集中的成对距离

评估TensorFlow中多维输入之间的成对欧氏距离