资讯动态

KMeans聚类算法入门:从原理到实战的完整指南

发布时间:2026/10/17 11:05:27 来源:尧图企业网站定制
1. 为什么KMeans依然是聚类入门的首选1.1 从一堆散点到K个簇KMeans到底在做什么如果你手上有一批没有任何标签的数据想从中找出“物以类聚”的结构KMeans大概率是你第一个该试的算法。它的核心思想朴素到可以用一句话说清让每个点归属于离它最近的中心然后不断调整中心位置直到中心不再移动。我经常拿它跟整理书架做类比。假设你有100本书要放到3个书架上你不知道该怎么分类于是先随便选3个位置放3本书当“临时标杆”然后把每本书放到离它最近的那个标杆旁边。放完之后每个书架上的书重新算一个“平均位置”把标杆挪过去。重复这个过程直到标杆不再动。这就是KMeans的全部逻辑。它解决的是无监督学习中最基础的聚类问题给定N个样本每个样本有M个特征把它分成K个簇使得簇内的样本尽可能相似簇间的样本尽可能不同。相似度用距离衡量最常用的是欧氏距离。适合谁看如果你刚接触机器学习想找一个能快速上手、可视化效果好、调参直观的聚类算法KMeans是最佳起点。如果你已经用过它但总觉得结果不稳定、K值不知道怎么选这篇文章会把那些文档里不会写的坑一个个拆开讲。1.2 KMeans的适用边界什么时候该用什么时候该换KMeans不是万能的它有明确的适用前提。我在实际项目中总结了几条判断标准簇的形状接近球形KMeans用距离做划分隐含假设簇是各向同性的球形分布。如果你的数据是环形、月牙形或者长条形KMeans会切得很糟糕。簇的大小和密度相近如果一个簇有1000个点、另一个只有50个点KMeans倾向于把大簇拆开去平衡因为它的目标函数是最小化所有点到各自中心的距离平方和。特征尺度统一如果某个特征取值范围是0到1另一个是0到10000距离计算会被大尺度特征主导。必须先做标准化。K值可以预估KMeans需要你事先指定簇的数量。如果你完全不知道数据该分几类需要配合肘部法、轮廓系数等方法来确定。反过来如果你的数据明显是非凸形状或者簇的密度差异极大DBSCAN或层次聚类会更合适。但话说回来KMeans的计算复杂度是O(N K M * 迭代次数)在大规模数据上依然是最快的选择之一很多时候先用它跑一版基线结果再决定要不要换更复杂的算法是性价比最高的做法。2. 核心原理拆解从目标函数到迭代流程2.1 目标函数KMeans到底在优化什么KMeans的优化目标叫簇内平方和英文缩写SSE或WCSS。公式很直白SSE Σ(k1到K) Σ(x属于Ck) ||x - μk||²其中Ck是第k个簇μk是第k个簇的中心点||x - μk||²是样本x到中心点的欧氏距离平方。这个公式的意思是把所有点到它们各自所属簇中心的距离平方加起来KMeans要做的就是让这个总和尽可能小。为什么用平方而不是绝对值因为平方函数可导求导后能得到解析解——簇中心的最优位置就是簇内所有点的均值。这也是“K-Means”这个名字的由来。注意SSE一定随着K增大而减小。当K等于样本数时每个点自成一簇SSE为0。所以不能单纯用SSE最小来判断聚类质量必须结合其他指标。2.2 迭代流程两步循环直到收敛KMeans的迭代过程可以拆成两个交替执行的步骤分配步骤固定当前K个中心点把每个样本分配给距离最近的中心。这一步是“硬分配”每个样本只属于一个簇不存在隶属度概率。更新步骤固定当前分配结果重新计算每个簇的中心点。新中心 簇内所有样本在各维度上的算术平均值。这两个步骤交替执行直到满足停止条件。停止条件通常有三种中心点位置变化小于阈值如1e-4达到最大迭代次数sklearn默认300SSE的改善量小于阈值我实测下来大多数中等规模数据集在20到50次迭代内就会收敛。如果你发现跑了300次还没停大概率是数据有问题或者K值设得不合理。2.3 初始化的坑为什么随机选点不靠谱KMeans最大的软肋是对初始中心点敏感。如果运气不好初始点选得差可能收敛到局部最优得到一个很烂的聚类结果。举个例子假设真实数据是三个明显的簇但随机初始化时两个中心点落在了同一个簇里第三个中心点落在另一个簇里。最终结果可能是把一个大簇硬拆成两个另一个簇和第三个簇合并。SSE比全局最优解高很多。sklearn默认用的是**K-Means**初始化专门解决这个问题。它的策略是第一个中心点随机选后续每个中心点按照“离已有中心越远的点被选中的概率越大”的原则来选。这样初始中心点天然分散大幅降低陷入局部最优的概率。实操建议永远不要手动设置initrandom除非你在做算法对比实验。生产环境一律用K-Means。3. 完整实操从数据准备到结果可视化3.1 环境准备与数据生成先确保你的环境里有这些库pip install scikit-learn numpy matplotlib pandas我用一份模拟数据来演示完整流程。这份数据包含三个簇每个簇500个样本特征维度为2方便可视化。import numpy as np import matplotlib.pyplot as plt from sklearn.datasets import make_blobs from sklearn.preprocessing import StandardScaler from sklearn.cluster import KMeans from sklearn.metrics import silhouette_score # 生成模拟数据 X, y_true make_blobs( n_samples1500, centers3, n_features2, cluster_std1.2, random_state42 ) print(f数据形状: {X.shape}) print(f前5个样本:\n{X[:5]})make_blobs生成的簇是各向同性的球形分布正好符合KMeans的假设。cluster_std控制簇的松散程度值越大簇越分散。random_state固定随机种子保证每次生成的数据一致。3.2 标准化别跳过这一步虽然这份模拟数据的各维度尺度差不多但实际项目中几乎一定会遇到尺度不一致的问题。养成标准化的习惯scaler StandardScaler() X_scaled scaler.fit_transform(X) print(f标准化前均值: {X.mean(axis0)}) print(f标准化前标准差: {X.std(axis0)}) print(f标准化后均值: {X_scaled.mean(axis0)}) print(f标准化后标准差: {X_scaled.std(axis0)})标准化后每个维度的均值为0、标准差为1。这样距离计算时各维度权重相同。注意标准化参数均值和标准差必须从训练数据中计算然后用同一套参数去转换新数据。不能每次来新数据都重新算否则线上和线下的处理逻辑不一致。3.3 确定K值肘部法加轮廓系数双保险KMeans需要预先指定K。我通常用两种方法交叉验证肘部法计算不同K值下的SSE画图找“拐点”。拐点之后SSE下降变缓说明增加K带来的收益递减。轮廓系数衡量每个样本与自身簇的紧密度和与最近邻簇的分离度取值范围-1到1越接近1越好。sse [] silhouette_scores [] K_range range(2, 11) for k in K_range: kmeans KMeans(n_clustersk, initk-means, n_init10, random_state42) labels kmeans.fit_predict(X_scaled) sse.append(kmeans.inertia_) silhouette_scores.append(silhouette_score(X_scaled, labels)) # 画图 fig, axes plt.subplots(1, 2, figsize(14, 5)) axes[0].plot(K_range, sse, bo-, linewidth2, markersize8) axes[0].set_xlabel(K值) axes[0].set_ylabel(SSE) axes[0].set_title(肘部法) axes[0].grid(True, alpha0.3) axes[1].plot(K_range, silhouette_scores, ro-, linewidth2, markersize8) axes[1].set_xlabel(K值) axes[1].set_ylabel(轮廓系数) axes[1].set_title(轮廓系数法) axes[1].grid(True, alpha0.3) plt.tight_layout() plt.show()n_init10的意思是跑10次不同的初始化取SSE最小的那次结果。sklearn 1.4版本之后默认就是10但显式写出来更清晰。从肘部法看K3之后SSE下降明显变缓。从轮廓系数看K3也是最高点。两个方法一致指向K3和真实簇数吻合。3.4 训练最终模型与结果可视化# 用最佳K值训练 kmeans_final KMeans( n_clusters3, initk-means, n_init10, max_iter300, tol1e-4, random_state42 ) labels_final kmeans_final.fit_predict(X_scaled) # 获取中心点转换回原始尺度 centers_scaled kmeans_final.cluster_centers_ centers_original scaler.inverse_transform(centers_scaled) # 可视化 plt.figure(figsize(10, 7)) colors [#FF6B6B, #4ECDC4, #45B7D1] for i in range(3): mask labels_final i plt.scatter(X[mask, 0], X[mask, 1], ccolors[i], labelf簇 {i}, alpha0.6, s30) plt.scatter(centers_original[:, 0], centers_original[:, 1], cblack, markerX, s200, edgecolorswhite, linewidths2, label中心点) plt.xlabel(特征 1) plt.ylabel(特征 2) plt.title(KMeans聚类结果 (K3)) plt.legend() plt.grid(True, alpha0.3) plt.show() print(f最终SSE: {kmeans_final.inertia_:.2f}) print(f迭代次数: {kmeans_final.n_iter_}) print(f轮廓系数: {silhouette_score(X_scaled, labels_final):.4f})inertia_属性就是最终的SSE值。n_iter_告诉你实际迭代了多少次。如果这个数字接近max_iter说明可能没收敛需要检查数据或调整参数。4. 参数调优与常见问题排查4.1 关键参数逐个拆解sklearn的KMeans有不少参数但日常调优真正需要关注的就这么几个参数作用建议值说明n_clusters簇的数量根据业务定最核心的参数用肘部法轮廓系数确定init初始化方法k-means别用random除非做对比实验n_init初始化次数10跑多次取最优增大更稳但更慢max_iter最大迭代次数300一般够用不够说明数据有问题tol收敛阈值1e-4中心点变化小于此值就停algorithm计算方式lloyd默认即可elkan在球形簇上更快n_init这个参数值得多说一句。它的逻辑是用不同的随机种子跑n_init次完整的KMeans每次得到一组中心和SSE最后返回SSE最小的那组。n_init越大结果越稳定但计算时间线性增长。我一般设10如果数据特别大或者对稳定性要求极高会设到20。4.2 常见问题速查表问题现象可能原因排查方法解决方案每次跑结果不一样初始化随机性检查n_init和random_state增大n_init固定random_state某个簇只有几个点初始中心点太近查看cluster_centers_用k-means增大n_initSSE一直很高K值太小或数据不适合画肘部图检查数据分布增大K或换DBSCAN等算法迭代不收敛数据有重复点或异常值检查n_iter_是否等于max_iter去重处理异常值增大tol轮廓系数很低簇结构不明显可视化数据分布降维后再聚类或换算法4.3 三个我踩过的坑第一个坑忘了标准化。有一次做用户分群特征里有“年龄”18-80和“年收入”30000-500000。没标准化直接跑KMeans结果所有簇几乎只按收入划分年龄完全没起作用。因为收入的数值范围比年龄大三个数量级距离计算被收入主导了。标准化之后年龄和收入都成了有效特征。第二个坑用SSE选K值。刚开始学的时候我直接选SSE最小的K。结果K越大SSE越小最后选了K50每个簇就几十个点完全没有业务意义。SSE必须配合肘部法看拐点不能只看绝对值。第三个坑忽略业务解释性。有一次聚类结果在数学上很漂亮轮廓系数0.75但业务方看了之后说“这三个簇我们没法区别对待”。后来发现是特征选得不对加入了一个业务上很关键的维度后虽然轮廓系数降到了0.62但簇的业务含义清晰了。聚类结果最终要能解释、能落地不能只看数学指标。5. 进阶技巧让KMeans跑得更快更准5.1 MiniBatchKMeans大数据集的选择当样本量超过10万时标准KMeans的每次迭代都要遍历所有样本速度会明显变慢。MiniBatchKMeans每次只用一小批样本更新中心点速度快很多结果和标准版接近。from sklearn.cluster import MiniBatchKMeans mbk MiniBatchKMeans( n_clusters3, initk-means, batch_size100, n_init10, random_state42 ) labels_mbk mbk.fit_predict(X_scaled) print(fMiniBatch SSE: {mbk.inertia_:.2f}) print(f标准KMeans SSE: {kmeans_final.inertia_:.2f})batch_size控制每批样本数默认1024。数据量越大batch_size可以适当调大。我实测在百万级数据上MiniBatchKMeans能快5到10倍SSE只比标准版高2%到5%。5.2 用PCA降维后再聚类如果特征维度很高比如超过50维距离计算会变得不稳定而且可视化困难。先用PCA降到2维或3维再聚类往往效果更好。from sklearn.decomposition import PCA pca PCA(n_components2) X_pca pca.fit_transform(X_scaled) kmeans_pca KMeans(n_clusters3, initk-means, n_init10, random_state42) labels_pca kmeans_pca.fit_predict(X_pca) print(fPCA解释方差比: {pca.explained_variance_ratio_}) print(f累计解释方差: {pca.explained_variance_ratio_.sum():.4f})注意PCA降维后再聚类得到的是降维空间中的簇。如果后续要分析原始特征需要把中心点逆变换回去或者用原始数据重新计算每个簇的统计量。5.3 聚类结果的业务落地聚类本身不是目的落地才是。我通常会把聚类标签作为新特征加入后续模型或者直接用于分群运营。比如电商场景中把用户分成“高价值活跃”、“价格敏感”、“流失预警”三群针对不同群做不同的运营策略。import pandas as pd # 假设原始数据有业务字段 df pd.DataFrame(X, columns[特征1, 特征2]) df[簇标签] labels_final # 查看每个簇的统计特征 cluster_summary df.groupby(簇标签).agg([mean, std, count]) print(cluster_summary)这一步的关键是用业务语言描述每个簇。比如“簇0的平均特征1为2.3特征2为-1.1样本量500”翻译成业务语言就是“簇0是特征1偏高、特征2偏低的群体占总量的三分之一”。6. 写在最后一些个人体会KMeans是我用得最多的聚类算法没有之一。它的代码量少、速度快、结果直观特别适合快速探索数据。但它也是最容易被误用的算法——很多人跑完KMeans拿到标签就结束了既不验证K值是否合理也不检查簇的业务含义更不做标准化。我自己的习惯是拿到无标签数据后先跑一版KMeans看看大致结构用肘部法和轮廓系数确定K可视化确认簇的形状是否合理。如果簇的形状明显不是球形再考虑换DBSCAN或GMM。如果数据量特别大直接上MiniBatchKMeans。还有一个容易被忽略的点KMeans对异常值很敏感。一个极端的离群点可能把整个簇的中心拉偏。跑之前用IQR或Z-score做一遍异常值检测把明显的离群点剔除或单独处理聚类结果会干净很多。最后分享一个小技巧如果你不确定K值但又不想反复跑肘部法可以先用KMeans的n_clusters设一个较大的值比如10然后合并中心点距离很近的簇。这样比从2开始逐个试要快尤其适合高维数据。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑