【问题标题】:NearestNeighbors tradeoff - run faster with less accurate resultsNearestNeighbors 权衡 - 运行速度更快但结果不太准确
【发布时间】:2015-08-27 19:07:51
【问题描述】:

我正在处理一个中等大小的数据集 (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

【问题讨论】:

  • 我不知道knn在Python中是怎么用的,但是你在哪里传参数呢?还是您允许他们获取默认值?
  • @gsamaras 看到第一个代码块 - NearestNeighbors的初始化
  • 我已经看过了。但是,我可以假设您使用的是默认参数,对吧?
  • @gsamaras 是的,任何未说明的参数都被赋予其默认值,您可以在documentation中看到这些
  • 是的,我看到了链接,谢谢,好问题顺便说一句,+1。

标签: python scikit-learn smooth nearest-neighbor


【解决方案1】:

首先,为什么要使用球树?也许您的指标确实对您暗示了这一点,但如果不是这样,您也可以使用 kd-tree。


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

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


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

【讨论】:

    【解决方案2】:

    没有你的数据集和完整的代码,很难确定。这是我的想法。

    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_dimensions==46 numpy 数组在N_samples 上调用它,并适当地调整您的smooth() 代码。 (例如,NearestNeighbors 返回 N_samples by N_neighbors 数组比返回 N_samples 长度为 N_samples 的单个数组快得多。)

    【讨论】:

    • 感谢您的提示。 distances *= ((1-alpha)/norm) 是有问题的,因为每个 norm 是距离的 l1 总和。你的意思可能是distances *= ((1-alpha)/sum(distances))。除此之外,我为每个样本调用smooth() 一次 次,而不是多次。
    • 至于打电话给smooth():抱歉,不清楚。您想对所有样本调用一次smooth(),而不是对每个样本调用一次。
    猜你喜欢
    • 2014-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多