如何在高维数据中有效地找到k-最近邻居?

时间:2010-10-18 19:46:36

标签: algorithm data-structures computational-geometry nearest-neighbor dimensionality-reduction

所以我有大约16,000个75维数据点,并且对于每个点我想找到它的k个最近邻居(使用欧氏距离,如果这使得它变得更容易,则当前k = 2)

我的第一个想法是为此使用kd树,但事实证明,随着维数的增长,它们变得相当低效。在我的示例实现中,它只比详尽的搜索快一点。

我的下一个想法是使用PCA(主成分分析)来减少维数,但我想知道:是否有一些聪明的算法或数据结构可以在合理的时间内完全解决这个问题?

6 个答案:

答案 0 :(得分:3)

维基百科关于kd-trees的文章有一个指向ANN library的链接:

  

ANN是一个用C ++编写的库   支持数据结构和   精确和算法的算法   近似最近邻搜索   在任意高的维度。

     

根据我们自己的经验,ANN   有效地执行点   套装的大小从数千到不等   成千上万,并在   尺寸高达20 。 (适用于明显更高的应用程序   尺寸,结果是相当的   参差不齐,但无论如何你都可以试试。)

就算法/数据结构而言:

  

该库实现了许多   基于的不同数据结构   kd-trees和box-decomposition trees,   并采用了几种不同的方式   搜索策略。

我会先直接尝试,如果不能产生令人满意的效果,我会在应用PCA / ICA后将其与数据集一起使用(因为你很可能最终得到的kd尺寸不够-tree来处理)。

答案 1 :(得分:2)

  

使用kd-tree

不幸的是,在高维度上,这种数据结构受到curse of dimensionality的严重影响,导致其搜索时间与强力搜索相当。

  

减少维数

Dimensionality reduction是一种很好的方法,可以在准确性和速度之间进行公平的权衡。当你减小尺寸时会丢失一些信息,但速度会增加。

准确性我的意思是找到确切的最近邻(NN)。

当您想要减少数据存在的维度空间时,

主成分分析(PCA)是个好主意。

  

是否有一些聪明的算法或数据结构可以在合理的时间内完全解决这个问题?

近似最近邻搜索(ANNS),您可以在这里找到一个可能不是最近邻点的点,但更确切地说是它的近似值(例如,对您的查询来说,这是第4个NN) ,当你正在寻找第一个NN)。

这种方法会降低您的准确性,但会显着提高性能。此外,找到一个好的NN(足够接近查询)的概率相对较高。

您可以在介绍我们的kd-GeRaF paper时阅读有关ANNS的更多信息。

一个好主意是将ANNS与降维相结合。

Locality Sensitive Hashing(LSH)是一种解决高维最近邻问题的现代方法。关键的想法是,彼此靠近的点被散列到同一个桶。因此,当查询到达时,它将被散列到一个桶,其中该桶(通常是其相邻的桶)包含良好的NN候选者。)

FALCONN是一个很好的C ++实现,它专注于余弦相似性。另一个很好的实现是我们的DOLPHINN,这是一个更通用的库。

答案 2 :(得分:1)

你可以想象使用Morton Codes,但是如果有75个尺寸,那么它们将是巨大的。如果您只拥有16,000个数据点,那么详尽的搜索不会花费太长时间。

答案 3 :(得分:1)

没有理由相信这是NP完全的。你并没有真正优化任何东西,我很难弄清楚如何将其转换为另一个NP完全问题(我的架子上有Garey and Johnson并且找不到类似的东西)。真的,我只是追求更有效的搜索和排序方法。如果你有n个观测值,你必须在前面计算n x n个距离。然后对于每次观察,你需要挑选出最近的k个最近邻居。对于距离计算,这是n平方,对于排序是n log(n),但是你必须进行n次排序(对于n的每个值都不同)。凌乱,但仍然是多项式的时间来得到你的答案。

答案 4 :(得分:1)

BK-Tree并不是一个糟糕的想法。看看Nick's Blog on Levenshtein Automata。虽然他的重点是弦乐,但它应该为你提供其他方法的弹簧板。我能想到的另一件事是R-Trees,但我不知道它们是否已被广泛用于大尺寸。我不能说更多,因为我既没有直接使用它们,也没有自己实现它们。

答案 5 :(得分:0)

一个非常常见的实现是排序您为每个数据点计算的最近邻数组。 由于排序整个数组可能非常昂贵,您可以使用间接排序等方法,例如Python Numpy库中的Numpy.argpartition,只对您感兴趣的最接近的K值进行排序。无需对整个数组进行排序。

@Grembo上面的回答应该会大大减少。因为你只需要K最接近的值。并且无需对每个点的整个距离进行排序。

如果您只需要K邻居,这种方法可以很好地降低您的计算成本和时间复杂度。

如果您需要排序K个邻居,请再次对输出进行排序

Documentation for argpartition