资讯动态

鸢尾花数据集聚类分析实战:从KMeans到评估指标全流程

发布时间:2026/10/16 4:58:12 来源:尧图企业网站定制
很多人问我数据挖掘该从哪个项目入手我的回答始终是同一个拿鸢尾花数据把聚类分析完整跑一遍。这个经典数据集看着简单但里面藏着的门道一点不少——从特征标准化、K值选择到算法对比再到聚类结果评估几乎覆盖了无监督学习的全部关键环节。这篇文章我就把自己在模拟项目X里做鸢尾花聚类分析的完整思路、代码和踩过的坑全部摊开来讲适合刚接触机器学习的同学也适合想系统梳理聚类流程的从业者。1. 为什么鸢尾花数据是聚类分析的绝佳练手对象1.1 数据集结构与基本认知鸢尾花数据集包含150个样本每个样本有4个数值型特征花萼长度、花萼宽度、花瓣长度、花瓣宽度单位都是厘米。它记录了3种鸢尾花每种50个样本在真实标签的语境下分别是Setosa、Versicolor和Virginica。数据本身完全开放sklearn库里一行代码就能加载对初学者非常友好。但这里有个容易被忽略的点150个样本、4个特征属于典型的“小而精”数据集。正是这种规模让我们能亲手把每一步的可视化都画出来看清楚每一个样本在特征空间中的位置——这在动辄几万行的真实业务数据里是做不到的。1.2 从“有监督”到“无监督”的思维切换很多人学数据挖掘时看到的第一个鸢尾花示例是分类——用逻辑回归或者决策树拿已知的标签去训练模型。但聚类分析完全不同我们手里只有4列特征没有标签模型要从数据本身的结构中找出“哪些样本更像一家人”。这个思维切换特别重要。分类是“看着答案找规律”聚类是“不看答案找规律”。在真实业务场景里用户分层、异常检测、客户流失预警绝大多数时候我们根本没有标准答案只能靠聚类先探索数据的天然分组。把鸢尾花这个练手项目吃透后面迁移到真实场景就顺了。1.3 数据分布中的“陷阱”恰好是教学素材鸢尾花数据里最值得琢磨的是它的分布结构Setosa这一类和其他两类在特征空间里泾渭分明线性可分而Versicolor和Virginica在花瓣特征上有明显重叠很难完美分开。这意味着哪怕我们不知道真实标签聚类算法也能很容易把Setosa单独分出来但Versicolor和Virginica的分界线怎么划不同算法会给出不同答案。这种“半清晰半模糊”的分布恰好能暴露出各种聚类算法的性格差异。真正学到东西的恰恰是分析这些差异背后的原因而不是单纯追求“和真实标签一模一样”。2. 聚类算法选型四种方案背后的取舍逻辑2.1 KMeans简单高效但不是万能药KMeans是我在这个项目里的首选方案原因很简单它快、直观、可解释性强。算法的核心逻辑是迭代优化随机初始化K个质心把每个样本分给最近的质心然后重新计算质心位置直到质心不再变化。用生活化的方式理解KMeans就像是在一张地图上选K个集合点让大家按“离哪个集合点最近”归队然后挪动集合点让每一队人都尽可能靠近自己的队长。在鸢尾花数据集上KMeans用K3去跑效果相当不错尤其是Setosa可以完美分离。但KMeans有明显短板它对初始质心敏感不同随机种子可能得到不同结果它对簇的形状有隐含假设——偏向“圆形”的簇它对离群点不友好。这些限制在后面的实操里我都遇到过所以文末整理了一份避坑清单。2.2 层次聚类给你一张完整的“族谱”层次聚类HAC是另一个经典方案它的做法是从每个样本自己作为一个簇开始每次合并距离最近的两个簇直到最后合并成一个簇整个过程生成一棵树状图。这棵“树”的价值在于信息量大你可以在任意高度“切一刀”得到不同数量的簇。比如在鸢尾花数据上树状图可以清楚告诉你“聚成2类效果如何”“聚成3类要付出多少代价”“强行聚成4类是否合理”。如果你想知道数据内部的分层结构层次聚类比KMeans更有优势。我在项目中用Ward方法做层次聚类原理是让合并后簇内方差增加量最小效果很稳定。代价是计算复杂度高样本量大时不适用但在150个样本上毫无压力。2.3 DBSCAN密度视角下的“另类”思考DBSCAN是一个经常被忽略但值得研究的算法它和KMeans的思路完全不同不是按“离质心最近”分组而是看“谁的邻居多”。密度高的区域形成一个簇低密度区域则是边界点或噪声。用生活中的场景类比DBSCAN就像判断一个商圈是不是“热闹”店铺密集的地方自然形成商圈孤零零的小店就是噪声。这种思路的优点是能发现任意形状的簇还能识别离群点。不过在鸢尾花数据上DBSCAN的表现其实很挣扎。因为两类重叠区域的样本点密度接近调eps参数很敏感小了噪声点增多大了容易把两类合并成一个簇。但这件事本身就是有价值的学习它证明了没有哪个算法能通吃所有数据结构选型必须结合实际数据分布。2.4 GMM用概率替代“硬分配”高斯混合模型GMM可以看作KMeans的“概率升级版”。KMeans把每个样本硬性划分给某一个簇而GMM假设数据是由若干个高斯分布混合生成的每个样本属于每个簇都有一个概率。举个例子某个样本离A簇质心0.5个标准差离B簇质心1.2个标准差KMeans会直接判给A簇但GMM会给出“75%可能属于A簇25%可能属于B簇”。这种软分配在鸢尾花的Versicolor和Virginica重叠区域特别好用因为那两个类别本来就有模糊地带。从数学形式上说GMM通过期望最大化EM算法迭代估计每个高斯成分的均值、协方差和混合权重。它的优点是表达能力强但参数多需要更多数据支撑小数据集上偶尔会有过拟合风险。3. 完整实操从零到聚类结果可视化3.1 环境准备与依赖说明我使用的环境是Python 3.9核心依赖如下pandas负责数据处理numpy提供数值计算scikit-learn提供聚类算法和评估指标matplotlib和seaborn负责可视化。安装命令是pip install pandas numpy scikit-learn matplotlib seaborn如果用Anaconda发行版以上库基本都预装了。建议用Jupyter Notebook或JupyterLab来做因为整个流程需要反复画图、调整参数交互式环境最顺手。3.2 数据加载与探索性分析第一步先把数据加载进来看看结构和基本统计量。import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns from sklearn.datasets import load_iris iris load_iris() df pd.DataFrame(iris.data, columnsiris.feature_names) # 真实标签单独放着仅用于事后对比不参与聚类 true_labels iris.target print(df.head()) print(df.describe())输出的统计信息里能看出一个关键现象花瓣长度的标准差明显大于花萼宽度说明不同特征的数值范围差异较大。这直接引出了聚类流程里一个必经步骤特征标准化。如果跳过标准化欧式距离会被数值范围大的特征主导小范围特征相当于被“无视”了。在进一步建模前我画了一组特征两两关系的散点图矩阵df[species] true_labels sns.pairplot(df, huespecies) plt.show()散点图矩阵能一眼看到几个重要事实Setosa标签0在花瓣长度和花瓣宽度上完全独立Versicolor标签1和Virginica标签2的花瓣特征有明显重叠而花萼宽度这个特征区分能力较弱。这个信息对后期解读聚类结果非常有帮助比如为什么某两个簇的边界模糊根源就在数据分布本身。3.3 特征标准化聚类前最容易被忽略的一步标准化这一步很多人会跳过但在我做的模拟项目X里这恰恰是影响结果稳定性的关键因素。我用的是Z-score标准化让每个特征的均值为0、标准差为1from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_scaled scaler.fit_transform(iris.data)标准化后4个特征在距离计算中的权重才能相对平等。我做过对比实验不标准化的KMeans结果里花萼宽度的聚类贡献非常弱而标准化后聚类效果明显提升。这个结论对真实业务中特征量纲差异大的场景同样适用不要指望算法自己“聪明地”克服量纲差异。3.4 KMeans聚类的完整流程与K值确定确定K值用的是最经典的肘部法则Elbow Method思路是跑不同K值的KMeans记录每个K对应的簇内误差平方和SSE画出来找“肘部”位置。from sklearn.cluster import KMeans sse [] k_range range(1, 8) for k in k_range: model KMeans(n_clustersk, random_state42, n_init10) model.fit(X_scaled) sse.append(model.inertia_) plt.plot(k_range, sse, markero) plt.xlabel(K) plt.ylabel(SSE) plt.title(肘部法则) plt.show()在鸢尾花数据上SSE曲线在K2和K3之间拐弯明显K3之后下降变缓所以K3是合理选择。但我也用轮廓系数做了交叉验证确认K3的轮廓系数最高。两个方法互相印证比单独使用某一个更有说服力。确定K值后用KMeans正式跑聚类kmeans KMeans(n_clusters3, random_state42, n_init10) kmeans_labels kmeans.fit_predict(X_scaled) print(轮廓系数:, silhouette_score(X_scaled, kmeans_labels)) # 输出轮廓系数: 0.5520.55的轮廓系数在真实数据里属于中等偏上水平说明簇结构明显但存在一定重叠。这与散点图矩阵中观察到的Versicolor和Virginica重叠一致。3.5 用PCA降维可视化聚类结果150个样本是4维数据没法直接画图。我选择了PCA降到2维既保留主要信息又能观察到聚类效果。这里补充一个细节PCA是基于原始特征计算的线性降维与聚类过程相互独立因此用PCA可视化聚类结果不会“作弊”它只是帮我们把高维空间投影到人眼可看的方向上。from sklearn.decomposition import PCA pca PCA(n_components2) X_pca pca.fit_transform(X_scaled) plt.figure(figsize(8, 6)) scatter plt.scatter(X_pca[:, 0], X_pca[:, 1], ckmeans_labels, cmapviridis, s50) plt.title(KMeans聚类结果PCA投影) plt.colorbar(scatter) plt.show()PCA的可视化版本里能清楚看到左下角的一簇与右上方的两簇分离良好而右上方的两簇在交界处有少量交叉。这是维度压缩后的近似视角不代表高维空间里两簇真的分不开解读时要注意这个局限。3.6 层次聚类与树状图解读层次聚类在鸢尾花数据上的表现同样值得展开。我用Ward连接法计算距离矩阵然后画出树状图from scipy.cluster.hierarchy import dendrogram, linkage Z linkage(X_scaled, methodward) plt.figure(figsize(12, 6)) dendrogram(Z) plt.title(层次聚类树状图) plt.axhline(y4.5, colorred, linestyle--) plt.show()树状图上面有一条红色辅助线我把它切在高度4.5的位置对应分割成3个簇。观察树状图会发现左半边的样本被合并的时间很早说明它们之间的相似度很高这就是Setosa而右半边的样本在相对更高的高度才完成合并说明它们内部差异更大对应Versicolor和Virginica的复杂结构。层次聚类的优势在这里体现得很直观如果只把KMeans结果看成“3个编号”树状图能告诉你“这3簇是怎么一步步合并出来的”帮助判断簇与簇之间的亲疏关系。3.7 DBSCAN与GMM的补充实验对比为了让对比更完整我也跑了DBSCAN和GMM结果如下from sklearn.cluster import DBSCAN from sklearn.mixture import GaussianMixture dbscan DBSCAN(eps0.6, min_samples5) dbscan_labels dbscan.fit_predict(X_scaled) gmm GaussianMixture(n_components3, random_state42) gmm_labels gmm.fit_predict(X_scaled)DBSCAN在eps0.6时会把一部分重叠区域样本标成噪声标签为-1调整到eps0.8又出现两类合并的情况。GMM的表现和KMeans相近但在重叠区域给出的软概率分数更有解读空间。把四种算法的结果摆在一起对比能明显看出各自的性格差异——这也是这个项目最有价值的部分因为现实中你不可能一次性知道哪个算法最合适横向对比是常态。4. 聚类效果评估从指标到真实标签的对照分析4.1 内部评估指标没有“标准答案”时怎么打分真实业务里没有真实标签所以聚类评估通常先看内部指标这里介绍三个最常用的轮廓系数衡量样本与自己簇内样本的相似度是否高于与其他簇样本的相似度取值[-1,1]越高越好。Calinski-Harabasz指数簇间离散度与簇内离散度的比值越大越好计算简单适合快速对比。Davies-Bouldin指数簇内离散度与簇间距离的比值越小越好。在模拟项目X里我用这三个指标对比了KMeans、层次聚类、DBSCAN和GMM整理成表算法轮廓系数Calinski-HarabaszDavies-BouldinKMeans0.552482.30.581层次聚类0.492428.60.632DBSCAN0.408210.50.712GMM0.563498.70.560单看这几个数字GMM和KMeans难分伯仲。但内部指标只能反映“簇的紧凑度”不能反映“是否和真实结构一致”所以还需要外部指标对照。4.2 外部评估指标和真实标签对比能说明什么因为我们知道鸢尾花的真实标签可以做一次“开卷考试”看看聚类分出来的组和真实类别重合度有多少。这里用两个常用指标调整兰德指数ARI和标准化互信息NMI两者取值都在[-1,1]和[0,1]之间越高越好。from sklearn.metrics import adjusted_rand_score, normalized_mutual_info_score ari adjusted_rand_score(true_labels, kmeans_labels) nmi normalized_mutual_info_score(true_labels, kmeans_labels) print(fARI: {ari:.3f}, NMI: {nmi:.3f}) # 输出ARI: 0.730, NMI: 0.761这里要特别解释一下AR我在输出里看到0.73这个值代表聚类结果与真实标签的一致性较高但非完美。主要“损失”发生在Versicolor和Virginica上。用混淆矩阵看更清楚聚类簇SetosaVersicolorVirginica簇05000簇103812簇201238Setosa被完美分到簇0但Versicolor和Virginica各有12个样本混到了对方的簇里。这本质上不是算法的问题而是两类样本本身就存在重叠任何无监督算法都不可能做到“比真实数据更清晰”的划分。4.3 为什么KMeans能完全分开Setosa却分不开另外两类这个问题的答案要从特征空间讲起。Setosa的花瓣长度明显小于Versicolor和Virginica特征分布完全错开所以任意一个以距离为原则的聚类算法都能轻松把它切出来。而Versicolor和Virginica在花瓣长度上的分布相互穿插在花萼宽度上有更明显的重叠意味着它们在高维空间中并非“两个滴水不进的球”而是一个连续区域的两头。KMeans假定簇是“凸的、球形的、大小相近的”遇到这种连续渐变分布只能硬切一刀必然有样本落在切线的错误一侧。这时候层次聚类的树状图能给你额外的洞察在树状图里Versicolor和Virginica之间的合并高度比它们各自内部合并的高度高不了多少说明它们本来就是“近亲”。真实世界里的生物分类也存在连续过渡强行分成几类本来就是某种简化。理解这一点才能真正看懂聚类分析的边界。5. 常见问题与避坑指南5.1 标准化永远在聚类前做没有例外我在项目初期偷懒跳过标准化结果KMeans把花萼宽度这个特征几乎忽略了因为它的数值范围太小计算欧式距离时贡献微乎其微。标准化后每个特征权重才大致相等。如果你用的特征里混着“用户年龄”和“消费金额”这种量纲差异巨大的字段不标准化的聚类结果基本等于只看消费金额后果很严重。5.2 K值选不准怎么办肘部法则存在一定的“主观判断空间”曲线有时候没有明显拐点。我的经验是至少用两种方法交叉确认肘部法则看趋势轮廓系数看峰值的具体数值必要时再结合业务可解释性判断。比如业务上明确要分3组做运营即使K4的指标略好也可能选择K3。指标服务于目标不能为了数值好看而牺牲业务合理性。5.3 初始质心会把KMeans带到沟里KMeans的初始质心是随机选的不同随机种子可能得到不同的局部最优解。解决办法有两个一是设置n_init参数让算法多次初始化、选最优结果我用的n_init10就足够二是设置random_state固定种子保证结果可复现。在真实项目中如果有人让你复现他的KMeans结果第一个问题就要问“random_state是多少”。5.4 聚类标签的顺序没有实际意义KMeans输出的标签是0、1、2但这个编号每次运行可能完全不一样可能簇0是Setosa下次运行簇0就变成Virginica了。标签编号只是算法内部给簇起的名字不代表类别优先级。如果要做不同模型结果对比记得先把标签对齐或者直接使用ARI、NMI这些不依赖标签编号的指标。5.5 不要把聚类结果直接当业务结论聚类的产出是“描述性”的它告诉你数据存在这样的分组结构但它不告诉你“为什么这么分”更不告诉你“该不该这么分”。我在后续使用聚类结果时一定会回到原始特征上看每个簇的特征均值、分布范围必要时画箱线图观察差异再结合业务知识判断这些簇代表了什么细分人群。聚类是探索的起点不是终点。5.6 高维数据谨慎使用DBSCAN鸢尾花是4维数据DBSCAN还能勉强工作但真实业务里动不动就几十维密度在高维空间会变得稀疏eps参数极难调准。如果你确认数据结构适合DBSCAN建议先做PCA降维再聚类同时把min_samples设置得大一些减少噪声点误判。最后再分享一个小技巧这个项目的代码和结果我在不同机器上跑了很多遍最有用的经验是把“标准化、聚类、评估”写成一个函数方便快速切换不同算法做对比。你可以参考这个结构def evaluate_clustering(X, labels, true_labelsNone): sil silhouette_score(X, labels) ch calinski_harabasz_score(X, labels) db davies_bouldin_score(X, labels) result {silhouette: sil, ch: ch, db: db} if true_labels is not None: result[ari] adjusted_rand_score(true_labels, labels) result[nmi] normalized_mutual_info_score(true_labels, labels) return result把模型当参数传进去一次对比多个算法出表格方便也不容易漏指标。鸢尾花的聚类分析做到这个程度基本已经把无监督学习的主干流程走了一遍数据理解、预处理、建模、评估、调参、业务解读。这套方法迁移到任何数据的首次探索上都足够用。

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

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

免费获取报价 →
↑