如何在高维数据中高效地找到k近邻?
Posted
技术标签:
【中文标题】如何在高维数据中高效地找到k近邻?【英文标题】:How to efficiently find k-nearest neighbours in high-dimensional data? 【发布时间】:2011-04-27 03:22:40 【问题描述】:所以我有大约 16,000 个 75 维数据点,对于每个点,我想找到它的 k 个最近邻(使用欧几里德距离,如果这样更容易,目前 k=2)
我的第一个想法是为此使用 kd-tree,但事实证明,随着维度数量的增加,它们变得相当低效。在我的示例实现中,它只比穷举搜索稍微快一点。
我的下一个想法是使用 PCA(主成分分析)来减少维数,但我想知道:是否有一些聪明的算法或数据结构可以在合理的时间内准确地解决这个问题?
【问题讨论】:
我认为这类似于以下之一:facebook.com/careers/puzzles.php 至于确切的解决方案,我怀疑答案是否定的,但我想建议随机投影,约翰逊林登斯特劳斯定理可能会有所帮助 +1 表示与 LSH 和 ANN 相同的前一条评论。 我会推荐 Hanan Samet 的书,Foundations of Multidimensional and Metric Data Structures。它有一些章节专门介绍适用于高维数据的结构。 我已经删除了我的答案,因为思考它让我意识到它既具有误导性又不正确。 【参考方案1】:使用kd树
不幸的是,在高维数据结构中,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】:一个非常常见的实现是对您为每个数据点计算的最近邻数组进行排序。 由于对整个数组进行排序可能非常昂贵,您可以使用间接排序等方法,例如 Python Numpy 库中的 Numpy.argpartition 仅对您感兴趣的最接近的 K 值进行排序。无需对整个数组进行排序。
@Grembo 上面的回答应该大大减少。因为您只需要 K 个最接近的值。并且不需要对每个点的整个距离进行排序。
如果您只需要 K 个邻居,此方法将非常有效地降低您的计算成本和时间复杂度。
如果需要排序的 K 个邻居,再次对输出进行排序
见
Documentation for argpartition
【讨论】:
【参考方案3】:关于 kd-trees 的 Wikipedia 文章有一个指向 ANN library 的链接:
ANN 是一个用 C++ 编写的库,它 支持数据结构和 精确算法和 近似最近邻搜索 在任意高的维度。
根据我们自己的经验,ANN 对点执行非常有效 集大小从数千到 数十万,并且在 尺寸高达 20。 (适用于显着更高的应用 尺寸,结果相当 参差不齐,但你还是可以试试。)
就算法/数据结构而言:
该库实现了许多 不同的数据结构,基于 kd-trees 和box-decomposition trees, 并雇用了几个不同的 搜索策略。
我会先直接尝试,如果这不能产生令人满意的结果,我会在应用 PCA/ICA 后将它与数据集一起使用(因为你不太可能最终得到足够少的维度来进行 kd -树来处理)。
【讨论】:
+1 表示 ANN,这是最近信息检索领域的热门话题。在“FLANN”中搜索 Lowe 的一种实现,以及 David Mount 的“ANN”。同时搜索相关的“局部敏感哈希”。 @Steve:Tnx 用于 FLANN 和位置敏感的哈希技巧,非常有趣的东西! 这就是我最终使用的,并且(连同用 c++ 重写的程序)它的速度非常快( @Benno:很高兴它对你有用。出于好奇,您是将其应用于原始数据(75 维)还是在进行 PCA/ICA 之后的结果? 我确实应用了 PCA,但只是为了改变基向量,而不是为了减少维度。但它有其局限性,对于另一个有 240,000 个点的数据集,大约需要 15 分钟。【参考方案4】:BK-Tree 并不是一个坏主意。看看Nick's Blog on Levenshtein Automata。虽然他的重点是弦乐,但它应该为您提供其他方法的跳板。我能想到的另一件事是R-Trees,但是我不知道它们是否已被推广到大尺寸。我不能说更多,因为我既没有直接使用它们,也没有自己实现它们。
【讨论】:
【参考方案5】:没有理由相信这是 NP 完全的。您并没有真正优化任何东西,我很难弄清楚如何将其转换为另一个 NP 完全问题(我的书架上有 Garey and Johnson 并且找不到类似的东西)。真的,我只是追求更有效的搜索和排序方法。如果您有 n 个观测值,则必须预先计算 n x n 距离。然后对于每个观察,您需要挑选出前 k 个最近的邻居。距离计算是 n 平方,排序是 n log (n),但是您必须进行 n 次排序(对于 n 的每个值都不同)。凌乱的,但仍然是多项式时间来得到你的答案。
【讨论】:
【参考方案6】:您可以使用Morton Codes,但如果有 75 个维度,它们将会非常庞大。如果您只有 16,000 个数据点,那么详尽搜索应该不会花费太长时间。
【讨论】:
每个点有 16,000 次比较,因此总共有 16,000^2 次比较。它不会永远持续,但至少需要几个小时到几天。不过,我会阅读莫顿密码。 @Benno:啊,我假设您正在查看最近的邻居以获取到达点。如果您想按地区订购,那么 Morton Codes 可能是您的最佳选择,无论大小。你应该用谷歌搜索 Z 阶曲线,因为它有些奇怪。您可能想查看有关 GeoHash 的 Wikipedia 文章,该文章本质上是 Morton Code 的文本翻译。 (最后,请注意,所描述的 Morton 代码适用于 2 维,但您可以交错的维数没有真正的限制)以上是关于如何在高维数据中高效地找到k近邻?的主要内容,如果未能解决你的问题,请参考以下文章