[机器学习]kmeans++算法

1,232 阅读12分钟

1/总结

k-means++ 是 k-means 算法的一个改进版本,
它通过智能地选择初始聚类中心来减少算法收敛到局部最优解的可能性,从而提高算法的稳定性和效果。

下面是一个使用 Python 和 scikit-learn 库实现的 k-means++ 算法的完整脚本。
请注意,scikit-learn 内部的 `KMeans` 类已经实现了 k-means++ 初始化选项(通过 `init='k-means++'` 参数)。

2/KMeans参数介绍

  1. n_clusters

    • 类型:int,默认为8。
    • 说明:表示希望将数据分成的簇的个数,即聚类的数量K。
  2. init

    • 类型:{'k-means++','random'或者一个ndarray数组或者可迭代的对象},默认为'k-means++'。
    • 说明:初始簇中心的获取方法。'k-means++'是一种智能的初始化方法,能加速收敛;'random'则是从数据中随机选择初始中心点。
  3. n_init

    • 类型:int,默认为10。
    • 说明:算法运行的次数,每次运行都会使用不同的初始质心位置。最终解是在inertia(即聚类效果评估指标)意义上最好的结果。
  4. max_iter

    • 类型:int,默认为300。
    • 说明:算法运行的最大迭代次数。如果算法收敛,则可能在达到最大迭代次数之前停止。
  5. tol

    • 类型:float,默认为1e-4。
    • 说明:算法收敛的容忍度阈值。如果连续两次迭代之间的聚类中心变化小于tol,则算法停止。
  6. precompute_distances

    • 类型:{'auto',True,False},默认为'auto'。
    • 说明:是否提前计算所有样本点之间的距离。这可以节省后续迭代中的距离计算时间,但会增加算法的内存消耗。
  7. verbose

    • 类型:int,默认为0。
    • 说明:冗长模式。如果非零,则在训练过程中打印进度信息。
  8. random_state

    • 类型:int,RandomState实例或者None,默认为None。
    • 说明:用于初始化质心的随机数生成器的种子。如果设置了随机种子,则每次运行算法时得到的聚类结果将是一致的。
  9. copy_x

    • 类型:bool,默认为True。
    • 说明:是否复制X数据,以便不修改原始数据。如果设置为False,则直接在原始数据上进行修改,这可能会节省内存,但会修改原始数据。
  10. n_jobs

    • 类型:int或None,默认为None。
    • 说明:并行运行的作业数。如果为-1,则使用所有CPU。这可以加速算法的执行,特别是在处理大型数据集时。
  11. algorithm

    • 类型:{'auto','full','elkan'},默认为'auto'。
    • 说明:用于实现K-Means的算法。'full'表示使用传统的K-Means算法;'elkan'是一种更快的变体,特别适用于数据集较大且簇较密集的情况。'auto'将自动选择最合适的算法。

请注意,这些参数可能会根据scikit-learn库的版本而有所变化,因此建议查阅最新的官方文档以获取最准确的信息。

3/n_init参数和max_iter参数的区别

`kmeans()` 函数,在 Python 的 `sklearn.cluster` 模块中,
通常指的是 `KMeans` 类的一个实例化对象的方法或是对该算法的调用。
然而,直接讨论 `kmeans()` 函数的参数可能有些误导,因为通常我们是在讨论 `KMeans` 类的参数。
关于 `n_init``max_iter` 这两个参数,它们在 `KMeans` 类中的作用和区别如下:

<1>n_init

定义n_init 参数指定了算法使用不同质心初始化方法运行算法的次数。

作用:由于 K-Means 算法的结果可能受到初始质心选择的影响(因为每次都是随机选择的),因此通过多次运行算法(每次使用不同的初始质心),并从多次运行结果中选择最优的(通常是惯性最小的)结果,可以提高算法的稳定性和聚类效果。

默认值:在大多数 sklearn 版本中,n_init 的默认值是 10。

<2>max_iter

定义max_iter 参数指定了算法的最大迭代次数。

作用:K-Means 算法是一个【迭代】算法,它通过不断更新质心的位置来优化聚类结果。然而,为了防止算法无限迭代下去(尤其是在某些情况下可能难以收敛),需要设置一个最大迭代次数。当算法达到最大迭代次数时,无论是否收敛,都会停止迭代。

默认值:在大多数 sklearn 版本中,max_iter 的默认值是 300。

<3>注意事项

  • 在实际应用中,需要根据数据的特点和计算资源来调整这些参数,以获得最佳的聚类效果。
  • n_init 的增加可能会提高算法的稳定性,但也会增加计算时间。
  • max_iter 的设置需要权衡算法的收敛速度和计算资源。如果数据集较大或算法难以收敛,可能需要增加 max_iter 的值。
  • 除了 n_initmax_iterKMeans 类还有其他重要的参数,如 n_clusters(聚类数目)、init(初始质心选择方式)、tol(收敛阈值)等,这些参数也需要根据具体问题进行适当的设置。

<4>归纳

图片.png

k-means++算法

k-means++算法选择初始聚类中心的基本思想就是:各个初始聚类中心之间的相互距离要尽可能的远。
算法步骤:
   <1>从所有的数据中·随机·选择一个点作为第一个初始聚类中心。
   <2>对于数据集中的每一个点,计算它与最近初始聚类中心(指已选择的初始聚类中心)的距离D(x)。
   <3>选择一个新的数据点作为另一个初始聚类中心,
      选择的原则是:D(x)较大的点,被选取作为聚类中心的概率较大。
   <4>重复<2>和<3>步骤,直到k个初始聚类中心被选出来。
   <5>最后选用这k个初始聚类中心来运行标准的k-means算法,剩下的步骤就和k-means算法一样了。

k-means++算法如何选择初始聚类中心

6中可以看到,算法的关键是第<3>步,如何将D(x)反映到点被选择的概率上??
    <1>先从我们的数据集合中随机选择一个点当“种子点”
    <2>对于其它的每个点,我们都计算其和最近的一个“种子点”的距离D(x),
       并保存在一个数组里,然后把这些距离加起来得到Sum(D(x))。
    <3>然后,再取一个随机数Random,用权重的方式来计算下一个“种子点”。
       这个算法的实现是,先取一个能落在Sum(D(x))中的随机值Random,然后用Random -= D(x),直到其<=0,此时的点就是下一个“种子点”。
    <4>重复<2>和<3>,直到k个初始聚类中心被选出来。
    <5>利用这k个初始聚类中心来运行标准的k-means算法。
    
可以看到算法的第三步选取新中心的方法,这样就能保证距离D(x)较大的点,会被选出来作为聚类中心了。
至于为什么原因比较简单,如下图所示:

如何确定最佳簇的个数

要确定K-means算法中的最佳簇(聚类)个数,
我们可以使用上述提到的方法之一,如肘部法则(Elbow Method)或轮廓系数(Silhouette Coefficient)。
在这里,我将提供一个使用肘部法则的完整Python代码示例,使用`sklearn`库中的`KMeans``metrics`模块。
from sklearn.cluster import KMeans  
from sklearn.datasets import make_blobs  
# 评估指标,轮廓系数
from sklearn.metrics import silhouette_score 
  
import numpy as np  
import matplotlib.pyplot as plt  

# 生成模拟数据  
X, _ = make_blobs(n_samples=300, centers=4, cluster_std=0.60, random_state=0)  
  
# K值的范围  
k_range = range(1, 11)  
  
# 存放每个K值对应的SSE  
sse_result_list = []  
  
for k in k_range:  
    # 初始化KMeans  
    kmeans = KMeans(n_clusters=k, random_state=0).fit(X)  
    # 计算SSE  
    sse_result_list.append(kmeans.inertia_)  
  
# 绘制Elbow Curve  
plt.figure()  
plt.plot(k_range, sse, marker='o')  
plt.xlabel('Number of clusters')  
plt.ylabel('SSE')  
plt.title('Elbow Method')  
plt.show()  
  
# 根据Elbow Curve选择最佳的K值  
# 在这个例子中,你可以通过观察图形来选择K值,但自动化的话,可以寻找SSE开始显著平缓的点  
# 这里我们直接选择图形中的“肘点”作为示例,实际上可能需要更复杂的逻辑来自动识别它  


# 假设通过图形观察得到的最佳K值为4  
best_k = 4  
  
# 可以进一步使用最佳K值进行聚类,并计算轮廓系数来验证  
kmeans_best = KMeans(n_clusters=best_k, random_state=0).fit(X)  
silhouette_avg = silhouette_score(X, kmeans_best.labels_)  
print(f"Silhouette Coefficient for best k = {best_k}: {silhouette_avg}")
  • 请注意,在上面的代码中,我假设了通过图形观察得到的最佳K值为4。

  • 在实际应用中,你可能需要编写额外的逻辑来自动检测“肘点”,这通常涉及到寻找SSE曲线开始显著平缓的点。

  • 然而,这种自动化检测并不总是准确的,特别是在SSE曲线没有明显“肘点”的情况下。

  • 轮廓系数的计算在这里只是为了展示如何使用它来评估聚类效果,虽然它本身并不直接用于确定最佳的K值。

  • 你可以通过轮廓系数进一步验证你的K值选择是否合理。轮廓系数的值范围在-1到1之间,值越大表示聚类效果越好。

图片.png

实战demo

from sklearn.cluster import KMeans  
import numpy as np  
import matplotlib.pyplot as plt  
from sklearn.datasets import make_blobs  # 自己造数据

# 模型评估
# 轮廓系数和ch指数
from sklearn.metrics import silhouette_score,calinski_harabasz_score  
  
# 生成模拟数据  
n_samples = 300  
random_state = 42  
X, _ = make_blobs(n_samples=n_samples, centers=4, cluster_std=0.60, random_state=random_state)  
  
# 使用 k-means++ 初始化进行聚类  
# 设置聚类中心数量为4  
kmeans = KMeans(n_clusters=4, # 这里的簇的个数,还是要自己来设定
                init='k-means++', 
                random_state=random_state)  
                
# 训练
kmeans.fit(X)  
# 聚类
y_kmeans = kmeans.predict(X)  
  
# 可视化结果  
plt.scatter(X[:, 0], X[:, 1], c=y_kmeans, s=50, cmap='viridis')  
  
centers = kmeans.cluster_centers_  
plt.scatter(centers[:, 0], centers[:, 1], c='red', s=200, alpha=0.75)  
plt.title("K-means++ Clustering")  
plt.xlabel("Feature 1")  
plt.ylabel("Feature 2")  
plt.show()  
  
# 打印聚类中心  
print("Cluster centers:\n", centers)  
  
# 如果你想获取每个点的聚类标签  
print("Cluster labels:", y_kmeans)

# 轮廓系数,越大越好
silhouette_avg = silhouette_score(X, y_kmeans)  
print("Silhouette Coefficient: %0.3f" % silhouette_avg)
# CH指数,越大越好
score = calinski_harabasz_score(X, y_kmeans)  
print("Calinski-Harabasz Index: %0.3f" % score)

代码说明

  1. 数据生成:使用 sklearn.datasets.make_blobs 生成包含 300 个样本的数据集,这些数据分布在 4 个中心点周围。
  2. 聚类:使用 sklearn.cluster.KMeans 类的实例 kmeans 进行聚类。通过设置 init='k-means++' 来指定使用 k-means++ 初始化方法。
  3. 可视化:使用 matplotlib 绘制原始数据点和聚类中心。不同的颜色代表不同的聚类,红色点表示聚类中心。
  4. 输出:打印聚类中心和每个点的聚类标签。

这个脚本提供了一个使用 k-means++ 算法的完整流程,从数据生成到聚类、可视化及结果输出。你可以根据需要调整 n_samplescenterscluster_std 等参数来生成不同分布的数据集。

如何评估

在Python中使用k-means++算法进行聚类后,评估模型效果是一个重要的步骤。
由于聚类是一种无监督学习技术,没有直接的“真实”标签来与预测结果进行比较,

因此需要使用一些特定的指标来评估聚类效果。以下是一些常用的评估聚类模型效果的指标:

<1>轮廓系数(Silhouette Coefficient):

轮廓系数结合了聚类的凝聚度(同一聚类内样本的相似度)和分离度(不同聚类间样本的相似度)。
轮廓系数的值范围从-1到1,值越大表示聚类效果越好。
from sklearn.metrics import silhouette_score  
silhouette_avg = silhouette_score(X, y_kmeans)  
print("Silhouette Coefficient: %0.3f" % silhouette_avg)

<2>Calinski-Harabasz Index(CH指数):

也称为方差比准则,它是聚类效果好坏的度量。
CH指数越高,代表类内越紧密,类间越分散,聚类效果越好。
from sklearn.metrics import calinski_harabasz_score  
score = calinski_harabasz_score(X, y_kmeans)  
print("Calinski-Harabasz Index: %0.3f" % score)

<3>Davies-Bouldin Index(DBI):

DBI是衡量聚类效果好坏的另一种指标,它考虑了类内的聚合度和类间的分离度。DBI的值越小,表示聚类效果越好。
注意:scikit-learn中并没有直接提供DBI的计算函数,但可以通过自定义函数来计算。

<4>轮廓图的视觉评估:

通过绘制每个点的轮廓系数来可视化聚类效果。虽然这不是一个直接的评估指标,但它可以帮助识别可能的聚类问题,如噪声点或重叠的聚类。

<5>内部评价指标(如Homogeneity, Completeness, V-measure, Adjusted Rand Index等):

这些指标基于聚类结果与某种形式的真实标签(在实际情况中通常不可用,但在有监督评估或模拟数据中可以使用)之间的比较。虽然它们主要用于有监督学习,但在某些情况下,如果数据集有某种形式的“真实”聚类结构,也可以用来评估无监督聚类。

对于大多数实际应用,轮廓系数和Calinski-Harabasz Index是评估k-means聚类效果的两个主要指标。

然而,选择哪个指标取决于你的具体需求和数据特性。
例如,如果数据集中有大量的噪声或重叠的聚类,轮廓系数可能不是一个好的选择,因为它对噪声和异常值很敏感。
相反,Calinski-Harabasz Index在这种情况下可能更加鲁棒。