如何提高这个Python kNN函数的效率?

在经历了MATLAB的困扰后,我决定尝试使用Python:

我编写了一个函数,用于计算当样本属于我自己的类时,使用我自己的距离函数进行kNN计算:

def closestK(sample, otherSamples, distFunc, k):"返回基于distFunc的样本最接近的k个样本"    n = len(otherSamples)    d = [distFunc(sample, otherSamples[i]) for i in range(0,n)]    idx  = sorted(range(0,len(d)), key=lambda k: d[k])    return idx[1:(k+1)]def kNN(samples, distFunc, k):    return [[closestK(samples[i], samples, distFunc, k)] for i in range(len(samples))]

这是距离函数:

@staticmethod    def distanceRepr(c1, c2):    r1 = c1.repr    r2 = c2.repr    # 因为cdist需要二维数组    if r1.ndim == 1:        r1 = np.vstack([r1,r1])    if r2.ndim == 1:        r2 = np.vstack([r2,r2])    return scipy.spatial.distance.cdist(r1, r2, 'euclidean').min()

但与“普通”的kNN函数相比,它仍然运行得非常慢,即使使用了“brute”算法。我做错了什么吗?

更新

我添加了类的构造函数。属性repr包含一组向量(从1到任意数量),距离计算为两个repr集合之间的最小欧几里得距离。

class myCluster:    def __init__(self, index = -1, P = np.array([])):        if index ==-1 :            self.repr = np.array([])            self.IDs = np.array([])            self.n = 0            self.center = np.array([])        else:            self.repr = np.array(P)            self.IDs = np.array(index)            self.n = 1            self.center = np.array(P)

以及其他相关代码(X是一个矩阵,其行是样本,列是变量):

level = [myCluster(i, X[i,:]) for i in range(0,n)]kNN(level, myCluster.distanceRepr, 3)

更新2

我进行了一些测量,发现耗时最多的行是

d = [distFunc(sample, otherSamples[i]) for i in range(0,n)]

所以问题出在distFunc上。当我将其更改为返回

np.linalg.norm(c1.repr-c2.repr)

即“普通”的向量计算,没有排序时,运行时间保持不变。所以问题在于调用这个函数。使用类会使运行时间增加60倍,这合理吗?


回答:

你只是遇到了Python(或者更准确地说是CPython解释器)的慢速问题。根据维基百科的说法:

NumPy针对的是Python的CPython参考实现,这是一个非优化的字节码编译器/解释器。为此版本的Python编写的数学算法通常比编译的等价物运行得慢得多。NumPy试图通过提供多维数组以及高效操作数组的函数和运算符来解决这个问题。因此,任何可以主要表示为数组和矩阵操作的算法都可以运行得几乎和等价的C代码一样快。

根据Scipy常见问题解答

Python的列表是高效的通用容器。它们支持(相当)高效的插入、删除、追加和连接操作,Python的列表推导式使它们易于构建和操作。然而,它们有一些限制:它们不支持像逐元素加法和乘法这样的“向量化”操作,并且它们可以包含不同类型的对象,这意味着Python必须为每个元素存储类型信息,并且在操作每个元素时必须执行类型调度代码。这也意味着很少有列表操作可以由高效的C循环执行——每次迭代都需要类型检查和其他Python API的簿记工作。

请注意,这不仅仅是Python的问题;有关更多背景,请参见例如这个这个在Stack Overflow上的问题。

由于动态类型系统和解释器的开销,如果Python无法利用各种编译的C和Fortran库(例如NumPy),它在高性能数字计算方面的用处将大大减少。此外,还有像Numba和PyPy这样的JIT编译器,它们试图使Python代码的执行速度接近静态类型、编译代码的速度。

结论:你在普通Python中做的工作太多,而卸载到快速C代码的工作太少。我认为你需要采用更像“数组导向”的编码风格,而不是面向对象的风格,以实现使用NumPy的高性能(MATLAB在这方面的情况非常相似)。另一方面,如果你使用更高效的算法(参见Ara的回答),那么Python的慢速可能就不是那么大的问题了。

Related Posts

L1-L2正则化的不同系数

我想对网络的权重同时应用L1和L2正则化。然而,我找不…

使用scikit-learn的无监督方法将列表分类成不同组别,有没有办法?

我有一系列实例,每个实例都有一份列表,代表它所遵循的不…

f1_score metric in lightgbm

我想使用自定义指标f1_score来训练一个lgb模型…

通过相关系数矩阵进行特征选择

我在测试不同的算法时,如逻辑回归、高斯朴素贝叶斯、随机…

可以将机器学习库用于流式输入和输出吗?

已关闭。此问题需要更加聚焦。目前不接受回答。 想要改进…

在TensorFlow中,queue.dequeue_up_to()方法的用途是什么?

我对这个方法感到非常困惑,特别是当我发现这个令人费解的…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注