原力注入

动手学机器学习 Kmeans 聚类算法

相关内容

原文链接:https://github.com/ForceInjection/hands-on-ML/blob/main/nju_software/%E5%8A%A8%E6%89%8B%E5%AD%A6%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%20Kmeans%20%E8%81%9A%E7%B1%BB%E7%AE%97%E6%B3%95.md

  1. 动手学机器学习随机森林算法

  2. 动手学机器学习朴素贝叶斯算法

  3. 动手学机器学习逻辑回归算法

  4. 动手学机器学习支持向量机

  5. 动手学线性回归算法

  6. 动手学决策树算法

  7. KNN 算法简介

  8. 分类模型评估工具 - 混淆矩阵

一、Kmeans 聚类算法基础

1、聚类的概念

聚类是一种无监督学习方法,旨在将数据集中的样本划分为若干个簇,使得同一簇内的样本相似度较高,而不同簇之间的样本相似度较低。它与分类、回归等其他机器学习任务不同,聚类不依赖于预定义的标签,而是通过数据本身的特征来发现其内在结构。

2、Kmeans 算法简介

Kmeans 算法是聚类算法中的一种,其基本思想是将数据集划分为 K 个簇,每个簇由其质心(centroid)表示。算法通过迭代优化的方式,使得每个样本被分配到最近的质心所在的簇,并不断更新质心的位置,直到达到收敛条件。

3、Kmeans 算法的数学原理及推导过程

####(1)目标函数Kmeans 算法的目标是最小化所有点到它们各自中心的距离平方和,即目标函数为:

其中, 表示第  个簇, 是第  个簇的质心, 是数据点, 是数据点与质心之间的欧氏距离。

####(2)求解过程Kmeans 算法通过交替执行以下两个步骤来求解目标函数的最小值:

1. 划分步骤(E 步骤)

将每个数据点分配到最近的质心所在的簇,即:

其中, 表示当前迭代次数, 是第  个簇在第  次迭代时的划分结果。

2. 更新步骤(M 步骤)

重新计算每个簇的质心,即:

其中, 是第  个簇在第  次迭代时的质心, 是第  个簇在第  次迭代时的数据点个数。

####(3)、收敛性Kmeans 算法的目标函数  在每次迭代中都会减小或保持不变,因此算法会在有限步数内收敛到一个局部最优解。

####(4)、数学推导

1. 目标函数的最小化

对于每个数据点 ,我们希望找到最近的质心 ,使得  最小。这可以通过计算  到所有质心的距离并选择最小的那个来实现。

2. 质心的更新

为了最小化目标函数 ,我们需要找到每个簇  的质心 。通过对  关于  求导并令其等于零,可以得到:

解得:

这表明质心  是簇  中所有数据点的均值。

####(5)、算法流程总结

  1. 初始化:随机选择 K 个数据点作为初始质心。
  2. 划分步骤:将每个数据点分配到最近的质心所在的簇。
  3. 更新步骤:重新计算每个簇的质心。
  4. 收敛检查:如果质心的变化小于某个阈值或目标函数的变化小于某个阈值,则算法收敛,否则重复步骤 2 和 3。

通过上述数学原理和推导过程,Kmeans 算法能够有效地将数据集划分为 K 个簇,使得每个簇内的数据点尽可能相似,而不同簇之间的数据点尽可能不同。

二、聚类算法效果评估方法详解

1、误差平方和(Sum of Squared Errors, SSE)

定义与计算

  • 核心思想:量化数据点与其所属簇中心的偏离程度。
  • 计算公式:其中, 为数据点, 为其所属簇的中心点。

优缺点分析

  • 优点:
  1. 计算简单,结果直观反映簇内紧密程度;
  2. 适用于监督聚类算法的迭代优化(如K-Means)。
  • 缺点:
    1. 仅能对比同一数据集不同聚类结果的优劣,无法跨数据集比较;
    2. 对簇形状敏感,假设数据呈凸分布时效果可靠。

    适用场景

    • 数据分布紧凑、簇间边界清晰的场景(如球形簇);
    • 通过肘部法则(观察SSE下降拐点)确定最佳聚类数。

    2、轮廓系数(Silhouette Coefficient)

    定义与计算

    • 核心思想:综合衡量簇内紧密性(Cohesion)与簇间分离性(Separation)。
    • 计算步骤:
    1. 对数据点,计算其与同簇其他点的平均距离 ;
    2. 计算到其他各簇的最小平均距离 ;
    3. 单点轮廓系数:
    4. 整体轮廓系数为所有点的均值,范围[-1,1],值越大聚类效果越好。

    优缺点分析

    • 优点:
    1. 适应不同形状与规模的簇结构(如非凸簇);
    2. 结果标准化,便于跨算法对比。
  • 缺点:
    1. 计算复杂度为,不适用于超大规模数据;
    2. 对密度不均的簇效果受限。

    适用场景

    • 数据分布复杂、簇间存在部分重叠的场景;
    • 需要评估聚类合理性与分离性的非监督任务。

    3、Calinski-Harabasz指数(CH Index)

    定义与计算

    • 核心思想:通过方差分析评估簇间离散度与簇内离散度的比值。
    • 计算公式:其中:
      • :簇间离散矩阵的迹(反映簇间分离度);
      • :簇内离散矩阵的迹(反映簇内紧密度);
      • 为簇数,为样本总数。

    优缺点分析

    • 优点:
    1. 计算效率高于轮廓系数(复杂度);
    2. 对高维稀疏数据仍有一定鲁棒性。
  • 缺点:
    1. 假设簇符合高斯分布,对异常值敏感;
    2. 簇数量接近样本数时结果失真。

    适用场景

    • 数据分布规则、簇间距离显著的场景;
    • 需快速筛选聚类数(通常CH值最大时对应最优簇数)。

    ###(4) 方法对比总结

    指标核心维度计算复杂度适用数据特点最佳值判定
    SSE
    簇内紧密度
    凸簇、均匀分布
    肘部拐点
    轮廓系数
    簇内/簇间平衡度
    任意形状、小规模数据
    趋近1
    CH指数
    方差比
    高斯分布、明显簇间分离
    最大值

    三、聚类算法示例

    1、环境搭建

    在进行啤酒品牌聚类分析之前,需要搭建合适的编程环境。主要使用 Python 语言,并借助 scikit-learn、numpy 和 matplotlib 等库来实现数据处理、模型训练和结果可视化。确保已安装这些库,并配置好开发环境。

    2、数据准备

    使用一个包含啤酒品牌相关信息的数据集,该数据集包含啤酒的热量(calories)、钠含量(sodium)、酒精含量(alcohol)和成本(cost)等特征。数据来源于公开数据集,可通过读取 CSV 文件等方式加载到程序中。

    ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineimport pandas as pd
    # 读取啤酒数据集beer = pd.read_csv('data.txt', sep=' ')# 选择用于聚类的特征X = beer[['calories', 'sodium', 'alcohol', 'cost']]

    在数据加载后,需要对数据进行预处理,如清洗数据、处理缺失值等。对于啤酒数据集,可能需要对数据进行标准化处理,以消除不同特征量纲和量级的影响,使聚类结果更加准确。

    ounter(lineounter(lineounter(lineounter(lineounter(linefrom sklearn.preprocessing import StandardScaler
    # 数据标准化scaler = StandardScaler()X_scaled = scaler.fit_transform(X)

    3、Kmeans 算法的实现

    使用 scikit-learn 库中的 KMeans 类来实现 Kmeans 聚类算法。首先,需要确定合适的 K 值,即簇的数量。可以通过肘部法则(Elbow Method)或轮廓系数(Silhouette Score)等方法来选择最优的 K 值。

    ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(linefrom sklearn.cluster import KMeansimport matplotlib.pyplot as plt
    # 计算不同 K 值对应的轮廓系数scores = []for k in range(2, 10):    kmeans = KMeans(n_clusters=k, random_state=0).fit(X_scaled)    score = silhouette_score(X_scaled, kmeans.labels_)    scores.append(score)
    # 绘制轮廓系数随 K 值变化的曲线plt.plot(range(2, 10), scores)plt.xlabel('Number of Clusters')plt.ylabel('Silhouette Score')plt.show()

    通过观察轮廓系数曲线,选择一个轮廓系数较高的 K 值作为最终的聚类数。例如,假设选择 K=3 作为最优簇数,然后使用该 K 值进行聚类。

    ounter(lineounter(lineounter(line# 使用 KMeans 进行聚类kmeans = KMeans(n_clusters=3, random_state=0).fit(X_scaled)beer['cluster'] = kmeans.labels_

    4、模型训练与评估

    在完成聚类后,可以对聚类结果进行可视化和评估。通过绘制散点图,可以直观地观察不同簇中啤酒品牌的分布情况。

    ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineimport numpy as np
    # 可视化聚类结果colors = np.array(['red', 'green', 'blue'])plt.scatter(beer['calories'], beer['alcohol'], c=colors[beer['cluster']])plt.xlabel('Calories')plt.ylabel('Alcohol')plt.show()

    此外,还可以计算每个簇的质心,了解每个簇中啤酒品牌在各个特征上的平均值,从而更好地理解不同簇的特点。

    ounter(lineounter(lineounter(line# 计算每个簇的质心centers = beer.groupby('cluster').mean()print(centers)

    通过分析质心的特征值,可以发现不同簇中啤酒品牌在热量、钠含量、酒精含量和成本等方面的差异,为啤酒品牌的市场定位和营销策略提供依据。

    四、Kmeans 聚类算法的优化与扩展

    1、Kmeans 算法的局限性

    尽管 Kmeans 算法在许多聚类任务中表现出色,但它也存在一些局限性。例如,算法对初始质心的选择较为敏感,不同的初始质心可能导致不同的聚类结果。此外,Kmeans 对异常值较为敏感,异常值可能对质心的计算产生较大影响,从而影响聚类的准确性。同时,确定合适的 K 值也是一个挑战,需要通过多次试验和评估来选择最优的 K 值。

    2、优化方法

    为了克服 Kmeans 算法的局限性,可以采用一些优化方法。例如,使用 Kmeans++ 算法来初始化质心,该方法通过选择与已有质心距离较远的点作为新的质心,可以有效提高聚类的稳定性和准确性。此外,在数据预处理阶段,可以通过去除异常值、进行数据变换等方法来减少异常值对聚类结果的影响。对于 K 值的选择,除了肘部法则和轮廓系数外,还可以尝试其他方法,如间隙统计量(Gap Statistic)等,以更准确地确定最优的簇数。

    3、扩展应用

    Kmeans 聚类算法在啤酒品牌聚类分析中的应用可以进一步扩展到其他领域。例如:

    • 在市场细分中,可以根据消费者的行为和偏好对客户进行聚类,以制定更精准的营销策略。
    • 在图像处理中,可以利用 Kmeans 对图像中的像素进行聚类,实现图像的分割和压缩。

    此外,还可以将 Kmeans 与其他算法结合使用,如层次聚类、DBSCAN 等,以弥补单一算法的不足,提高聚类的效果和鲁棒性。

    五、例题

    1、题目

    已知6个点:A(2, 1)、B(3, 5)、C(6, 4)、D(5, 6)、E(7, 3)、F(1, 3),根据KMeans算法的主要步骤,假设K = 3,随机选取A(2, 1)、C(6, 4)、E(7, 3) 作为三个初始簇中心,使用欧氏距离作为相似性判断,请问正确的聚类结果是( )

    • A、三个簇:{ A,F }; { B,D }; { C,E }
    • B、三个簇:{ A,F,B }; { D }; { C,E }
    • C、三个簇:{ A,F }; { B,D,C }; { E }
    • D、三个簇:{ A,F }; { B,C }; { D,E }

    根据KMeans算法的主要步骤,假设K=3,随机选取A(2, 1)、C(6, 4)、E(7, 3) 作为三个初始簇中心,使用欧氏距离作为相似性判断,正确的聚类结果是:

    三个簇:{ A,F }; { B,D }; { C,E }

    2、初始设置

    • 点:A(2, 1)、B(3, 5)、C(6, 4)、D(5, 6)、E(7, 3)、F(1, 3)
    • 初始簇中心:A(2, 1)、C(6, 4)、E(7, 3)
    • K:3

    3、第一次迭代

    步骤 1:计算每个点到簇中心的欧氏距离并分配到最近的簇

    点到A(2,1)的距离到C(6,4)的距离到E(7,3)的距离分配到的簇
    A(2,1)
    0
    5
    ≈5.385
    簇1
    B(3,5)
    ≈4.123
    ≈3.162
    ≈4.472
    簇2
    C(6,4)
    5
    0
    ≈1.414
    簇2
    D (5,6)
    ≈5.831
    ≈2.236
    ≈3.606
    簇2
    E(7,3)
    ≈5.385
    ≈1.414
    0
    簇3
    F(1,3)
    ≈2.236
    ≈5.099
    6
    簇1

    步骤2:更新簇中心

    • 簇1:{A(2,1), F(1,3)}
      • 新中心:(1.5, 2)
    • 簇2:{B(3,5), C(6,4), D(5,6)}
      • 新中心:(4.667, 5)
    • 簇3:{E(7,3)}
      • 新中心:(7, 3)

    4、第二次迭代

    步骤1:计算每个点到新簇中心的欧氏距离并分配到最近的簇

    点到(1.5,2)的距离到(4.667,5)的距离到(7,3)的距离分配到的簇
    A(2,1)
    ≈1.118
    ≈4.333
    ≈5.385
    簇1
    B(3,5)
    ≈3.354
    ≈1.667
    ≈4.472
    簇2
    C(6,4)
    ≈4.924
    ≈1.667
    ≈1.414
    簇3
    D(5,6)
    ≈5.315
    ≈1.054
    ≈3.606
    簇2
    E(7,3)
    ≈5.590
    ≈3.127
    0
    簇3
    F(1,3)
    ≈1.118
    ≈4.216
    6
    簇1

    步骤2:更新簇中心

    • 簇1:{A(2,1), F(1,3)}
      • 新中心:(1.5, 2)(不变)
    • 簇2:{B(3,5), D(5,6)}
      • 新中心:(4, 5.5)
    • 簇3:{C(6,4), E(7,3)}
      • 新中心:(6.5, 3.5)

    5、第三次迭代

    步骤1:计算每个点到新簇中心的欧氏距离并分配到最近的簇

    点到(1.5,2)的距离到(4,5.5)的距离到(6.5,3.5)的距离分配到的簇
    A(2,1)
    ≈1.118
    ≈4.924
    ≈5.148
    簇1
    B(3,5)
    ≈3.354
    ≈1.118
    ≈3.808
    簇2
    C(6,4)
    ≈4.924
    2.5
    ≈0.707
    簇3
    D(5,6)
    ≈5.315
    ≈1.118
    ≈2.915
    簇2
    E(7,3)
    ≈5.590
    ≈3.905
    ≈0.707
    簇3
    F(1,3)
    ≈1.118
    ≈3.905
    ≈5.148
    簇1

    步骤2:更新簇中心

    • 簇1:{A(2,1), F(1,3)}
      • 新中心:(1.5, 2)(不变)
    • 簇2:{B(3,5), D(5,6)}
      • 新中心:(4, 5.5)(不变)
    • 簇3:{C(6,4), E(7,3)}
      • 新中心:(6.5, 3.5)(不变)

    结果

    经过第三次迭代后,簇中心不再变化,算法收敛。

    最终聚类结果为:

    • 簇1:{A(2,1), F(1,3)}
    • 簇2:{B(3,5), D(5,6)}
    • 簇3:{C(6,4), E(7,3)}

    因此,正确答案是三个簇:{ A,F }; { B,D }; { C,E }。