最近邻中的距离是衡量相似性的好方法吗?

数据挖掘 机器学习 Python scikit-学习 距离 k-nn
2021-09-24 06:09:27

让我们训练一个只包含一个样本的最近邻模型:

In [48]: nn = NearestNeighbors().fit([[0, 1, 0, 0]])

所以这个样本只有一个显着特征。查询具有相同样本的模型按预期返回第一个数组中的 0 距离:

In [50]: nn.kneighbors([[0, 1, 0, 0]], 1)
Out[50]: (array([[0.]]), array([[0]]))

但是样本为 [0,2,0,0] 和 [0,1,1,0] 的查询都返回相同的距离值 1:

In [51]: nn.kneighbors([[0, 2, 0, 0]], 1)
Out[51]: (array([[1.]]), array([[0]]))

In [52]: nn.kneighbors([[0, 1, 1, 0]], 1)
Out[52]: (array([[1.]]), array([[0]]))

这是违反直觉的,因为人们会期望 [0,2,0,0] 与 [0,1,0,0] 比 [0,1,1,0] 更相似。使用 Jaccard 度量可以稍微改善这个问题:

In [56]: nn = NearestNeighbors(metric=scipy.spatial.distance.jaccard).fit([[0, 1, 0, 0]])

In [57]: nn.kneighbors([[0, 1, 0, 0]], 1)
Out[57]: (array([[0.]]), array([[0]]))

In [58]: nn.kneighbors([[0, 2, 0, 0]], 1)
Out[58]: (array([[1.]]), array([[0]]))

In [59]: nn.kneighbors([[0, 1, 1, 0]], 1)
Out[59]: (array([[0.5]]), array([[0]]))

但是对于我的数据集,Jaccard 度量使得 kNN 查询需要很长时间,也许它更适合二进制特征。我有一组来自每行 52 个传感器的读数,用零均值很好地标准化。当我将这个集合安装到 sklearn.neighbors.NearestNeighbors 并查询将训练集的第一行作为样本并且 K=2 时,我偶然发现了这个问题,因此它返回了预期距离为 0 处的第 0 个索引和其他一些 0.02 的索引距离。当我检查另一个时,我看不到任何相似之处,实际上大多数功能在值和/或符号方面非常不同。我可以从训练集中的第一行的组成样本中获得相同的距离,其中任何一个特征都增加了 0.02。

我现在想知道如何克服这个问题,以及是否有一种简单的方法(即通过调整 NearestNeighbors 的参数)或一种 hacky 方法(即自定义指标、特征权重等)或者我应该使用不同的模型?例如,KMeans 可以非常快地从我的数据集中聚合集群,但它在内部使用 NN,由于 init 中的随机性,我并不完全喜欢它。

1个回答

这是违反直觉的,因为人们会期望 [0,2,0,0] 与 [0,1,0,0] 比 [0,1,1,0] 更相似。

不,这是意料之中的,因为这两个点在欧几里得空间中的距离完全相同。要查看它需要简化的 2D 版本的点:

  • 一个 (1,0)
  • 乙 (2,0)
  • C (1,1)

B 和 C 都与 A 的距离正好为 1。

但是对于我的数据集,Jaccard 度量使得 kNN 查询需要很长时间,也许它更适合二进制特征。

Jaccard 实际上假设二进制特征,它将以相同的方式考虑所有非零值。其结果基于两个点共有多少个非零维度。我假设实现遵循原始定义,可能会有变体。通常这是一个非常简单的度量,不需要任何繁重的计算,所以它需要很长时间是令人惊讶的。

当我检查另一个时,我看不到任何相似之处,实际上大多数功能在值和/或符号方面非常不同。符号的变化在欧几里得空间中并不特别重要,重要的是距离。

那么真正检查的唯一方法是计算欧几里得距离,52 维仅查看值并不能给出很好的指示。

我可以从训练集中的第一行的组成样本中获得相同的距离,其中任何一个特征都增加了 0.02。

我不确定我是否理解这部分,但这听起来完全正确:将任何特征更改 0.02 会将空间中的数据点移动 0.02 的距离,所以......它将与原始点的距离为 0.02 .

我现在想知道如何克服这个问题,以及是否有一种简单的方法(即通过调整 NearestNeighbors 的参数)或一种 hacky 方法(即自定义指标、特征权重等)

如果有任何特定于数据的方法来测量两点之间的距离,您绝对应该定义一个自定义指标。这不是 hack,因为您可以看到 NN 完全依赖于指标。默认欧几里得距离是一种通用度量,它可能不适合您的目的。