Scikit Learn - K-Means - Elbow - criterion

54

今天我正在尝试学习关于K-means的一些东西。我已经理解了算法并知道它如何工作。现在我正在寻找正确的k值...我发现肘部准则是一种检测正确k值的方法,但我不知道如何在scikit learn中使用它?!在scikit learn中,我是用这种方式对事物进行聚类

kmeans = KMeans(init='k-means++', n_clusters=n_clusters, n_init=10) 
kmeans.fit(data)

那我是不是应该对于 n_clusters = 1...n 这些值多次执行此操作,并观察误差率以确定正确的 k 值?但是这样做很愚蠢,而且需要很长时间!

3个回答

85

如果事先不知道真实标签(就像您的情况一样),则可以使用拐点准则或轮廓系数来评估 K-Means 聚类

拐点准则方法:

拐点法背后的思想是在给定数据集上运行 k-means 聚类,对于一系列 k 值(例如 k=1 到 10),为每个 k 值计算平方误差和(SSE)。

然后,绘制每个 k 值的 SSE 的线图。如果线图呈现出手臂状 - 在下面的线图中表示为红色圆圈(如角度)- 手臂上的"拐点"即为最佳 k(聚类数)的值。

这里,我们希望最小化 SSE。随着增加 k,SSE 倾向于逐渐减少并趋近于 0(当 k 等于数据集中的数据点数时,SSE 为 0,因为每个数据点都是其自身的聚类,与其聚类中心之间没有误差)。

因此,目标是选择一个较小的 k 值,仍具有较低的 SSE,而拐点通常表示通过增加 k 值获得收益递减的位置。

让我们考虑鸢尾花数据集,

import pandas as pd
from sklearn.datasets import load_iris
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt

iris = load_iris()
X = pd.DataFrame(iris.data, columns=iris['feature_names'])
#print(X)
data = X[['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)']]

sse = {}
for k in range(1, 10):
    kmeans = KMeans(n_clusters=k, max_iter=1000).fit(data)
    data["clusters"] = kmeans.labels_
    #print(data["clusters"])
    sse[k] = kmeans.inertia_ # Inertia: Sum of distances of samples to their closest cluster center
plt.figure()
plt.plot(list(sse.keys()), list(sse.values()))
plt.xlabel("Number of cluster")
plt.ylabel("SSE")
plt.show()

上述代码的图表如下: enter image description here

在图表中,我们可以看到对于鸢尾花数据集,“3”是最佳聚类数(用红圈圈出来),这是正确的。



轮廓系数法:

sklearn 文档 中可以得知,轮廓系数越高,表示聚类效果越好。轮廓系数被定义为每个样本点的两个分数之差: `

a:一个样本与同一类中所有其他样本的平均距离。

b:一个样本与其它最近的类中所有样本的平均距离。

单个样本点的轮廓系数定义如下:

现在,为了找到适合 KMeans 的最优值 k,需要在 1..n 中循环 n_clusters,并为每个样本计算轮廓系数。

更高的轮廓系数表明对象与其自身聚类匹配良好,但与相邻聚类匹配差。

from sklearn.metrics import silhouette_score
from sklearn.datasets import load_iris
from sklearn.cluster import KMeans

X = load_iris().data
y = load_iris().target
   
for n_cluster in range(2, 11):
    kmeans = KMeans(n_clusters=n_cluster).fit(X)
    label = kmeans.labels_
    sil_coeff = silhouette_score(X, label, metric='euclidean')
    print("For n_clusters={}, The Silhouette Coefficient is {}".format(n_cluster, sil_coeff))

输出 -

对于n_clusters=2,轮廓系数为0.680813620271。
对于n_clusters=3,轮廓系数为0.552591944521。
对于n_clusters=4,轮廓系数为0.496992849949。
对于n_clusters=5,轮廓系数为0.488517550854。
对于n_clusters=6,轮廓系数为0.370380309351。
对于n_clusters=7,轮廓系数为0.356303270516。
对于n_clusters=8,轮廓系数为0.365164535737。
对于n_clusters=9,轮廓系数为0.346583642095。
对于n_clusters=10,轮廓系数为0.328266088778。

从这些结果可以看出,n_clusters=2具有最高的轮廓系数。这意味着2应该是最优数量的簇点,对吗?

但问题在于:

Iris数据集有3种花的品种,这与将2作为最优簇点数相矛盾。因此,尽管n_clusters=2具有最高的轮廓系数,由于以下原因,我们会考虑n_clusters=3作为最优簇点数:

  1. Iris数据集有3种品种。(最重要的)
  2. n_clusters=3具有第二高的轮廓系数。

因此,选择n_clusters=3是Iris数据集的最佳聚类数量。

选择最优聚类数量将取决于数据集的类型和我们试图解决的问题。但在大多数情况下,选择最高的轮廓系数将产生最优簇点数。

希望这有所帮助!


2
但是在你的例子中,n=2不是更高的轮廓系数吗? - mattdeak
1
感谢@mattdeak指出。在这种情况下,选择n=3将是最好的选择,因为鸢尾花数据集有三个物种,但同时轮廓系数并不是最高的。这似乎与更高的轮廓系数会导致最佳聚类数的事实相矛盾。您可以尝试间隙统计法 - Om Prakash
请将 n_cluster 设置为最优值 3。谢谢。 - Yogesh
我正在尝试计算轮廓分数,但出现了这个错误:ValueError: Number of labels is 1. Valid values are 2 to n_samples - 1 (inclusive)。有什么想法,可能是什么原因呢?我也已经在这个问题上提出了一个问题https://dev59.com/rlUK5IYBdhLWcg3wuB54。 - Suhail Gupta
事实上,在我的数据集中,最好的轮廓系数是3,但实际上只有两个类别。 - keramat
感谢您详细解释,非常有帮助,请继续保持良好的工作@OmPrakash - Arpan Saini

36

肘部准则是一种视觉方法。我还没有见过其强有力的数学定义。 但k-means也是一种相当粗糙的启发式算法。

因此,是的,您需要运行k=1...kmax的k-means,然后绘制结果的SSQ,并决定出一个“最佳”的k。

存在高级版本的k-means,例如X-means,它将从k=2开始,然后增加它,直到次要标准(AIC/BIC)不再改进。双分k-means是一种方法,它也从k=2开始,然后重复分裂群集,直到k=kmax。您可能可以从中提取中间SSQ。

无论哪种方式,在任何实际用例中,k-mean真的很好用,您应该事先知道所需的k。在这些情况下,k-means实际上并不是“聚类”算法,而是向量量化算法。例如,将图像的颜色数量减少到k。(通常会选择k为32,因为那时5位颜色深度,并且可以以比特压缩方式存储)。或者例如在视觉词袋方法中,您将手动选择词汇表大小。流行的值似乎是k=1000。然后,您并不真正关心“聚类”的质量,而主要目的是能够将图像减少为一个1000维的稀疏向量。 900维或1100维表示的性能差异不会很大。

对于实际的聚类任务,即当您想要手动分析结果集时,人们通常使用比k-means更高级的方法。K-means更多的是一种数据简化技术。


4
这个答案受到OmPrakash所写的启发。这包含了绘制SSE和Silhouette分数的代码。我提供的是一个通用的代码片段,您可以在所有无监督学习的情况下使用它,其中您没有标签并且想要知道最优聚类的数量。有两个标准。1)平方误差和(SSE)和Silhouette分数。您可以参考OmPrakash的答案进行解释。他在这方面做得很好。
假设您的数据集是数据框df1。这里我使用了不同的数据集,只是为了展示如何使用这两个标准来确定最佳的聚类数量。这里我认为6是正确的聚类数量。然后
range_n_clusters = [2, 3, 4, 5, 6,7,8]
elbow = []
ss = []
for n_clusters in range_n_clusters:
   #iterating through cluster sizes
   clusterer = KMeans(n_clusters = n_clusters, random_state=42)
   cluster_labels = clusterer.fit_predict(df1)
   #Finding the average silhouette score
   silhouette_avg = silhouette_score(df1, cluster_labels)
   ss.append(silhouette_avg)
   print("For n_clusters =", n_clusters,"The average silhouette_score is :", silhouette_avg)`
   #Finding the average SSE"
   elbow.append(clusterer.inertia_) # Inertia: Sum of distances of samples to their closest cluster center
fig = plt.figure(figsize=(14,7))
fig.add_subplot(121)
plt.plot(range_n_clusters, elbow,'b-',label='Sum of squared error')
plt.xlabel("Number of cluster")
plt.ylabel("SSE")
plt.legend()
fig.add_subplot(122)
plt.plot(range_n_clusters, ss,'b-',label='Silhouette Score')
plt.xlabel("Number of cluster")
plt.ylabel("Silhouette Score")
plt.legend()
plt.show()

Graphs used to compare both the criterion in order to help us find optimal cluster values


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接