高维数据中的最近邻?

Posted

技术标签:

【中文标题】高维数据中的最近邻?【英文标题】:Nearest neighbors in high-dimensional data? 【发布时间】:2011-08-10 16:51:29 【问题描述】:

几天前我问过question,如何找到给定向量的最近邻居。我的向量现在是 21 维,在我继续之前,因为我不是机器学习或数学领域的人,我开始问自己一些基本问题:

欧几里得距离是否是一个很好的指标来首先找到最近的邻居?如果没有,我有什么选择? 此外,如何确定确定 k 邻居的正确阈值?是否可以进行一些分析来计算出这个值? 以前,有人建议我使用 kd-Trees,但 Wikipedia 页面明确指出,对于高维,kd-Tree 几乎等同于蛮力搜索。在这种情况下,在百万点数据集中高效查找最近邻的最佳方法是什么?

有人可以澄清上述部分(或全部)问题吗?

【问题讨论】:

尝试在 metaoptimize.com 上询问 “高维”对于某些人和某些数据是 20,对于其他人是 50 或 100 或 1000。如果可以,请提供数字,例如“我已经使用 xx 完成了 21、1000000 个数据点的暗淡处理”。 kD-Tree 一次沿一个维度将数据一分为二。如果您有 20 个维度且只有 1M 个数据点,则您将获得大约 1 级树 - 其中级别意味着在每个轴上拆分。由于没有真正的深度,因此忽略树的分支不会带来好处。最好不要将其视为二叉树,而更像是四叉树、八叉树等,即使它的实现方式类似于二叉树。 @denis,Higgs 数据集是 'dim 21, 1000000 data points' 吗? 这里是下载希格斯数据集的链接。具有 28 个属性的 1100 万个观测值。最后一列是标签:1 表示信号,0 表示噪声。 archive.ics.uci.edu/ml/datasets/HIGGS 【参考方案1】:

不久前我有一个类似的问题。对于快速近似最近邻搜索,您可以使用来自 spotify 的 annoy 库:https://github.com/spotify/annoy

这是 Python API 的一些示例代码,在 C++ 中进行了优化。

from annoy import AnnoyIndex
import random

f = 40
t = AnnoyIndex(f, 'angular')  # Length of item vector that will be indexed
for i in range(1000):
    v = [random.gauss(0, 1) for z in range(f)]
    t.add_item(i, v)

t.build(10) # 10 trees
t.save('test.ann')

# ...

u = AnnoyIndex(f, 'angular')
u.load('test.ann') # super fast, will just mmap the file
print(u.get_nns_by_item(0, 1000)) # will find the 1000 nearest neighbors

它们提供不同的距离测量值。您要应用哪种距离测量在很大程度上取决于您的个人问题。还要首先考虑对某些维度的重要性进行预缩放(意思是加权)。这些维度或特征重要性权重可能通过熵损失之类的方法计算,或者如果您有监督学习问题 gini impurity gain 或 mean average loss,您可以在其中检查您的机器学习模型执行得有多差,如果您打乱这些维度值。

通常向量的方向比它的绝对值更重要。例如,在文本文档的语义分析中,我们希望文档向量在语义相似时接近,而不是长度相似。因此,我们既可以将这些向量归一化为单位长度,也可以使用角距离(即余弦相似度)作为距离度量。

希望这有帮助。

【讨论】:

【参考方案2】:

一一回答你的问题:

不,欧几里得距离在高维空间中是一个不好的度量。基本上在高维中,数据点之间存在很大差异。这会减小给定数据点与其最近和最远邻居之间距离的相对差异。 很多论文/研究都包含在高维数据中,但大部分内容都需要大量复杂的数学知识。 KD 树不适合高维数据...一定要避免

这是一篇很好的论文,可以帮助您朝着正确的方向开始。 “When in Nearest Neighbour meaningful?”拜尔等人。

我使用尺寸为 20K 及以上的文本数据。如果您需要一些与文字相关的建议,我或许可以为您提供帮助。

【讨论】:

+1 我正在打印那张纸以供阅读。同时,您对如何找出最近的邻居有什么建议吗?如果距离度量和邻居本身的定义都有缺陷,那么人们通常如何解决他们想要基于特征向量进行近似匹配的高维问题?有什么建议吗? 在文本的情况下,我们经常使用余弦相似度。我自己从事文本分类工作,发现对于高维度,带有线性内核的 SVM 似乎是最有效的。 @BiGYaN 你是如何定义你的空间的。我的意思是基于词向量的词向量或嵌入向量? @user3487667,空间取决于您如何表述您的问题。我说的是一个简单的词袋模型。【参考方案3】:

您所面临的就是curse of dimensionality。有时运行诸如 PCA 或 ICA 之类的算法会很有用,以确保您确实需要所有 21 个维度,并可能找到一个线性变换,这将允许您使用少于 21 个且结果质量大致相同.

更新: 我在 Rangayyan 的《生物医学信号处理》一书中遇到了它们(我希望我没记错)。 ICA 不是一项简单的技术,但它是由芬兰的研究人员开发的,我认为它的 Matlab 代码可以公开下载。 PCA 是一种使用更广泛的技术,我相信你应该能够找到它的 R 或其他软件实现。 PCA 是通过迭代求解线性方程来执行的。我很久以前做过,不记得怎么做的了。 = )

想法是将信号分解为独立的特征向量(实际上是离散的特征函数)及其特征值,在您的情况下为 21。每个特征值显示每个特征函数对每个测量值的贡献量。如果特征值很小,您可以非常接近地表示信号,而无需使用其相应的特征函数,这就是您摆脱维度的方式。

【讨论】:

+1 谢谢。这是一个非常有趣的建议,并且非常有意义。作为最后的要求,您是否熟悉任何解释如何以交互方式执行此操作的动手教程(使用 python 或 R 或​​其他语言)(我的意思是逐步解释整个过程)。从昨天开始,我已经阅读了一些文件,但其中大部分似乎都超出了我的理解范围。有什么建议吗? 吹毛求疵:ICA 不是降维算法。它不知道如何对组件进行评分,因此不应这样使用。【参考方案4】:

热门答案很好但很旧,所以我想添加一个 2016 年的答案


如前所述,在高维空间中,维度的诅咒潜伏在拐角处,这使得传统方法(例如流行的 k-d 树)变得像蛮力方法一样缓慢。因此,我们将兴趣转向了近似最近邻搜索 (ANNS),它有利于提高准确性,从而加快了这一过程。您可以很好地近似精确的 NN,并且具有很好的概率。


可能有价值的热门话题:

    LSH 的现代方法,例如Razenshteyn's。 RKD 森林:随机 k-d 树 (RKD) 的森林,如 FLANN 中所述, 或者在我参与的更新方法中,kd-GeRaF。 LOPQ 代表局部优化产品量化,如 here 所述。它与新的 Babenko+Lemptitsky 的approach 非常相似。

你也可以查看我的相关回答:

    Two sets of high dimensional points: Find the nearest neighbour in the other set Comparison of the runtime of Nearest Neighbor queries on different data structures PCL kd-tree implementation extremely slow

【讨论】:

【参考方案5】:

您可以尝试 z 阶曲线。 3维很容易。

【讨论】:

【参考方案6】:

欧几里得距离是首先找到最近邻居的好指标吗?如果没有,我有什么选择?

我建议软子空间聚类,这是当今非常常见的一种方法,通过计算特征权重来找到最相关的维度。例如,您可以在使用欧几里得距离时使用这些权重。有关常见问题,请参阅维度诅咒,本文也可以以某种方式启发您:

一种k-means类型的聚类算法,用于混合数值的子空间聚类和 分类数据集

【讨论】:

【参考方案7】:

KD 树适用于 21 维,如果你提前退出, 在查看了所有分数的 5% 之后。 FLANN 这样做(和其他加速) 匹配 128-dim SIFT 向量。 (不幸的是,FLANN 只做欧几里得度量, 又快又稳 scipy.spatial.cKDTree 只做 Lp 指标; 这些对于您的数据可能合适,也可能不合适。) 这里当然需要权衡速度和准确性。

(如果你能描述你的 Ndata、Nquery、数据分布, 这可能有助于人们尝试类似的数据。)

在我的旧 mac ppc 上添加了 4 月 26 日 cKDTree 的运行时间,以给出一个非常粗略的可行性概念:

kdstats.py p=2 dim=21 N=1000000 nask=1000 nnear=2 cutoff=1000 eps=0 leafsize=10 clustype=uniformp
14 sec to build KDtree of 1000000 points
kdtree: 1000 queries looked at av 0.1 % of the 1000000 points, 0.31 % of 188315 boxes; better 0.0042 0.014 0.1 %
3.5 sec to query 1000 points
distances to 2 nearest: av 0.131  max 0.253

kdstats.py p=2 dim=21 N=1000000 nask=1000 nnear=2 cutoff=5000 eps=0 leafsize=10 clustype=uniformp
14 sec to build KDtree of 1000000 points
kdtree: 1000 queries looked at av 0.48 % of the 1000000 points, 1.1 % of 188315 boxes; better 0.0071 0.026 0.5 %
15 sec to query 1000 points
distances to 2 nearest: av 0.131  max 0.245

【讨论】:

【参考方案8】:

我。距离度量

首先,数据集中的特征(列)数量不是选择用于 kNN 的距离度量的因素。有不少已发表的研究正是针对这个问题,通常比较的依据是:

基础统计 分发您的数据;

特征之间的关系 包含您的数据(它们是 独立的——即,什么是 协方差矩阵看起来像);和

你的坐标空间 得到了数据。

如果您事先不知道数据的抽样分布,那么至少有一个(有据可查且详尽的)study 得出结论,欧几里得距离是最佳选择。

在大型网络推荐引擎以及当前的学术研究中使用的 YEuclidean 度量。欧几里得计算的距离具有直观的意义和计算尺度——即欧几里得距离的计算方式相同,无论两点是在二维空间还是在二十二维空间。

对我来说它只失败了几次,每个案例欧几里得距离都失败了,因为底层(笛卡尔)坐标系是一个糟糕的选择。而且您通常会认识到这一点,因为例如路径长度(距离)不再是相加的 - 例如,当度量空间是棋盘时,曼哈顿距离比欧几里得更好,同样当度量空间是地球并且您的距离是反式时- 大陆航班,适合极坐标系的距离度量是个好主意(例如,伦敦到维也纳是 2.5 小时,维也纳到圣彼得堡是另外 3 小时,或多或少在同一方向,但伦敦到圣彼得堡. 圣彼得堡不是 5.5 小时,而是 3 小时多一点。)

但除了您的数据属于非笛卡尔坐标系的情况之外,距离度量的选择通常并不重要。 (参见一位CS学生的blog post,通过检查它们对kNN分类器的影响来比较几个距离度量——卡方给出了最好的结果,但差异并不大;更全面的研究在学术论文中,@987654323 @--Mahalanobis(本质上是欧几里得归一化以考虑维度协方差)是这项研究中最好的。

一个重要的附带条件:要使距离度量计算有意义,您必须重新缩放您的数据——很少有可能构建一个 kNN 模型来生成准确的没有这样做的预测。例如,如果您正在构建一个 kNN 模型来预测运动表现,并且您的期望变量是身高 (cm)、体重 (kg)、体脂 (%) 和静息脉搏(每分钟心跳数),那么典型的数据点可能看起来像这样:[ 180.4, 66.1, 11.3, 71 ]。显然,距离计算将以身高为主,而体脂百分比的贡献几乎可以忽略不计。换句话说,如果数据报告不同,体重以克而不是公斤为单位,那么 86.1 的原始值将是 86,100,这将对您的结果产生很大影响,这正是您所做的不想。可能最常见的缩放技术是减去均值并除以标准差(均值和 sd 是指为每列或该数据集中的特征单独计算的;X 是指数据行中的单个条目/单元格):

X_new = (X_old - mu) / sigma

二。数据结构

如果您关心 kd-tree 结构的性能,Voronoi Tessellation 是一个概念上简单的容器,但它会显着提高性能并比 kd-Tree 更好地扩展。

这不是保留 kNN 训练数据的最常用方法,尽管为此目的应用 VT 以及随之而来的性能优势已得到充分证明(例如,参见 Microsoft Research report)。这样做的实际意义在于,如果您使用的是“主流”语言(例如,在TIOBE Index 中),那么您应该找到一个库来执行 VT。我知道在 Python 和 R 中,每种语言都有多个选项(例如,CRAN 上提供的 R 的 voronoi 包)

对 kNN 使用 VT 的工作方式如下::

从您的数据中,随机选择 w 个点——这些是您的 Voronoi 中心。 Voronoi 单元封装了离每个中心最近的所有相邻点。想象一下,如果您为每个 Voronoi 中心分配不同的颜色,那么分配给给定中心的每个点都被涂上该颜色。只要你有足够的密度,这样做会很好地显示每个 Voronoi 中心的边界(作为分隔两种颜色的边界。

如何选择 Voronoi 中心?我使用两个正交指南。随机选择 w 个点后,计算训练数据的 VT。接下来检查分配给每个 Voronoi 中心的数据点的数量——这些值应该大致相同(给定数据空间中的均匀点密度)。在二维中,这将导致 VT 具有相同大小的图块。这是第一条规则,这是第二条规则。通过迭代选择 w——以 w 作为可变参数运行 kNN 算法,并测量性能(通过查询 VT 返回预测所需的时间)。

所以想象一下你有一百万个数据点.....如果这些点被保存在一个普通的二维数据结构中,或者在一个 kd-tree 中,你将平均为 每个执行几百万个距离计算 您希望预测其响应变量的新数据点。当然,这些计算是在单个数据集上执行的。对于 V/T,最近邻搜索分两步依次执行,针对两个不同的数据群——首先针对 Voronoi 中心,然后一旦找到最近的中心,单元格内的点对应于搜索该中心以找到实际最近的邻居(通过连续的距离计算)结合起来,这两个查找比单个蛮力查找要快得多。这很容易看出:对于 1M 数据点,假设您选择 250 个 Voronoi 中心来细分您的数据空间。平均而言,每个 Voronoi 单元将有 4,000 个数据点。因此,您无需执行平均 500,000 次距离计算(蛮力),而是执行得更少,平均只需 125 + 2,000 次。

三。计算结果(预测的响应变量)

从一组 kNN 训练数据中计算预测值有两个步骤。第一个是识别 n,或 最近邻的数量,以用于此计算。第二个是如何衡量他们对预测值的贡献

W/r/t 第一个组件,您可以通过解决优化问题(非常类似于最小二乘优化)来确定 n 的最佳值。这就是理论;在实践中,大多数人只使用 n=3。无论如何,在 n=1、n=2、n=3 等的一组测试实例(以计算预测值)上运行 kNN 算法并将误差绘制为 n 的函数很简单。如果您只是想要一个合理的 n 值来开始,那么再次使用 n = 3。

第二个部分是如何加权每个邻居的贡献(假设 n > 1)。

最简单的加权技术只是将每个邻居乘以一个加权系数,这只是 1/(dist * K),或者是从该邻居到测试实例的距离的倒数,通常乘以一些经验得出的常数, K. 我不喜欢这种技术,因为它经常过度加权最近的邻居(同时降低更远的邻居的权重);这样做的意义在于,给定的预测几乎可以完全依赖于单个邻居,这反过来又增加了算法对噪声的敏感度。

高斯函数是一个必须更好的加权函数,它基本上避免了这个限制,它在python中看起来像这样:

def weight_gauss(dist, sig=2.0) :
    return math.e**(-dist**2/(2*sig**2))

要使用您的 kNN 代码计算预测值,您需要确定与您希望预测其响应变量的数据点(“测试实例”)的 n 个最近邻点,然后调用 weight_gauss 函数,每个 n 个邻居,传递每个邻居与测试点之间的距离。该函数将返回每个邻居的权重,然后将其用作加权平均计算中该邻居的系数。

【讨论】:

很好的答案!根据我的经验,全面而准确。 不错的答案,+1,我添加了一个新的更新的答案here,好吗? "假设你有一百万个数据点.....如果这些点被保存在一个普通的 2D 数据结构中,或者一个 kd-tree,你会为您希望预测其响应变量的每个新数据点平均执行 几百万 次距离计算。”不同意。可以证明,KD-trees 在二维中具有O(sqrt(n)) 搜索复杂度。【参考方案9】:

我也遇到过同样的问题,可以这么说。

    欧几里得距离是一个很好的距离度量,但它在计算上比Manhattan distance 更昂贵,有时会产生稍差的结果,因此,我会选择后者。

    k 的值可以根据经验找到。您可以尝试不同的值并检查生成的 ROC curves 或其他一些精度/召回度量,以找到可接受的值。

    欧几里得距离和曼哈顿距离都尊重Triangle inequality,因此您可以在度量树中使用它们。事实上,当数据超过 10 个维度时,KD 树的性能会严重下降(我自己也遇到过这个问题)。我发现VP-trees 是一个更好的选择。

【讨论】:

【参考方案10】:

我目前正在研究此类问题——分类、最近邻搜索——用于音乐信息检索。

您可能对近似最近邻ANN)算法感兴趣。这个想法是您允许算法返回足够的近邻(可能不是最近的邻居);这样做可以降低复杂性。你提到了kd-tree;这是一个例子。但正如你所说,kd-tree 在高维度上效果不佳。事实上,所有当前的索引技术(基于空间分区)都退化为线性搜索以获取足够高的维度 [1][2][3]。

在最近提出的 ANN 算法中,也许最流行的是 Locality-Sensitive Hashing (LSH),它在将高维空间放入一组 bin,即哈希表 [1][3]。但与传统哈希不同,locality-sensitive 哈希将 nearby 点放入同一个 bin。

LSH 有一些巨大的优势。首先,它很简单。您只需计算数据库中所有点的哈希值,然后从中创建一个哈希表。要查询,只需计算查询点的哈希,然后从哈希表中检索同一 bin 中的所有点。

其次,有一个严格的理论支持其性能。可以看出,查询时间在数据库大小上是次线性的,即比线性搜索快。快多少取决于我们可以容忍多少近似值。

最后,LSH0 < p <= 2 的任何 Lp 规范兼容。因此,要回答您的第一个问题,您可以将 LSH 与欧几里德距离度量一起使用,或者您可以将其与曼哈顿 (L1) 距离度量一起使用。汉明距离和余弦相似度也有变体。

Malcolm Slaney 和 Michael Casey 于 2008 年为 IEEE 信号处理杂志 [4] 撰写了一篇不错的概述。

LSH 似乎无处不在。您可能想尝试一下。


[1] Datar, Indyk, Immorlica, Mirrokni,“基于 p 稳定分布的局部敏感散列方案”,2004 年。

[2] Weber, Schek, Blott,“高维空间中相似性搜索方法的定量分析和性能研究”,1998 年。

[3] Gionis, Indyk, Motwani,“通过散列进行高维相似性搜索”,1999 年。

[4] Slaney, Casey,“用于查找最近邻居的局部敏感散列法”,2008 年。

【讨论】:

@Steve:谢谢你的回复。你对 LSH 的实施有什么建议吗?我唯一看到的是麻省理工学院的那个。是否还有其他包裹漂浮? 除此之外,不,我不知道其他人。为了我的特定目的,我最终用 Python 编写了自己的代码。本质上,每个哈希表都实现为 Python 字典 d,其中 d[k] 是一个带有键 k 的 bin。 d[k] 包含哈希为k 的所有点的标签。然后,您只需要计算每个点的哈希值。见方程式。 [4] 中的 (1) 或 [1] 中的第 3 节。 @Steve:感谢您的帮助。我现在将开始实施它。您对这种方法在大型数据集上的表现有任何想法吗? 另一个支持 LSH 的参考资料:比较高维空间中的最近邻算法,Hendra Gunadi,2011。cs.anu.edu.au/student/projects/11S2/Reports/Hendra%20Gunadi.pdf @SteveTjoa:发现很难直观地掌握关键字和嵌入式公式。因为你已经有一个关于 LSH 的亮点,我补充了它。只有最好的意图。不过,请随意恢复。毕竟是你的答案。 :)【参考方案11】:

iDistance 可能是高维数据中精确 knn 检索的最佳选择。您可以将其视为近似的 Voronoi 镶嵌。

【讨论】:

【参考方案12】:

kd-trees 确实不能很好地处理高维数据。因为修剪步骤不再有很大帮助,因为最近的边缘 - 一维偏差 - 几乎总是小于与已知最近邻居的全维偏差。

但此外,据我所知,kd-trees 仅适用于 Lp 范数,并且存在距离集中效应,使得基于距离的算法随着维度的增加而退化。

有关更多信息,您可能需要阅读维度诅咒及其各种变体(它不止一个方面!)

我不相信盲目地近似欧几里得最近邻有很多用处,例如使用 LSH 或随机投影。首先可能需要使用更精细的距离函数!

【讨论】:

你的第一段和第二段有参考资料吗? 不,但是从通常的“维度诅咒”实例化中它们应该是相当明显的(参见survey)并尝试找到任何支持欧几里得以外的任何东西的kd树......支持其他距离是可能的,但不常见(ELKI 允许所有 Minkowski 距离 + 平方欧几里得,但大多数只有欧几里得)。只需考虑 k-d-trees 仅使用 one 维度进行修剪,并将其与涉及 all 维度的距离进行比较。另外,您的拆分将无法在每个维度上拆分。【参考方案13】:

我认为布尔特征的tf-idf 上的余弦对于大多数问题都适用。这是因为它在许多搜索引擎(如 Lucene)中使用了久经考验的启发式方法。根据我的经验,欧几里得距离对于任何类似文本的数据都显示不好的结果。可以通过训练数据和蛮力参数选择来选择不同的权重和 k-example。

【讨论】:

【参考方案14】:

余弦相似度是比较高维向量的常用方法。请注意,由于它是相似性而不是距离,因此您希望最大化它而不是最小化它。您还可以使用特定领域的方式来比较数据,例如,如果您的数据是 DNA 序列,您可以使用考虑突变概率等的序列相似性。

要使用的最近邻的数量取决于数据的类型、噪音的大小等。没有一般规则,您只需通过尝试所有值来找到最适合您的特定数据和问题的方法一个范围内。人们有一个直观的认识,即数据越多,需要的邻居就越少。在您拥有所有可能数据的假设情况下,您只需要寻找单个最近邻进行分类。

已知 k 最近邻方法的计算量很大。这是人们转向支持向量机等其他算法的主要原因之一。

【讨论】:

这很有趣。您能否详细说明如何在我的案例中使用 SVM?我认为 k 最近邻更像是无监督的,而 SVM 是有监督的。如果我错了,请纠正我。 两种方法都受到监督,因为您的训练数据使用正确的类进行了注释。如果你只有特征向量,不知道它们所属的类,那么你就不能使用 kNN 或 SVM。无监督学习方法通​​常被称为聚类算法。他们可以识别相似数据的组,但不会告诉您这些组的含义。 感谢您的澄清。你说的对。这确实是一种有监督的技术。我只是没有意识到我所谓的类别实际上也是类:)【参考方案15】:

很大程度上取决于您为什么想知道最近的邻居。如果您真正想要的是找到数据集的模式,您可以查看均值偏移算法http://en.wikipedia.org/wiki/Mean-shift。

【讨论】:

据我所知,Mean-Shift 不适合对高维数据进行聚类。 K-Means 可能是更好的选择。

以上是关于高维数据中的最近邻?的主要内容,如果未能解决你的问题,请参考以下文章

数据集 Node.js 中的最近邻

从几何角度切入最近邻

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

使用 R 中的最近邻分类器分配类标签

给定距离矩阵的 Python 中的最近邻

为集合 A 中的所有点查找集合 B 中的最近邻的算法