动手学机器学习 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
一、Kmeans 聚类算法基础
1、聚类的概念
聚类是一种无监督学习方法,旨在将数据集中的样本划分为若干个簇,使得同一簇内的样本相似度较高,而不同簇之间的样本相似度较低。它与分类、回归等其他机器学习任务不同,聚类不依赖于预定义的标签,而是通过数据本身的特征来发现其内在结构。
2、Kmeans 算法简介
Kmeans 算法是聚类算法中的一种,其基本思想是将数据集划分为 K 个簇,每个簇由其质心(centroid)表示。算法通过迭代优化的方式,使得每个样本被分配到最近的质心所在的簇,并不断更新质心的位置,直到达到收敛条件。
3、Kmeans 算法的数学原理及推导过程
####(1)目标函数Kmeans 算法的目标是最小化所有点到它们各自中心的距离平方和,即目标函数为:
其中, 表示第 个簇, 是第 个簇的质心, 是数据点, 是数据点与质心之间的欧氏距离。
####(2)求解过程Kmeans 算法通过交替执行以下两个步骤来求解目标函数的最小值:
1. 划分步骤(E 步骤)
将每个数据点分配到最近的质心所在的簇,即:
其中, 表示当前迭代次数, 是第 个簇在第 次迭代时的划分结果。
2. 更新步骤(M 步骤)
重新计算每个簇的质心,即:
其中, 是第 个簇在第 次迭代时的质心, 是第 个簇在第 次迭代时的数据点个数。
####(3)、收敛性Kmeans 算法的目标函数 在每次迭代中都会减小或保持不变,因此算法会在有限步数内收敛到一个局部最优解。
####(4)、数学推导
1. 目标函数的最小化
对于每个数据点 ,我们希望找到最近的质心 ,使得 最小。这可以通过计算 到所有质心的距离并选择最小的那个来实现。
2. 质心的更新
为了最小化目标函数 ,我们需要找到每个簇 的质心 。通过对 关于 求导并令其等于零,可以得到:
解得:
这表明质心 是簇 中所有数据点的均值。
####(5)、算法流程总结
初始化:随机选择 K 个数据点作为初始质心。 划分步骤:将每个数据点分配到最近的质心所在的簇。 更新步骤:重新计算每个簇的质心。 收敛检查:如果质心的变化小于某个阈值或目标函数的变化小于某个阈值,则算法收敛,否则重复步骤 2 和 3。
通过上述数学原理和推导过程,Kmeans 算法能够有效地将数据集划分为 K 个簇,使得每个簇内的数据点尽可能相似,而不同簇之间的数据点尽可能不同。
二、聚类算法效果评估方法详解
1、误差平方和(Sum of Squared Errors, SSE)
定义与计算
核心思想:量化数据点与其所属簇中心的偏离程度。 计算公式:其中, 为数据点, 为其所属簇的中心点。
优缺点分析
优点:
计算简单,结果直观反映簇内紧密程度; 适用于监督聚类算法的迭代优化(如K-Means)。
仅能对比同一数据集不同聚类结果的优劣,无法跨数据集比较; 对簇形状敏感,假设数据呈凸分布时效果可靠。
适用场景
数据分布紧凑、簇间边界清晰的场景(如球形簇); 通过肘部法则(观察 SSE下降拐点)确定最佳聚类数。
2、轮廓系数(Silhouette Coefficient)
定义与计算
核心思想:综合衡量簇内紧密性(Cohesion)与簇间分离性(Separation)。 计算步骤:
对数据点,计算其与同簇其他点的平均距离 ; 计算到其他各簇的最小平均距离 ; 单点轮廓系数: 整体轮廓系数为所有点的均值,范围[-1,1],值越大聚类效果越好。
优缺点分析
优点:
适应不同形状与规模的簇结构(如非凸簇); 结果标准化,便于跨算法对比。
计算复杂度为,不适用于超大规模数据; 对密度不均的簇效果受限。
适用场景
数据分布复杂、簇间存在部分重叠的场景; 需要评估聚类合理性与分离性的非监督任务。
3、Calinski-Harabasz指数(CH Index)
定义与计算
核心思想:通过方差分析评估簇间离散度与簇内离散度的比值。 计算公式:其中: :簇间离散矩阵的迹(反映簇间分离度); :簇内离散矩阵的迹(反映簇内紧密度); 为簇数,为样本总数。
优缺点分析
优点:
计算效率高于轮廓系数(复杂度); 对高维稀疏数据仍有一定鲁棒性。
假设簇符合高斯分布,对异常值敏感; 簇数量接近样本数时结果失真。
适用场景
数据分布规则、簇间距离显著的场景; 需快速筛选聚类数(通常 CH值最大时对应最优簇数)。
###(4) 方法对比总结
| 指标 | 核心维度 | 计算复杂度 | 适用数据特点 | 最佳值判定 |
|---|---|---|---|---|
| SSE | ||||
| 轮廓系数 | ||||
| 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)的距离 | 分配到的簇 |
|---|---|---|---|---|
步骤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)的距离 | 分配到的簇 |
|---|---|---|---|---|
步骤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)的距离 | 分配到的簇 |
|---|---|---|---|---|
步骤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 }。