在机器学习中,并不是所有任务都带有“标准答案”。有一类问题的目标不是根据已知标签去预测结果,而是直接从数据本身出发,寻找样本之间潜在的结构与分组。
聚类(Clustering)正是这种无监督学习任务的典型代表。其中的 K 均值聚类(K-Means Clustering)通常把样本划分为若干组,并通过最小化组内平方和准则来完成聚类。
一、K 均值聚类的基本思想
K 均值的核心思想是:用 K 个中心点去代表数据中的 K 个簇,并让每个样本归属于距离自己最近的那个中心。
从直观上看,K 均值完成的是这样一件事:
• 先假设数据中存在 K 个簇
• 为这 K 个簇各设一个中心
• 把每个样本分配给最近的中心
• 再根据分配结果更新各簇中心
• 不断重复,直到中心基本稳定
图 1:K 均值聚类的基本迭代过程
这样做的目标,是让同一簇内部的样本尽量彼此接近,而不同簇之间尽量相互分离。
这说明,K 均值聚类并不是在学习一个“输入到输出”的监督映射,而是在寻找一种更紧凑、更有组织的数据划分方式。
二、K 均值聚类的数学表达
1、簇中心的含义
设数据集包含 n 个样本,每个样本记为:
假设我们希望把它们划分为 K 个簇,每个簇记为:
并为每个簇定义一个中心:
这里的μⱼ就是第 j 个簇的中心,通常也称为质心(centroid)。
这些中心一般并不是原始样本中的某个点,而是处在相同特征空间中的均值点。
2、优化目标
K 均值的目标,是让每个样本到其所属簇中心的平方距离之和尽可能小。常见写法为:
这就是 K 均值聚类最核心的目标函数,也常被称为簇内平方和准则。
它的含义非常直观:
• 如果样本离所属中心越近,这个值就越小
• 如果同一簇内部样本越紧凑,这个值也越小
因此,最小化这个目标,就等价于追求“簇内更紧凑”。
3、距离度量
K 均值通常以欧氏距离为基础。对于两个样本 x 和 z,若它们都是 p 维向量,则欧氏距离可写为:
由于 K 均值目标函数本身使用的是平方距离,因此它对特征尺度非常敏感:某个量纲较大的特征可能在距离计算中占主导地位。
这也是为什么在很多实际任务中,使用 K 均值前常常需要先做标准化。
三、K 均值聚类如何工作
1、初始化
K 均值并不会自动知道“哪 K 个位置最适合做中心”,因此一开始必须先给出初始中心。
Scikit-learn 中 KMeans 的 init 参数支持多种初始化方式,最常见的是:
• k-means++
• random
其中,k-means++ 会以一种更有策略的方式选择初始中心,以加快收敛并减少糟糕初始化带来的影响;它也是当前默认初始化方式。
2、分配步骤
给定一组中心后,算法会把每个样本分配给距离最近的那个中心。
这一步的含义是:先固定中心,再重新决定“谁属于哪个簇”。
3、更新步骤
当所有样本都完成分配后,每个簇都会重新计算自己的中心。
新的中心通常是该簇内所有样本的均值,因此才叫“K 均值”。
4、迭代直到稳定
分配和更新会不断重复,直到中心变化足够小,或者达到最大迭代次数。
KMeans 的 max_iter 默认值为 300,tol 用于判断相邻两次中心变化是否足够小,从而宣布收敛。
这说明,K 均值本质上是一种迭代优化算法:在固定簇分配时更新中心,在固定中心时更新簇分配,二者交替进行。
四、K 的含义与初始化问题
1、K 表示簇的数量
参数 K 表示我们希望把数据划分成多少个簇。
这意味着,K 均值的一个前提是:聚类前需要先给定簇数。
Scikit-learn 中对应的参数是 n_clusters,默认值为 8。
如果 K 设得太小,不同结构可能会被强行合并;如果 K 设得太大,原本属于同一结构的数据又可能被过度拆分。
图 2:簇数选择与初始化对聚类结果的影响
2、初始化会影响最终结果
K 均值并不保证找到全局最优解,它常常会停在局部最优附近。
这意味着,不同的初始化方式可能会得到不同结果。
Scikit-learn 的 n_init 参数就用于控制用不同初始化重复运行算法的次数,并最终选择 inertia 最小的一次结果。
比如当前稳定版中,n_init='auto' 是默认值;当 init='k-means++' 时默认运行 1 次,当 init='random' 时默认运行 10 次。
因此,初始化并不是无关紧要的细节,而是 K 均值聚类效果的重要影响因素。
五、聚类结果如何解释
1、簇标签的含义
K 均值输出的 labels_ 表示每个样本所属的簇编号。
需要特别注意的是,这些簇编号本身没有固定语义顺序。例如,标签 0、1、2 并不表示“大小递增”或“重要程度递增”,它们只是算法内部给不同簇分配的编号。
2、簇中心的含义
训练完成后,cluster_centers_ 给出各簇中心的坐标。
这些中心可以帮助我们理解每个簇的大致位置与特征均值特征。
例如:
• 某个簇中心在“收入高、消费高”区域,可能对应高价值客户群
• 某个簇中心在“收入低、消费低”区域,可能对应另一类群体
3、inertia 的意义
KMeans 训练后还会给出 inertia_ 属性,它表示所有样本到其最近簇中心的平方距离之和。
这意味着:inertia 越小,说明样本整体上离各自中心越近。
但 inertia 小并不自动意味着“聚类就一定有意义”,当 K 增大时,inertia 通常会继续下降。
因此,inertia 是一个重要的内部指标,但不能机械地孤立解读。
六、Python 实现:二维 K 均值聚类示例
下面先用 make_blobs 生成一个二维样本集,并用 KMeans 完成聚类。
Scikit-learn 把 KMeans 描述为对数据进行 K 组划分、并最小化 inertia 的方法;make_blobs 则常用于生成聚类示例数据。
import matplotlib.pyplot as plt # 绘图库,用于可视化聚类结果from sklearn.cluster import KMeans # KMeans聚类算法from sklearn.datasets import make_blobs # 生成各向同性高斯团块(聚簇)数据的工具 # 根据操作系统选择中文字体(二选一,取消注释对应行)plt.rcParams["font.sans-serif"] = ["Microsoft YaHei"] # Windows# plt.rcParams["font.sans-serif"] = ["Songti SC"] # macOSplt.rcParams["axes.unicode_minus"] = False # 解决负号 '-' 显示为方块的问题(使负号正常显示) # 1. 生成二维聚类数据# make_blobs 生成各向同性的高斯分布团块,常用于聚类算法的演示# 参数解释:# n_samples=300: 生成300个样本点# centers=4: 数据真实的中心数量为4,即数据会围绕4个中心点分布# cluster_std=0.80: 每个团块的标准差为0.80,控制簇的分散程度(值越大簇越分散)# random_state=42: 随机种子,保证每次生成的数据相同,便于复现# 返回值:X 是特征数组(形状 300×2),_ 是真实标签(此处用不到,故用下划线忽略)X, _ = make_blobs( n_samples=300, centers=4, cluster_std=0.80, random_state=42) # 2. 创建 KMeans 模型# KMeans 参数解释:# n_clusters=4: 指定要聚类的簇的数量为4(与真实中心数一致)# init="k-means++": 使用 k-means++ 初始化方法,能更快收敛并得到更优的聚类结果(默认)# n_init="auto": 自动选择初始化次数(默认10次),取其中 inertia 最小的作为最终模型# random_state=42: 固定随机种子,确保每次运行结果可复现model = KMeans( n_clusters=4, init="k-means++", n_init="auto", random_state=42) # 3. 训练模型并获得聚类标签# fit_predict 方法等价于先调用 fit() 再调用 predict(),返回每个样本所属簇的标签(0,1,2,3)labels = model.fit_predict(X) # 4. 查看聚类中心与 inertia# cluster_centers_ 属性:每个簇的中心坐标(形状为 n_clusters × n_features,此处为 4×2)print("聚类中心:\n", model.cluster_centers_)# inertia_ 属性:所有样本到其最近簇中心的距离平方和(Sum of Squared Distances),用于评估聚类效果(值越小越好)print("inertia_ =", model.inertia_) # 5. 可视化plt.figure(figsize=(8, 5)) # 创建画布,大小为宽8英寸、高5英寸 # 绘制散点图:X 的第一列作为 x 坐标,第二列作为 y 坐标;c=labels 表示按聚类标签着色plt.scatter(X[:, 0], X[:, 1], c=labels) # 在相同坐标系中绘制聚类中心plt.scatter( model.cluster_centers_[:, 0], # 所有中心的 x 坐标 model.cluster_centers_[:, 1], # 所有中心的 y 坐标 marker="X", # 标记形状为叉形,与普通样本点区分 s=200, # 标记大小(面积),200 表示较大 label="聚类中心" # 图例标签) # 添加图表标题和坐标轴标签plt.title("K 均值聚类示例")plt.xlabel("特征 1")plt.ylabel("特征 2")plt.legend() # 显示图例(此时会显示“聚类中心”)plt.show() # 展示图形(会弹出窗口或显示在 Jupyter 中)这段代码展示了 K 均值聚类的基本工作流:
1、生成或加载数据
2、创建 KMeans 模型
3、用 fit_predict 完成训练与标签输出
4、查看 cluster_centers_ 与 inertia_
4、用散点图观察聚类结果
这里的 fit_predict 会先拟合模型,再直接返回聚类标签;这是 KMeans 提供的常用便捷方法。
七、Python 实现:用 inertia 观察不同 K 的变化
下面再给出一个简单示例,用不同的 K 重复训练,并观察 inertia_ 的变化趋势。
from sklearn.cluster import KMeansfrom sklearn.datasets import make_blobs # 1. 生成数据X, _ = make_blobs( n_samples=400, centers=4, cluster_std=0.90, random_state=42) # 2. 比较不同 K 下的 inertiafor k in range(1, 8): model = KMeans( n_clusters=k, init="k-means++", n_init="auto", random_state=42 ) model.fit(X) print(f"K = {k}, inertia = {model.inertia_:.2f}")这段代码常用于帮助理解一个基本现象:随着 K 增大,inertia 通常会不断减小,因为簇数越多,每个样本更容易靠近某个中心。
但这并不意味着 “K 越大越好”,因为聚类的目标并不是把每个样本都单独变成一个簇,而是在紧凑性与可解释性之间寻找较合适的划分。
八、K 均值适用场景与主要局限
1、适用场景
K 均值较适合以下情况:
• 希望把样本划分为若干相对紧凑的簇
• 特征空间中的距离具有明确意义
• 簇大致接近“球形”或“均值可代表”
• 数据规模中等,且希望使用一个直观、快速的聚类基线模型
• 需要一个简单方法来进行客户分群、样本分组或原型发现
Scikit-learn 的 KMeans 对大样本数具有较好的可扩展性,并已广泛应用于许多领域。
2、主要局限
K 均值虽然常用,但也并不是万能方法:
• 需要预先指定 K:聚类前必须先给定簇数
• 对初始化敏感:不同初始中心可能得到不同局部解
• 对特征尺度敏感:未标准化时,大尺度特征可能主导结果
• 不适合任意形状的簇:若真实簇呈细长、环状或密度差异很大,K 均值往往表现不好
• 对异常值较敏感:少量离群点可能显著拉动中心位置
因此,K 均值最适合的是“簇中心有代表性、距离度量合理、簇形状相对规则”的任务;如果数据结构明显更复杂,往往需要考虑 DBSCAN、层次聚类或高斯混合模型等其他方法。
📘 小结
K 均值聚类通过“分配样本—更新中心”的迭代过程,寻找使簇内平方和尽量小的样本划分。它直观、高效,是理解无监督学习、距离度量与簇结构的重要入口。
“点赞有美意,赞赏是鼓励”