【问题标题】:Suggestions on how to speed up a distance calculation关于如何加快距离计算的建议
【发布时间】:2011-05-13 11:13:20
【问题描述】:

考虑以下类:

class SquareErrorDistance(object):
    def __init__(self, dataSample):
        variance = var(list(dataSample))
        if variance == 0:
            self._norm = 1.0
        else:
            self._norm = 1.0 / (2 * variance)

    def __call__(self, u, v): # u and v are floats
        return (u - v) ** 2 * self._norm

我用它来计算向量的两个元素之间的距离。我基本上为使用此距离度量的向量的每个维度创建该类的一个实例(有些维度使用其他距离度量)。分析显示,这个类的__call__ 函数占了我的 knn 实现的 90% 的运行时间(谁会想到)。我认为没有任何纯 Python 方法可以加快速度,但也许如果我用 C 实现它?

如果我运行一个简单的 C 程序,它只使用上面的公式计算随机值的距离,它比 Python 快几个数量级。所以我尝试使用 ctypes 并调用一个 C 函数来进行计算,但显然参数和返回值的转换非常昂贵,因为生成的代码要慢得多。

我当然可以在 C 中实现整个 knn 并调用它,但问题是,正如我所描述的,我对向量的某些维度使用不同的距离函数,并且将这些转换为 C 将是太多的工作.

那么我的替代方案是什么?使用Python C-API 编写 C 函数会摆脱开销吗?还有其他方法可以加快计算速度吗?

【问题讨论】:

  • 我建议使用 Cython(可能会在几分钟内给出示例实现的答案)。我假设你的算法已经尽可能合理地调整了?
  • @delnan:我已经在可能和适当的情况下使用了缓存,所以我看不到任何节省距离计算的方法。
  • 那么……不相关,dataSamplevar 是什么?
  • @delnan: datasamplefloats 的列表,var 是来自 numpy 的方差函数。
  • 有点跑题了:你确实意识到__call__()返回的表达式被计算为好像这样写(u - v) ** (2 * self._norm)?请参阅运算符优先级表here

标签: python performance python-c-api


【解决方案1】:

这可能没有多大帮助,但您可以使用嵌套函数重写它:

def SquareErrorDistance(dataSample):
    variance = var(list(dataSample))
    if variance == 0:
        def f(u, v):
            x = u - v
            return x * x
    else:
        norm = 1.0 / (2 * variance)
        def f(u, v):
            x = u - v
            return x * x * norm
    return f

【讨论】:

    【解决方案2】:

    以下 cython 代码(我意识到 __init__ 的第一行不同,我将其替换为随机的东西,因为我不知道 var 并且因为无论如何都没关系 - 你说 __call__ 是瓶颈):

    cdef class SquareErrorDistance:
        cdef double _norm
    
        def __init__(self, dataSample):
            variance = round(sum(dataSample)/len(dataSample))
            if variance == 0:
                self._norm = 1.0
            else:
                self._norm = 1.0 / (2 * variance)
    
        def __call__(self, double u, double v): # u and v are floats
            return (u - v) ** 2 * self._norm
    

    通过一个简单的 setup.py 编译(只是 the example from the docs 更改了文件名),它在简单设计的 timeit 基准测试中的性能比等效的纯 python 好近 20 倍。请注意,唯一更改的是cdefs 的_norm 字段和__call__ 参数。我认为这非常令人印象深刻。

    【讨论】:

    • 这 - 太棒了。太感谢了。我实际上也可以将这个(意思是 Cython)应用到许多其他热点。你让我很开心:)
    • @Space_C0wb0y:总是乐于提供帮助 :) 如果你大量使用 numpy,也可以看看 docs.cython.org/src/tutorial/numpy.html
    • 您也可以将方差声明为双精度数。它可能不会有太大的不同,但为什么不呢?
    猜你喜欢
    • 2017-08-10
    • 2013-04-23
    • 1970-01-01
    • 2020-05-17
    • 2013-09-27
    • 2019-08-01
    • 1970-01-01
    • 2021-01-30
    • 2021-06-18
    相关资源
    最近更新 更多