考虑以下课程:
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函数会消除开销吗?还有其他方法可以加快计算速度吗?
答案 0 :(得分: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倍。请注意,唯一更改的是cdef
字段的_norm
和__call__
参数。我认为这非常令人印象深刻。
答案 1 :(得分:0)
这可能无济于事,但您可以使用嵌套函数重写它:
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