【问题标题】:Wrap-around when calculating distance for k-means计算 k-means 距离时的环绕
【发布时间】:2013-09-12 10:08:06
【问题描述】:

我正在尝试使用 sklearn 对某些数据集进行 K 均值聚类。问题是其中一个维度是一天中的小时:一个从 0 到 23 的数字,因此距离算法认为 0 离 23 很远,因为从绝对意义上来说它是。实际上,出于我的目的,0 小时非常接近 23 小时。有没有办法让距离算法进行某种形式的环绕,以便计算更“真实”的时差。 我正在做一些简单的事情,类似于以下内容:

from sklearn.cluster import KMeans

clusters = KMeans(n_clusters = 2)
data = vstack(data)
fit = clusters.fit(data)
classes = fit.predict(data)

data 元素类似于[22, 418, 192],其中第一个元素是小时。

有什么想法吗?

【问题讨论】:

    标签: python machine-learning cluster-analysis classification


    【解决方案1】:

    即使@elyase 的答案被接受,我认为它不是正确的方法。

    是的,要使用这样的距离,你必须改进你的距离测量,所以 - 使用不同的库。但更重要的是 - k-means 中使用的 mean 概念不适合循环维度。让我们考虑以下示例:

    #current cluster X,, based on centroid position Xc=24
    x1=1
    x2=24
    
    #current cluster Y, based on centroid position Yc=10
    y1=12
    y2=13
    

    计算简单算术平均值会将质心放在Xc=12.5,Yc=12.5,从循环测量的角度来看是不正确的,它应该是Xc=0.5,Yc=12.5。如您所见,基于循环距离度量的分配与简单的均值运算不“兼容”,并导致奇怪的结果。

    • 简单的 k-means 将产生簇 {x1,y1}, {x2,y2}
    • 简单的k--means +距离测量导致退化的超级集群{x1,x2,y1,y2}
    • 正确的聚类应该是{x1,x2},{y1,y2}

    解决此问题需要检查一个 if(是否更好地测量“简单平均值”或将其中一个点表示为 x'=x-24)。不幸的是,鉴于n 点,它使2^n 成为可能。

    这似乎是 kernelized k-means 的用例,您实际上是在抽象特征空间中进行聚类(在您的情况下 - 围绕时间维度滚动的“管”)内核(“相似性度量”,是某个向量空间的内积)。

    kernel k-means的详细信息在here

    【讨论】:

    • kkmeans 中的什么内核实现了这个管维度?
    【解决方案2】:

    为什么 k-means 不适用于任意距离

    K-means 不是基于距离的算法。

    K-means 最小化簇内平方和,这是一种方差(它大致是所有簇的加权平均方差,其中每个对象和维度都被赋予相同的权重)。

    为了使 Lloyds 算法收敛,您需要两个步骤优化相同的功能:

    • 重新分配步骤
    • 质心更新步骤

    现在“均值”函数是一个最小二乘估计器。 IE。在步骤 2 中选择平均值对于 WCSS 目标是最佳的。在步骤 1 中通过最小二乘偏差(= 平方欧几里得距离,单调到欧几里得距离)分配对象可以保证收敛。 平均值就是你的环绕式想法会崩溃的地方

    如果您按照@elyase 的建议插入随机的其他距离函数k-means 可能不再收敛

    适当的解决方案

    对此有多种解决方案:

    • 使用 K-medoids (PAM)。通过选择 medoid 而不是平均值,您确实可以保证在任意距离上收敛。但是,计算中心点相当昂贵。
    • 将数据转换为内核空间,您可以在该空间中最小化平方和。例如,您可以将小时转换为 sin(hour / 12 * pi), cos(hour / 12 * pi),这对于 SSQ 来说可能没问题。
    • 使用其他基于距离的聚类算法。 K-means 很老,从那时起就有很多关于聚类的研究。您可能想从层次聚类(实际上与 k-means 一样古老)开始,然后尝试 DBSCAN 及其变体。

    【讨论】:

    • 这一切都说得通。我天真地认为使用圆柱距离而不是欧几里得可以产生适当的结果。在接受作为答案之前,我会详细了解您的建议。谢谢!
    【解决方案3】:

    对我来说,最简单的方法是通过计算维度的"circular mean" 来调整 K-means 算法环绕维度。当然,您还需要相应地更改到质心的距离计算。

    #compute the mean of hour 0 and 23
    import numpy as np
    hours = np.array(range(24))
    #hours to angles
    angles = hours/24 * (2*np.pi)
    
    sin = np.sin(angles)
    cos = np.cos(angles)
    
    a = np.arctan2(sin[23]+sin[0], cos[23]+cos[0])
    if a < 0: a += 2*np.pi
    
    #angle back to hour
    hour = a * 24 / (2*np.pi)
    #23.5
    

    【讨论】:

      猜你喜欢
      • 2013-02-02
      • 2018-04-06
      • 1970-01-01
      • 2013-04-22
      • 2018-12-06
      • 2017-03-13
      • 1970-01-01
      • 2012-04-09
      • 2019-06-01
      相关资源
      最近更新 更多