【问题标题】:Using K-means with cosine similarity - Python使用具有余弦相似度的 K-means - Python
【发布时间】:2018-03-06 16:43:42
【问题描述】:


我正在尝试在 python 中实现Kmeans 算法,它将使用cosine distance 而不是欧几里得距离作为距离度量。
我知道使用不同的距离函数可能是致命的,应该谨慎行事。使用余弦距离作为度量迫使我改变平均函数(根据余弦距离的平均值必须是归一化向量的逐元素平均值)。

我见过this手动覆盖sklearn的距离函数的优雅解决方案,我想使用相同的技术来覆盖代码的平均部分但我找不到它。

有谁知道怎么办?
距离度量不满足三角不等式有多重要?
如果有人知道我使用余弦度量或满足距离和平均函数的 kmeans 的不同有效实现,那也将非常有帮助。
非常感谢!

编辑:
使用角距离而不是余弦距离后,代码看起来像这样:

def KMeans_cosine_fit(sparse_data, nclust = 10, njobs=-1, randomstate=None):
    # Manually override euclidean
    def euc_dist(X, Y = None, Y_norm_squared = None, squared = False):
        #return pairwise_distances(X, Y, metric = 'cosine', n_jobs = 10)
        return np.arccos(cosine_similarity(X, Y))/np.pi
    k_means_.euclidean_distances = euc_dist
    kmeans = k_means_.KMeans(n_clusters = nclust, n_jobs = njobs, random_state = randomstate)
    _ = kmeans.fit(sparse_data)
    return kmeans

我注意到(通过数学计算)如果向量被归一化,则标准平均值适用于角度度量。据我了解,我必须将_mini_batch_step() 更改为k_means_.py。但是这个功能非常复杂,我不明白该怎么做。
有人知道替代解决方案吗?
或者,有谁知道如何使用始终强制质心标准化的函数来编辑此函数?

【问题讨论】:

  • 看看 scikit-learn 源代码中的k_means_.py。您链接到的余弦距离示例只不过是用自定义函数替换 k_means_ 模块中名为 euclidean_distance 的函数变量。如果您发布您的 k-means 代码以及您想要覆盖的功能,我可以给您一个更具体的答案。但是如果你想自己做,只需在k_means_源中查找平均函数的名称并替换它即可。
  • 此外,一般而言,SO 问题应包含minimal, complete, viable example - 如果您包含要修改的代码或不起作用的代码,您可以获得更多帮助。
  • @charlesreid1 谢谢,我添加了代码。我的问题是我还没有完全理解k_means_.py 中的平均函数是如何工作的,因此我无法理解如何更改它。
  • 有一个名为 spherecluster 的 python 包在球体上实现了 K-means 算法(因此它与您尝试做的事情基本相同)。

标签: python scikit-learn k-means cosine-similarity sklearn-pandas


【解决方案1】:

很遗憾,没有。 Sklearn 当前的 k-means 实现只使用欧几里得距离。

原因是K-means包含了寻找聚类中心并将样本分配到最近的中心的计算,而欧几里得只有样本中的中心的含义。

如果要使用具有余弦距离的 K-means,则需要制作自己的函数或类。或者,尝试使用其他聚类算法,例如 DBSCAN。

【讨论】:

    【解决方案2】:

    所以事实证明,您可以将 X 归一化为单位长度并正常使用 K-means。原因是如果 X1 和 X2 是单位向量,看下面的等式,最后一行括号内的项是余弦距离。

    因此,就使用 k-means 而言,只需这样做:

    length = np.sqrt((X**2).sum(axis=1))[:,None]
    X = X / length
    
    kmeans = KMeans(n_clusters=10, random_state=0).fit(X)
    

    如果您需要质心和距离矩阵,请执行以下操作:

    len_ = np.sqrt(np.square(kmeans.cluster_centers_).sum(axis=1)[:,None])
    centers = kmeans.cluster_centers_ / len_
    dist = 1 - np.dot(centers, X.T) # K x N matrix of cosine distances
    

    注意事项:

    • 刚刚意识到您正试图最小化集群的平均向量与其组成部分之间的距离。当您简单地平均向量时,平均向量的长度小于 1。但在实践中,仍然值得运行正常的 sklearn 算法并检查平均向量的长度。在我的情况下,平均向量接近单位长度(平均约为 0.9,但这取决于数据的密集程度)。 TLDR:正如@σηγ 指出的那样,使用spherecluster 包。

    【讨论】:

    • 我们朋友在 Cross Validated 上的相关讨论 --> stats.stackexchange.com/a/146279/243511
    • 如果你使用sklearn.feature_extraction.text.TfidfVectorizer,似乎默认应用了L2归一化,即vectorizer的输出已经归一化了。
    【解决方案3】:

    您可以规范化您的数据,然后使用 KMeans。

    from sklearn import preprocessing
    from sklearn.cluster import KMeans
    
    kmeans = KMeans().fit(preprocessing.normalize(X))
    

    【讨论】:

      猜你喜欢
      • 2021-05-12
      • 2017-06-25
      • 1970-01-01
      • 2014-05-08
      • 2020-08-12
      • 1970-01-01
      • 2016-03-30
      • 2011-01-01
      相关资源
      最近更新 更多