NearestNeighbors tradeoff - 运行速度越快,结果越准确

时间:2015-06-11 21:27:28

标签: python scikit-learn smooth nearest-neighbor

我正在使用中等大小的数据集(shape=(14013L, 46L))。 我想用knn来平滑每个样本。 我用以下方式训练我的模型:

NearestNeighbors(n_neighbors, algorithm='ball_tree',    
    metric=sklearn.metrics.pairwise.cosine_distances)

顺利如下:

def smooth(x,nbrs,data,alpha):
    """
    input:
        alpha: the smoothing factor
        nbrs: trained NearestNeighbors from sklearn
        data: the original data 
             (since NearestNeighbors returns only the index and not the samples)
        x:    what we want to smooth
    output:
        smoothed x with its nearest neighbours
    """
    distances, indices = nbrs.kneighbors(x)
    distances = map(lambda z:abs(-z+1),distances)[0]
    norm = sum(distances)
    if norm == 0:
        "No neighbours were found."
        return x
    distances = map(lambda z: (1-alpha)*z/norm ,distances)
    indices = map(lambda z: data[z],indices)[0]
    other = np.array([indices[i] * distances[i] for i in range(len(distances))])
    z = x * alpha
    z = z.reshape((1,z.shape[0]))
    smoothed = sum(np.concatenate((other,z),axis=0))
    return smoothed

我的问题:

  1. 怎么可能没有找到邻居?(我在我的数据集上经历了if条件)
  2. 拟合("训练")需要18秒,但平滑~1000个样本需要20多分钟。如果平滑过程会更短,我愿意得到不太准确的结果。可能吗?我应该改变哪个parameters才能实现它?

2 个答案:

答案 0 :(得分:1)

没有您的数据集和完整代码,很难肯定地说。这就是我的想法。

distances = map(lambda z:abs(-z+1),distances)[0]
norm = sum(distances)

因为您索引地图的结果,所以您应该只获得第一个邻居。 如果 x实际上是您用来训练的点之一,那么第一个最近的邻居将是...... x。因为您使用余弦距离,所以该距离恰好是:1。abs(1-1) == 0。在我提出修正之前,让我们谈谈表现。

关于性能:你到处都在使用map函数,这是一个内置的Python。 Scikit-learn建立在numpy之上,旨在比原生Python代码更快地完成数学运算。这就是为什么培训比你的代码快得多的原因。您应该使用numpy算术而不是map。一个例子:这一行

distances = map(lambda z: (1-alpha)*z/norm ,distances)

应该是这个

distances *= ((1-alpha)/norm)

如果norm是一个数组,它应该有适当的维度来启动numpy广播规则并完成“正确的事情”,完全用C语言。

好的,因为我建议使用numpy数组(而不是map和Python列表),我相信在if语句之前两行的正确之处是删除索引。 (其余的代码也可能被索引破坏;在函数的第二行之后,distances不是数组列表,而是标量。)

distances = np.abs( distances-1 )
norm = np.sum(distances)

此外,您不应多次调用smooth()函数,每个样本一次。您应该在N_samples N_dimensions==46个numpy数组中调用它,并相应地调整smooth()代码。 (例如,NearestNeighbors将返回N_samplesN_neighbors数组比返回N_samples长度为N_samples的单个数组快得多。)

答案 1 :(得分:1)

首先,为什么要使用Ball树?也许您的指标确实对您有所帮助,但如果情况并非如此,您也可以使用kd树。

我将从理论的角度来处理你的问题。 radius参数默认设置为1.0。这对于您的数据集来说可能太小了,因为如果我理解正确,这将指定要查询的查询的半径。因此,我建议您在增加此值时运行代码,直到获得一些结果。然后减少它,直到没有结果。再次增加一点,你就拥有了数据集的最佳参数。

一个重要参数是leaf_size,它实际上影响了查询到达时要检查的点数。此参数的较小值可能会导致执行速度加快,精度较低。

您可能还想查看我的this答案,这解释了速度和准确性之间的权衡,这是在进行最近邻搜索时理解的关键。