【问题标题】:Is there a way to speed up this function to calculate K-Nearest Neighbor?有没有办法加快这个函数来计算 K 近邻?
【发布时间】:2020-06-14 00:07:02
【问题描述】:

我对编程相当陌生,我创建了一个函数来计算 1 K 最近邻 (KNN1) 以进行预测。问题是,代码太慢了,我无法在我真正需要的训练集上对其进行测试。我的训练集约为 1200 x 5600,其中有 1200 个数据点和 5600 个特征。我需要计算每一行中每个特征的平方差之和,然后选择最相似的另一行。下面的代码需要几个小时,但仍未完成。我相信永远需要的是距离循环(三重循环)。

我已包含来自 sklearn IRIS 数据集的一个小型训练集用于测试。

如果有人对加快此过程有任何建议,以便我可以在合理的时间范围内测试我的其他代码,我们将不胜感激。

from sklearn.datasets import load_iris
import numpy as np   

def l2_distance(row1, row2):
    distance = 0.0
    for i in range(len(row1)):
        #print('row one: {}'.format(row1[i]))
        #print('row two: {}'.format(row2[i]))
        distance += (row1[i] - row2[i])**2
    return sqrt(distance)

def KNN1(x, y):
    # Create sum of square distances for each feature in each row
    d_arr = []
    for i in range(0,len(x)):
        d_temp = []
        for j in range(0,len(x)):
            d = l2_distance(x[i], x[j])
            d_temp.append(d)
        d_arr.append(d_temp)
        #del d_temp

    # Find the index for the first NN
    idx_arr = []
    for i in range(0,len(d_arr)):
        temp = list(d_arr[i])
        m = min(j for j in temp if j > 0)
        idx_arr.append(temp.index(m))
        del temp

    del d_arr
    # Make a prediction based off the position in y_train for the test row
    y_hat = []
    for i in range(0,len(idx_arr)):
        y_hat.append(float(y[idx_arr[i]]))
    del idx_arr
    y_hat = np.array(y_hat)
    y_hat = np.reshape(y_hat,(len(y_hat),1))
    a = np.where(y==y_hat, 1, 0)    
    accuracy = float(np.sum(a,axis=0)/float(len(a)))*100.0
    return accuracy

iris = load_iris()
xtrain2 = iris.data[:, :2]
ytrain2 = (iris.target != 0) * 1
ytrain2 = np.reshape(ytrain2, (len(ytrain2),1))

acc = KNN1(xtrain2,ytrain2)
print('Accuracy for KNN (k=1) for the base dataset:\n\t{}\n'.format(acc))

【问题讨论】:

标签: python performance numpy knn


【解决方案1】:

正如评论中提到的,您需要考虑其他算法来加速 KNN,例如球树(在具有大量特征的数据集上效果很好)或 k-d 树。算法的优化将成倍降低时间复杂度。


但如果您坚持使用暴力搜索,以下信息可能会有所帮助:

既然你已经使用了 numpy,为什么不也使用 scipy 来加速你的计算。您可以使用scipy.spatial.distance.cdist 而不是三重循环来获取距离矩阵,并使用scipy.argsort 来查找第一个 NN 的索引。

我把你的代码改成这样:

from scipy.spatial.distance import cdist
from scipy import argsort
from scipy.stats import mode

def KNN2(x, y):
    # Create sum of square distances for each feature in each row
    d_arr = cdist(x,x)
    d_arr += np.eye(x.shape[0])*np.max(d_arr)

    # Find the index for the first NN
    idx_arr = argsort(d_arr, axis=1)[:, : 1]

    # ! I don't touch this part
    # Make a prediction based off the position in y_train for the test row
    y_hat = []
    for i in range(0,len(idx_arr)):
        y_hat.append(float(y[idx_arr[i]]))
    del idx_arr
    y_hat = np.array(y_hat)
    y_hat = np.reshape(y_hat,(len(y_hat),1))
    a = np.where(y==y_hat, 1, 0)    
    accuracy = float(np.sum(a,axis=0)/float(len(a)))*100.0

    return accuracy

在我的电脑上测试:

%timeit KNN1(xtrain2,ytrain2)
# 51.4 ms ± 523 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit KNN2(xtrain2,ytrain2)
# 1.24 ms ± 26.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

可以看到我实现的一个 tiny-KNN here

【讨论】:

    猜你喜欢
    • 2018-05-09
    • 1970-01-01
    • 1970-01-01
    • 2020-02-25
    • 2012-07-19
    • 1970-01-01
    • 2010-10-16
    • 2011-06-20
    • 2014-03-04
    相关资源
    最近更新 更多