news 2026/9/23 22:24:25

三种聚类算法在鸢尾花数据集上的对比与调参指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
三种聚类算法在鸢尾花数据集上的对比与调参指南

简介:一套基于鸢尾花数据集的三种聚类算法 Python 代码包,面向机器学习初学者与数据分析人员,用于掌握无监督学习中的 K-Means、合并聚类和 DBSCAN,并通过同一份数据直观对比不同算法的聚类效果。资源既包含三种算法的核心实现代码,也给出在鸢尾花数据集上的可直接运行示例,同时附有 Word 版说明文档,系统讲解各算法的原理步骤与关键差异,例如 K-Means 需要预置簇数且受初始中心影响,合并聚类通过相似度逐层聚合,DBSCAN 则依赖邻域半径与最小样本数识别任意形状簇。压缩包共 6 个文件,以 .py 源码和 .doc 说明文档为主,整体仅 433KB,轻量便携,下载解压后即可对照运行或二次改进,适合课堂教学、课程设计和自学实践。目前已有 2320 人学习下载,代码注释清晰、输出结果易于追踪,是快速上手并落地聚类算法的实用资源。

1. 三种聚类算法在同一份鸢尾花数据上做对照:这份代码到底在验证什么

做聚类实验的人,十有八九第一份数据就是鸢尾花(Iris)。150 条样本、4 个特征、3 个类别,小到能逐行检查,又刚好能暴露不同聚类算法之间的本质差异。k均值、合并聚类和 DBSCAN 对同一份 Iris 数据集聚类,表面上只是三个脚本并排跑,实际上回答的是三个完全不同的问题:数据里有没有球形簇、簇能否按层次结构合并、密度不均时边界在哪里。这篇笔记把三类算法的代码、参数和评估口径拆开讲,适合正在赶机器学习作业、或者想把手头无标签数据快速跑通一遍的从业者。看完你能直接得到三张聚类图、一套评估指标,并且知道每个结果怎么解释、哪里最容易翻车。

2. 鸢尾花数据集与算法选型:为什么三套逻辑差异这么大的算法都拿它当试金石

2.1 Iris 数据集的结构与“有标签却做无监督”的实验边界

鸢尾花数据集由 Fisher 在 1936 年整理,sklearn里直接load_iris()就能拿到。150 条样本分成 setosa、versicolor、virginica 三类,每类 50 条,每条有花萼长度、花萼宽度、花瓣长度、花瓣宽度四个数值特征。这里有个容易混淆的点:在无监督聚类任务里,这 150 个真实标签是被“封存”的,算法在训练阶段看不到类别,标签只在最后评估时拿出来做对照。

from sklearn.datasets import load_iris import pandas as pd iris = load_iris() X = iris.data # 特征矩阵,150x4,单位是厘米 y = iris.target # 真实标签,0/1/2,仅用于事后评估 feature_names = iris.feature_names df = pd.DataFrame(X, columns=feature_names) df['label'] = y print(df.describe())

逻辑说明:load_iris()返回一个 Bunch 对象,data是特征矩阵,target是整数标签。上面把数据转成 DataFrame 是为了先看分布——花萼长度在 4.3 到 7.9 之间,花瓣长度在 0.1 到 6.9 之间,意味着花瓣特征的方差远大于花萼特征。这个量纲差异直接决定后面的 DBSCAN 必须做标准化,否则 eps 参数会完全失效。

实验边界在于:我们拿着有标签的数据做无监督,目的是对比算法在“不知道答案”时的表现。真实标签不能参与任何训练参数的选择,比如不能因为知道有三类就把 K-Means 的 K 硬定为 3,然后说模型很准——这在教学演示里常见,但实际落地时你的数据往往根本没有标签,K 要靠轮廓系数等内部指标来选。

2.2 三种算法的划分逻辑:从“先定 K”到“密度可达”

K-Means 的思路是:预先指定 K 个簇,随机初始化 K 个中心,然后迭代两步——把每个样本分给最近的中心,再重新计算中心位置,直到中心不再变化。它对簇形状的默认假设是“凸的、近似等方差的球形簇”,这也是它处理 Iris 时天然吃亏的地方:Iris 里 versicolor 和 virginica 两类在特征空间中有重叠,实心球假设会让边界处的样本被随机劈开。

合并聚类(Agglomerative Hierarchical Clustering)走的是另一条路:一开始每个样本自己是一个簇,然后按距离不断合并最近的两个簇,直到剩下一个簇。整个过程形成一棵树,你可以在任意高度“切一刀”得到任意数量的簇。它的优势是不用提前定 K,而是看树状图决定在哪里切;缺点是计算复杂度高,150 条样本没问题,几万条样本就要考虑用linkage='ward'配合距离矩阵的优化实现。

DBSCAN 完全抛弃了“中心”和“树”的概念。它把样本分成核心点、边界点和噪声点三类:如果一个点的 eps 邻域内样本数不少于 min_samples,它就是核心点;核心点之间通过“密度可达”连成簇;落单的样本就是噪声。DBSCAN 不需要指定簇个数,能发现任意形状的簇,还能显式标出离群点。代价是两个参数 eps 和 min_samples 对结果极其敏感,而且在高维数据上“密度”这个概念会退化。

三种算法在 Iris 上的对照,本质上是在问同一个问题:这份数据的簇结构到底更像球形、层次型还是密度型?Iris 的正确答案是“近似球形但有部分重叠”,所以 K-Means 和 ward 合并聚类通常表现接近,DBSCAN 则要看参数配得怎么样。这个判断对实际项目很有参考价值——拿到新数据时,先想清楚簇的形状假设,再选算法,比逐个试错快得多。

2.3 预期表现与核心难点:setosa 好分,另两类才是真正的分水岭

把三种算法放在同一份数据上,本质是做一次受控对照实验。Iris 的第一个类别 setosa 和其他两类线性可分,所以几乎所有算法都能把它干净地分出来;真正的难点在 versicolor 和 virginica,这两类在花瓣特征上有明显重叠区域。

预先知道这个结构对调参很有帮助:如果聚类结果连 setosa 都分错了,说明代码有 bug 或者特征没有标准化;如果只是 versicolor 和 virginica 混在一起,那是数据本身重叠导致的贝叶斯误差,不是算法实现的问题。我一般建议先用seaborn.pairplot或散点矩阵看一眼原始特征分布,再跑算法,否则你连“这个结果对不对”都判断不了。

比如花瓣长度和花瓣宽度两个特征就能把三类分得七七八八,而花萼宽度单独拿出来几乎没有区分度。这意味着如果你用全部 4 个特征做聚类,花萼宽度反而会引入噪声,让重叠变得更严重。实践里不少人只取花瓣长度和花瓣宽度两个特征跑聚类,效果往往比四维全量更好,这并不是偷懒,而是特征选择本身就嵌在聚类流程里。

3. 用 Python 跑通三类算法的核心代码:参数怎么设、结果怎么看

3.1 K-Means:K 值选择、随机初始化与聚类中心解读

K-Means 在 sklearn 里的调用非常成熟,但有两个新手最容易忽略的参数:n_initrandom_staten_init表示用多少组不同的随机中心去跑,最后保留代价函数最小的那组结果,默认是 10;random_state固定随机种子,让结果可复现。后面避坑章节会专门讲这个问题,这里先跑通。

from sklearn.cluster import KMeans from sklearn.preprocessing import StandardScaler scaler = StandardScaler() X_scaled = scaler.fit_transform(X) kmeans = KMeans(n_clusters=3, init='k-means++', n_init=10, random_state=42, max_iter=300) kmeans.fit(X_scaled) print('聚类标签:', kmeans.labels_[:20]) print('聚类中心:', kmeans.cluster_centers_)

逻辑说明:先标准化再聚类是这里的关键一步。K-Means 用欧氏距离衡量样本相似度,如果花瓣长度(方差大)和花萼宽度(方差小)不做标准化,前者会在距离计算里占据绝对主导,相当于你只用了两个特征。init='k-means++'是默认也推荐的初始化方式,它让初始中心尽量分散,减少落到局部最优的概率。

参数说明:n_clusters=3在本实验里是为了和真实三类做对照,实际无标签场景应该换用轮廓系数或其他内部指标来确定 K,后面第四章会讲;max_iter=300是单次迭代上限,Iris 这种小数据几十次迭代就收敛了,不需要动;random_state=42保证每次跑出来完全一致。

运行完可以看两个东西:一是kmeans.labels_和真实标签的对应关系,注意聚类标签的编号是任意的,0/1/2 和真实类别的对应可能要映射,直接数一致数会误以为准确率很低;二是聚类中心在标准化空间里的坐标,如果配合scaler.inverse_transform把它还原成原始单位,能得到“每个簇的平均花萼长度、平均花瓣长度”这类可解释的结论。

3.2 合并聚类:三种 linkage 策略在 Iris 上的差异

合并聚类的核心参数是linkage,它决定“两个簇之间的距离”怎么定义。常见的三种:ward用合并后簇内方差增量,倾向生成大小相近的球状簇;complete用两个簇最远样本的距离,对噪声敏感;average用所有样本对距离的平均,介于两者之间。Iris 上建议直接用ward,它和 K-Means 的假设最接近,也是实际项目中默认首选。

from sklearn.cluster import AgglomerativeClustering from scipy.cluster.hierarchy import dendrogram, linkage import matplotlib.pyplot as plt # 直接聚类 agg = AgglomerativeClustering(n_clusters=3, linkage='ward') agg_labels = agg.fit_predict(X_scaled) # 画树状图(需要先用 scipy 重新算 linkage 矩阵) Z = linkage(X_scaled, method='ward') plt.figure(figsize=(10, 6)) dendrogram(Z, truncate_mode='level', p=5) plt.title('Ward 合并聚类的树状图(截断显示)') plt.ylabel('簇间距离') plt.show()

逻辑说明:AgglomerativeClustering适合直接拿聚类结果,但它不保留树状图的中间过程;要看树得用scipy.cluster.hierarchy.linkage单独算一次 Z 矩阵。dendrogram画出来的横轴是样本索引,纵轴是合并时的距离,距离越大说明两个簇越晚被合并、差异越大。

参数说明:truncate_mode='level', p=5表示只显示最上面 5 层合并,因为 150 个样本的完整树状图横轴会挤成一团。树状图在 Iris 上的典型特征是:setosa 那一支在很低的距离就独立成形,versicolor 和 virginica 在高得多的距离才分开,这个距离差就是后面判断“该切几刀”的依据。

一个常见误用:直接用AgglomerativeClusteringn_clusters=3而不看树状图,等于放弃了合并聚类最大的优势。正确的做法是先画树状图,看最大的几个“高度落差”在哪里,再回到n_clusters那一刀。Iris 上通常在高度约 5 到 8 的位置有明显的分叉,切在那里得到 3 簇左右最合理。

3.3 DBSCAN:eps 和 min_samples 的参数配对方法

DBSCAN 的两个参数比 K-Means 的 K 难调得多。eps是邻域半径,min_samples是成为核心点的最少样本数。经验上min_samples先取2 * 特征维度,这里特征维度是 4,所以先取 8 左右;eps则要画 k-distance 图来确定——计算每个样本到它第min_samples个最近邻居的距离,排序后找拐点。

from sklearn.cluster import DBSCAN from sklearn.neighbors import NearestNeighbors import numpy as np # 方法一:直接跑,先给一组保守参数 db = DBSCAN(eps=0.5, min_samples=8).fit(X_scaled) print('簇标签(-1 表示噪声):', db.labels_) print('簇个数:', len(set(db.labels_)) - (1 if -1 in db.labels_ else 0)) # 方法二:k-distance 图辅助定 eps nn = NearestNeighbors(n_neighbors=8).fit(X_scaled) distances, _ = nn.kneighbors(X_scaled) k_dist = np.sort(distances[:, -1]) # 每个样本到第 8 近邻居的距离 plt.plot(k_dist) plt.xlabel('样本序号(按距离排序)') plt.ylabel('第 8 近邻距离') plt.title('k-distance 图,拐点处对应合适 eps') plt.show()

逻辑说明:k-distance 图的原理是,簇内样本的第 8 近邻距离普遍较小,噪声样本的距离会突然变大,图上会出现一个明显的“肘部”,这个肘部对应的纵坐标就是合理的 eps。Iris 标准化之后,肘部通常在 0.4 到 0.8 之间,所以上面先用 0.5 试跑。

参数说明:eps=0.5对标准化后的 Iris 通常能分出 2 到 3 个簇,但很容易把 versicolor 和 virginica 的一部分标成噪声;min_samples=8偏大时噪声点变多,偏小时会把边界样本也变成核心点,簇的边界变得破碎。DBSCAN 另一个特点是它的标签里-1是噪声,统计簇个数时必须先排除-1,这个细节很多人头一次跑都会漏。

DBSCAN 在 Iris 上的表现通常弱于 K-Means 和 ward 合并聚类,原因是 Iris 的类分布更接近“球形加部分重叠”,而不是“密度不均的任意形状”。这不是 DBSCAN 的缺陷,而是它的适用场景不同——如果你的数据有大量离群点、簇形状是长条形或月牙形,DBSCAN 才会明显胜出。

3.4 一张图跑通三种算法:聚类结果并排可视化的完整脚本

把上面三段捏成一个脚本,输出三张子图,是最常见的交付形态。可视化时注意两点:一是用 PCA 降维后的轴而不是原始特征轴,Iris 四个特征里取前两个 PCA 分量通常能解释 95% 以上的方差,画出来更干净;二是给每个子图标注算法名和簇个数,不要把三个图混在一起。

from sklearn.decomposition import PCA pca = PCA(n_components=2, random_state=42) X_pca = pca.fit_transform(X_scaled) fig, axes = plt.subplots(1, 3, figsize=(15, 4)) results = [ ('K-Means', kmeans.labels_), ('合并聚类(ward)', agg_labels), ('DBSCAN', db.labels_) ] for ax, (name, labels) in zip(axes, results): scatter = ax.scatter(X_pca[:, 0], X_pca[:, 1], c=labels, cmap='viridis', s=20, alpha=0.8) ax.set_title(name) ax.set_xlabel('PC1') ax.set_ylabel('PC2') ax.legend(*scatter.legend_elements(), title='簇') plt.tight_layout() plt.show()

逻辑说明:PCA(n_components=2)把四维标准化特征压成两维,X_pca只是用于画图,聚类本身仍然在四维的X_scaled上完成,顺序不要搞反。三个算法都使用同一个X_scaled,保证对比公平。

参数说明:cmap='viridis'是色盲友好的配色,s=20控制点大小,alpha=0.8让重叠的点稍微透明。如果 DBSCAN 的图里出现大片灰色(-1 噪声),说明 eps 太小或 min_samples 太大,回到 k-distance 图重新取值。跑完这张图,你就能直观看到 K-Means 和 ward 的结果几乎相同,DBSCAN 则多出若干噪声点——这个对比本身就是结论。

4. 聚类效果评估:轮廓系数、ARI 与 NMI 怎么搭配使用

4.1 无监督内部指标:轮廓系数能告诉你什么

没有真实标签时,只能用内部指标评估聚类质量。轮廓系数(Silhouette Coefficient)是最常用的一种:对每个样本,计算它到同簇其他样本的平均距离 a,以及到最近其他簇样本的平均距离 b,轮廓系数就是 (b - a) / max(a, b)。取值范围在 -1 到 1 之间,越接近 1 说明簇内紧致、簇间分离。

from sklearn.metrics import silhouette_score, calinski_harabasz_score, davies_bouldin_score for name, labels in [('K-Means', kmeans.labels_), ('合并聚类', agg_labels), ('DBSCAN(含噪声)', db.labels_)]: # DBSCAN 的 -1 噪声样本需要剔除,否则指标会失真 if -1 in labels: mask = labels != -1 sc = silhouette_score(X_scaled[mask], labels[mask]) else: sc = silhouette_score(X_scaled, labels) print(f'{name}: 轮廓系数={sc:.3f}')

逻辑说明:轮廓系数的输入是特征矩阵和聚类标签,不需要真实标签。上面代码处理了一个隐蔽问题:DBSCAN 的噪声点-1会被误认为一个簇,导致轮廓系数计算失真,所以先mask掉。实际报结果时,DBSCAN 要同时报告“剔除噪声后的轮廓系数”和“噪声占比”两个数。

参数说明:calinski_harabasz_score也叫方差比准则,值越大越好;davies_bouldin_score值越小越好。这三个内部指标在 Iris 上通常给出一致的排序:K-Means 和 ward 合并聚类接近,DBSCAN 剔除噪声后三者接近,但 DBSCAN 的噪声率会拖累它的工程可用性。

轮廓系数的局限也要讲清楚:它只衡量几何紧致度,完全不看真实标签。一个极端例子是把 Iris 按花瓣长度中位数一分为二,轮廓系数可能很高,但这两半里都混着三个真实类别——所以内部指标只能用来筛参数和诊断,不能用来证明“聚类找到了真实结构”。

4.2 有监督对照指标:ARI 与 NMI 的正确解读

因为 Iris 有真实标签,我们还能用外部指标做“开卷考试”。调整兰德指数(ARI)和归一化互信息(NMI)都考虑了随机划分的基线,取值范围最大为 1,接近 0 表示和随机划分差不多。ARI 对簇结构更敏感,NMI 对类别分布更鲁棒,实践里两个都报。

from sklearn.metrics import adjusted_rand_score, normalized_mutual_info_score for name, labels in [('K-Means', kmeans.labels_), ('合并聚类', agg_labels), ('DBSCAN', db.labels_)]: if -1 in labels: mask = labels != -1 ari = adjusted_rand_score(y[mask], labels[mask]) nmi = normalized_mutual_info_score(y[mask], labels[mask]) print(f'{name}: ARI={ari:.3f}, NMI={nmi:.3f} (噪声样本 {np.sum(labels == -1)} 个已剔除)') else: ari = adjusted_rand_score(y, labels) nmi = normalized_mutual_info_score(y, labels) print(f'{name}: ARI={ari:.3f}, NMI={nmi:.3f}')

逻辑说明:adjusted_rand_score把聚类结果和真实标签做排列匹配,它自动处理了标签编号不一致的问题——聚类把 setosa 标成 0 还是 2 都不影响得分。在 Iris 上 K-Means 的 ARI 通常在 0.7 左右,不是 0.99,因为 versicolor 和 virginica 的重叠区域样本无论怎么分都会被判错,这是数据本身的贝叶斯误差。

参数说明:NMI 的默认平均方式是'arithmetic',样本不平衡时可以考虑'geometric';这两个指标对 DBSCAN 噪声的处理必须显式说明,剔除噪声样本后再算,否则等于默认把噪声当作一个簇,得分会虚高或虚低。

这里要顺便纠正一个常见误解:聚类“准确率”不是随便算的。如果直接把聚类标签和真实标签逐位对比,因为标签编号错位,准确率可能只有 30%,但这不代表算法差。ARI 已经隐式做了标签匹配,所以报告里优先用 ARI 而不是手工匹配后的准确率。周志华《机器学习》里对聚类性能度量也是分外部指标和内部指标两类讲,这个分类本身就是行业通行的口径。

4.3 三个指标怎么组合使用:一套实用的判读顺序

指标要用组合拳,不能单看一个。我一般按这个顺序判读:先看轮廓系数,低于 0.4 说明聚类结构本身弱,先回头检查标准化和特征选择;再看 ARI,它告诉你和真实标签的吻合度,在 Iris 这种有标准答案的数据上最有说服力;最后看噪声占比,DBSCAN 超过 20% 的噪声就基本不可用了。

这套判读顺序在无标签数据上要稍作调整:没有 ARI 就用轮廓系数加簇个数一起看,再结合业务方对“簇的语义”的判断。比如一个簇如果同时包含高价值和低价值客户,即使几何上很紧致,业务上也没有意义——聚类最终要服务于解释,不是服务于分数。

算法建议主指标辅助指标关注点
K-Means轮廓系数、ARI簇内平方和(SSE)K 值是否合理,是否陷入局部最优
合并聚类树状图高度差、ARI轮廓系数在哪个高度切,linkage 选对没有
DBSCAN噪声占比、ARI簇个数稳定性eps 是否在平台期,噪声是否可解释

上面这个表是我做实验报告时的固定格式。每个算法两到三个指标,不多报,因为每多一个数字就要多解释一层,报告反而难读。评估还有一个实际作用:参数调优。比如 DBSCAN 的 eps 在 0.2 到 1.5 之间扫一遍,每个值跑一次聚类、算一次轮廓系数和 ARI,画出曲线,就能看到参数变化的敏感区间。这个“参数—指标”曲线比单点调参可靠得多,第六节会给具体代码。

5. 避坑指南:鸢尾花聚类最容易翻车的六个细节

5.1 现象:K-Means 每次运行结果完全不一样

第一次跑 K-Means 没设random_state,第二次跑簇标签变了,甚至某个样本的归属在两个簇之间横跳。原因是 K-Means 对初始中心敏感,不同随机种子可能收敛到不同的局部最优。

原因:n_init虽然会跑多组初始中心,但随机种子不同,多组中心的组合也不同,最终选出的最优解可能有微差。在 Iris 这种有重叠的数据上,边界样本的归属本来就模糊,随机性就会被放大。

解决:固定random_state=42(或其他任意整数)让实验可复现;如果要做多种子实验,也要固定一组种子列表并记录每个种子的代价函数值。正式交付的代码里,不设随机种子的 K-Means 是不允许上线的,因为你没法向别人解释“为什么昨天跑的和今天跑的不一样”。

5.2 现象:DBSCAN 画出来要么全是噪声,要么只有一个大簇

eps=0.2跑出来满屏灰色噪声,eps=2.0跑出来所有样本合为一类,中间值怎么调都不对。

原因:特征没有标准化。原始特征里花瓣长度的方差远大于花萼宽度,欧氏距离被花瓣长度主导,eps 的尺度完全错乱。这是 DBSCAN 最常见的失败方式,比 K-Means 严重得多,因为 K-Means 好歹会用 k-means++ 缓解一下,DBSCAN 对距离尺度没有任何补救。

解决:先StandardScaler标准化,再用 k-distance 图找拐点。标准化后 eps 的合理区间一般在 0.3 到 1.5 之间,扫参时优先在这个区间画曲线。记住一个经验:DBSCAN 调参前先问自己一句“所有特征是否已经缩放到同一量纲”,这一步能省掉一半的调参时间。

5.3 现象:合并聚类的树状图横轴挤成一团,根本看不清

150 个样本的完整树状图横轴密密麻麻,标签叠在一起,无法判断该在哪里切。

原因:完整树状图在样本量大于 50 时横轴就会拥挤,这是可视化问题,不是算法问题。很多人第一次看到这团“黑毛线”就以为代码写错了,其实只是没做截断显示。

解决:用truncate_mode='level', p=5只显示顶层合并,或者把dendrogramorientation改为横放。更重要的是,不要靠肉眼数树状图的分支,而是看合并高度差量化判断——相邻合并的高度差最大的地方就是最佳切割点。可以顺手把Z矩阵里的高度列打印出来,用np.diff找最大落差的位置。

5.4 现象:轮廓系数很高,但 ARI 很低,结果自相矛盾

K-Means 跑出轮廓系数 0.6,结果 ARI 只有 0.5,感觉算法“高分低能”。

原因:轮廓系数衡量的是簇的几何紧致度,不关心簇是否匹配真实类别。如果真实类别有重叠,算法可以切出几何上非常漂亮的球形簇,但把两个真实类别的边缘样本归错了——几何指标照样给高分。

解决:有标签就用 ARI/NMI 做主指标,轮廓系数做诊断辅助;无标签时承认轮廓系数有局限,结合簇个数和业务可解释性判断。不要拿几何指标证明“聚类很准”,这就像说一个盒子排列整齐所以里面装的东西一定是对的,逻辑上站不住。

5.5 现象:DBSCAN 结果里噪声点特别多,簇却只有一个

有时 eps 怎么调,DBSCAN 都倾向于把一大部分样本标为噪声,剩下的粘成一个簇,看不到“多簇”结构。

原因:Iris 标准化后密度分布比较均匀,没有明显的密度分离,DBSCAN 在这种数据上本就表现一般。它在“密度有高低起伏”的数据上才发挥优势,Iris 不是它的主场。

解决:换用 K-Means 或 ward 合并聚类做主打,DBSCAN 只作为“离群点检测器”使用,看它标出的噪声是否对应真实的异常样本。如果业务上确实要 DBSCAN 出多簇,考虑先降维或改用 HDBSCAN,但这是另一个方向了。强行让 DBSCAN 在 Iris 上出三个簇,等于逼它做不擅长的事,结果一定别扭。

5.6 现象:三维散点图看不出聚类效果,颜色混在一起

matplotlibAxes3D画了三维散点图,转半天角度也看不清簇边界。

原因:三维可视化把四个特征压成三个,重叠区域的视觉遮蔽反而比二维更严重,旋转带来的信息量不足以抵消透视变形。而且三维图在纸面和屏幕上展示时,深度方向的遮挡会让读者误判簇的分离程度。

解决:Iris 这种低维数据,用 PCA 降到二维画图比三维图清晰得多;如果非要用三维,固定一个视角并分别画簇的轮廓,不要指望单张图说明问题。真正的簇结构判断交给指标,图片只是给读者看的证据——这也是为什么第四章的指标表比任何一张散点图都重要。

6. 进阶:把三类算法变成一份可复现、可交付的聚类比对报告

6.1 封装一个统一的聚类流程函数

散落的脚本只能自己跑,交付给团队或写进实验记录就需要封装。我习惯把“标准化 → 聚类 → 评估 → 可视化”包成一个函数,传入算法名和参数,返回指标字典和图像。这样换数据集、换参数都只改一行。

def run_clustering(name, model, X, y, pca_components=2): from sklearn.preprocessing import StandardScaler scaler = StandardScaler() Xs = scaler.fit_transform(X) labels = model.fit_predict(Xs) noise_mask = labels != -1 if -1 in labels else None # 统一指标计算入口 metrics = {} if noise_mask is not None: metrics['noise_ratio'] = (labels == -1).mean() eff_labels = labels[noise_mask] eff_X = Xs[noise_mask] eff_y = y[noise_mask] else: eff_labels, eff_X, eff_y = labels, Xs, y metrics['silhouette'] = silhouette_score(eff_X, eff_labels) metrics['ari'] = adjusted_rand_score(eff_y, eff_labels) metrics['nmi'] = normalized_mutual_info_score(eff_y, eff_labels) metrics['n_clusters'] = len(set(eff_labels)) # PCA 投影用于画图 pca = PCA(n_components=pca_components, random_state=42) Xp = pca.fit_transform(Xs) return metrics, Xp, labels

逻辑说明:函数内部先标准化再聚类,保证调用方不会忘记缩放;noise_mask把 DBSCAN 的特殊情况统一处理掉,噪声占比作为额外指标返回。pca_components默认 2,画图用;聚类始终在标准化后的原始维度上做,PCA 只影响可视化。

参数说明:model是已经配好参数的 sklearn 估计器对象,调用fit_predict时保持接口一致。这个函数的输出是(metrics, Xp, labels)三元组,便于在循环里攒成表格。调用方可以这样用:model = KMeans(n_clusters=3, random_state=42),一行就能拿到指标和绘图数据。

6.2 参数网格扫描:用一张折线图定 DBSCAN 的 eps

与其一次次改参数重跑,不如把 eps 的候选值列出来一次跑完,同时记录轮廓系数和 ARI,你会直观看到参数敏感区间。这个做法同样适用于 K-Means 扫 K 值——把n_clusters从 2 到 8 各跑一遍,画一条“K—轮廓系数”曲线,取拐点处的 K。

candidates = np.linspace(0.2, 1.5, 20) sil_scores, ari_scores, n_clusters_list = [], [], [] for eps in candidates: db = DBSCAN(eps=eps, min_samples=8) labels = db.fit_predict(X_scaled) mask = labels != -1 if len(set(labels[mask])) < 2 or mask.sum() < 10: sil_scores.append(np.nan) ari_scores.append(np.nan) else: sil_scores.append(silhouette_score(X_scaled[mask], labels[mask])) ari_scores.append(adjusted_rand_score(y[mask], labels[mask])) n_clusters_list.append(len(set(labels)) - (1 if -1 in labels else 0)) # 画双轴折线图 fig, ax1 = plt.subplots() ax1.plot(candidates, sil_scores, 'o-', label='轮廓系数') ax1.plot(candidates, ari_scores, 's--', label='ARI') ax1.set_xlabel('eps') ax1.set_ylabel('得分') ax2 = ax1.twinx() ax2.bar(candidates, n_clusters_list, alpha=0.2, label='簇个数') ax2.set_ylabel('簇个数') plt.legend() plt.show()

逻辑说明:代码里加了两个保护条件——有效样本少于 10 个或者只有 1 个簇时,指标没有意义,直接记为 NaN。twinx()生成双 y 轴,左边是得分,右边是簇个数,一眼就能看出“eps 多大时簇个数稳定、得分最高”。

参数说明:np.linspace(0.2, 1.5, 20)把 eps 在 0.2 到 1.5 之间均匀取 20 个值,这一步把离散的调参变成连续观察;min_samples=8保持之前定好的值。实际跑出来的典型结果是:eps 在 0.4 附近 ARI 出现峰值,之后簇个数快速下降,这个“肩部”就是最优区间,不需要追求唯一精确值。

6.3 用一份表格把结果落成报告,以及三个值得再试的方向

聚类实验做完,交付物不是图,而是一张汇总表。我通常按算法一行、指标一列组织:簇个数、轮廓系数、ARI、NMI、噪声占比。Iris 这份数据跑完,典型结果是 K-Means 和 ward 的 ARI 都在 0.7 左右,DBSCAN 调好参数后 ARI 接近但噪声占比 5% 左右。这张表写进实验记录,谁都能复现——这才是“聚类代码.zip”真正该有的价值。

如果做完这份对照还想往深走,我给你三个方向。第一,把特征维度从 4 个缩减到 2 个(只留花瓣长宽),重新跑一遍三套算法,你会看到 ARI 普遍提升,这能帮你理解特征选择对聚类的真实影响。第二,给数据人为加 10 个离群点,再看 DBSCAN 和 K-Means 的表现差异——DBSCAN 会把这 10 个点全部标成噪声,K-Means 则会硬把它们塞进最近的簇里,这个对比比任何理论都直观。第三,换一份形状完全不同的数据集(比如 sklearn 的make_moons),你会看到 DBSCAN 和 K-Means 的排名直接反转。

做聚类这份数据多年,我最深的体会是:三种算法跑在同一份 Iris 上,比的不是谁更准,而是谁更适合问题的形状假设。K-Means 适合球形簇加快速迭代,合并聚类适合需要解释层次关系的场景,DBSCAN 适合密度不均和噪声明确的数据。调参之前先问一句数据长什么样,比任何参数搜索都管用。每次拿到一份新的无标签数据,我都会先画分布、定形状假设、再选算法,这套流程已经成了反射动作。希望这些踩过的坑和封装好的流程能帮到你,让你拿到一份聚类代码时,不是把它当黑匣子跑完就扔,而是能拆开、能调参、能解释、能复现。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/23 22:23:45

计算书看着没问题就稳过?土木毕设 AI 暗藏 4 大致命漏洞,盲审直接判定结构分析失效[特殊字符]️

2026 土木工程本科毕业论文、毕业设计盲审要求持续收紧。土木毕设高度看重**荷载取值、规范条文、结构计算书、PKPM/YJK 建模结果、构件配筋验算**&#xff0c;无论是框架结构设计、桥梁计算、施工组织设计方向&#xff0c;计算逻辑、规范版本、构件参数必须一一对应&#xff0…

作者头像 李华
网站建设 2026/9/23 22:22:59

2026年 | 国内宠物检测实验室TOP5推荐

一、引言随着国内宠物保有量持续增长&#xff0c;宠物医疗行业逐步向精准化、专科化、规范化方向升级&#xff0c;第三方临床检测作为宠物疾病诊断的核心支撑环节&#xff0c;其技术水平与服务质量直接影响临床诊疗的准确性与效率。当前国内宠物检测市场参与者类型丰富&#xf…

作者头像 李华
网站建设 2026/9/23 22:21:56

数字广告五大计费模式解析与应用指南

1. 数字广告计费模式全景解析在数字营销领域&#xff0c;广告计费模式的选择直接影响着营销预算的使用效率和最终ROI。作为从业十年的数字营销专家&#xff0c;我见过太多企业因为计费模式选择不当而浪费大量预算。今天我们就来深度剖析五种主流计费模式的内在逻辑和应用场景。…

作者头像 李华
网站建设 2026/9/23 22:19:54

旅游景点情感分析:细粒度属性级建模与BERT微调实践

简介&#xff1a;本资源是一套面向计算机专业本科生的毕业设计实战项目&#xff0c;聚焦旅游景点评论的细粒度情感分析任务&#xff0c;适用于Python Web开发、自然语言处理与数据库应用等课程实践或毕设选题参考。项目基于Django框架构建Web系统&#xff0c;集成RNCC情感分析模…

作者头像 李华
网站建设 2026/9/23 22:16:48

基于arXiv API的每日论文自动分析系统:从数据拉取到趋势追踪

1. 一份日报的诞生&#xff1a;为什么要做 arXiv 每日论文分析每天早上刷 arXiv 的人不少&#xff0c;但真正能把当天上百篇新论文筛明白的人不多。我自己做计算机视觉和机器人方向的研究&#xff0c;前几年养成了一个习惯&#xff1a;每天花二十分钟扫一遍 arXiv 的 cs.CV、cs…

作者头像 李华