为啥我的 k-means 收敛条件给出的结果与 sklearn 不同?

Posted

技术标签:

【中文标题】为啥我的 k-means 收敛条件给出的结果与 sklearn 不同?【英文标题】:Why does my k-means convergence condition give different results than sklearn?为什么我的 k-means 收敛条件给出的结果与 sklearn 不同? 【发布时间】:2022-01-15 00:54:56 【问题描述】:

我编写了一个函数,它使用 dataKn(迭代次数)作为输入来执行 k-means 聚类。我可以设置n=100,以便该函数计算欧几里得距离、分配集群并在完成之前计算新的集群质心 100 次。当将此结果与 sklearn 的 KMeans(使用相同的 dataK)进行比较时,我得到了相同的质心。

我希望我的函数只运行到收敛(以避免不必要的迭代),因此它不再需要迭代次数作为输入。为了实现这一点,我使用了一个 while 循环,而在我的原始函数中,我使用了一个迭代 n 次的 for 循环。我的代码的一般步骤如下:

    随机创建质心并存储在变量centroids中。 计算每个数据点与上面生成的质心的欧几里得距离,并存储在变量EucDist中。 根据来自centroidsEucDist 为每个数据点分配cluster。 根据集群索引cluster重新组合数据点。然后计算分离集群的平均值并将其分配为新的centroids

然后循环步骤 2-4 直到收敛。下面是我的 while 循环的代码(我知道这里可能有一些不必要的代码,但是我不确定问题出在哪里,如果它甚至是一个问题)。

while True:

    previouscentroids = centroids
    EucDist = np.array([]).reshape(a, 0)

    for k in range(K):
        Dist = np.sqrt(np.sum((data - centroids[:, k]) ** 2, axis = 1))
        EucDist = np.c_[EucDist, Dist]
    
    cluster = np.argmin(EucDist, axis = 1) + 1
    
    points = 
    for k in range(K):
        points[k + 1] = np.array([]).reshape(2, 0)
    for i in range(a):
        points[cluster[i]] = np.c_[points[cluster[i]], data[i]]
     
    for k in range(K):
        points[k + 1] = points[k + 1].T
        
    for k in range(K):
        centroids[:, k] = np.mean(points[k + 1], axis = 0)
        
    if np.all(previouscentroids - centroids == 0):
        break

我对此的理解是,一旦 centroids 与迭代开始时 (previouscentroids) 相同,循环就会中断,我将拥有最后的集群。我假设这与我的原始函数产生的质心相同(与 sklearn 相同),因为我认为一旦达到收敛,无论你迭代循环多少次,集群都将保持不变(因此将质心)。下面是我原始函数中的 for 循环进行比较(几乎没有改变)。

for i in range(n):
    
    EucDist = np.array([]).reshape(a, 0)

    for k in range(K):
        Dist = np.sqrt(np.sum((data - centroids[:, k]) ** 2, axis = 1))
        EucDist = np.c_[EucDist, Dist]
        
    cluster = np.argmin(EucDist, axis = 1) + 1
    
    points = 
    for k in range(K):
        points[k + 1] = np.array([]).reshape(2, 0)
    for i in range(a):
        points[cluster[i]] = np.c_[points[cluster[i]], data[i]]
     
    for k in range(K):
        points[k + 1] = points[k + 1].T
    
    for k in range(K):
        centroids[:, k] = np.mean(points[k + 1], axis = 0)

【问题讨论】:

【参考方案1】:

K-means 是一种非确定性算法。如果您没有与 scikit-learn 中相同的初始化,则不确定是否找到相同的集群。在 scikit-learn 文档中是这样写的:

如果有足够的时间,K-means 将始终收敛,但这可能会达到局部最小值。这高度依赖于质心的初始化。因此,计算通常会进行多次,并且对质心进行不同的初始化。

【讨论】:

【参考方案2】:

Kmeans 每次运行时都会发生变化,因为初始的集群中心是随机选择的。因此,除非您具有完全相同的初始集群中心,否则结果将不会相同。

【讨论】:

以上是关于为啥我的 k-means 收敛条件给出的结果与 sklearn 不同?的主要内容,如果未能解决你的问题,请参考以下文章

K-Means聚类若干问题

K-means聚类算法java实现

无论集群中心如何初始化,Kmeans 算法是不是都能保证收敛?为啥?

K-means聚类算法

为啥 k-means 聚类散点图显示不同的结果?

K-means算法的缺点