代码之家  ›  专栏  ›  技术社区  ›  Björn Pollex

关于加快距离计算速度的建议

  •  6
  • Björn Pollex  · 技术社区  · 14 年前

    请考虑以下类别:

    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

    2 回复  |  直到 14 年前
        1
  •  2
  •   user395760user395760    14 年前

    下面的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 在更改文件名的情况下,它的性能几乎比简单设计中的等效纯python高20倍 timeit 基准。注意唯一的改变是 cdef s代表 _norm 字段和 __呼叫__ 参数。我觉得这很令人印象深刻。

        2
  •  0
  •   adw    14 年前

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

    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