轮廓系数通过silhouette_score(x, labels)计算,必须传入特征矩阵x和整数型聚类标签labels;x可为原始特征(需欧氏距离适用)或预计算距离矩阵,二者不可混用,且每类至少含2个样本。

轮廓系数 silhouette_score 怎么算?必须传什么参数?
直接调用 sklearn.metrics.silhouette_score 即可,但它**不接受原始聚类标签和原始数据就自动算完**——你得先确保输入的是「样本两两之间的距离」或「原始特征矩阵 + 标签」,且二者不能混用。
常见错误是把 X(比如 shape 为 (1000, 4) 的数值型数组)和 labels(比如 array([0,0,1,1,...]))传进去后报错 ValueError: Found array with 0 sample(s),这通常是因为 labels 中存在全为同一类(比如全是 0),或某类样本数
-
X必须是二维数组,每行一个样本,列是特征;不能是稀疏矩阵(除非显式设metric='precomputed') -
labels必须是 1D 整数数组,长度与X行数一致,且至少含两个不同类别 -
metric默认是'euclidean',若传了预计算的距离矩阵(如用scipy.spatial.distance.pdist+squareform得到的 (n,n) 矩阵),则必须设为'precomputed'
为什么 silhouette_score 返回 nan 或报错 “Number of labels is 1”?
不是代码写错了,而是聚类结果本身不满足轮廓系数的数学前提:它只对「至少两个簇、且每个簇至少含两个样本」的情况有定义。如果 KMeans 跑出全一样标签(比如全 0),或者 DBSCAN 返回了大量 -1 噪声点 + 仅一个有效簇,silhouette_score 就会拒绝计算。
- 检查
len(set(labels))是否 ≥ 2,且每个类的样本数np.bincount(labels)都 ≥ 2 - DBSCAN 常见陷阱:直接把
labels == -1的点丢掉再算,但这样改变了原始聚类结构;更稳妥的做法是先过滤掉噪声点,再对剩余点重编号(如用LabelEncoder),并确认新标签仍满足条件 - 如果只有两类但其中一类只有 1 个点,
silhouette_score会静默返回nan,不抛异常——务必用np.isnan()显式检查返回值
轮廓系数低,一定是聚类效果差吗?
不一定。轮廓系数对簇的形状敏感:它偏好凸形、等大小、等密度的簇。在真实数据中,遇到以下情况时,高轮廓系数反而可能误导:
- 簇呈长条形或环形(如用 KMeans 对半月形数据聚类,
silhouette_score可能只有 0.2,但 UMAP+HDBSCAN 可能更合理) - 簇间尺度差异大(比如一类方差是另一类的 10 倍),欧氏距离下的轮廓系数会偏向小方差簇
- 使用了不匹配的距离度量(比如对高维稀疏文本向量用
euclidean,应换cosine并传metric='cosine')
示例:对 scikit-learn 的 make_moons 数据用 KMeans(k=2) 聚类,silhouette_score(X, labels, metric='euclidean') 通常在 0.15~0.25 之间,但这不代表算法失败——它只是说明欧氏距离+凸形假设在此场景下天然受限。
怎么批量评估不同 k 下的 KMeans 并选最优?
不能只画 silhouette_score 曲线就定 k,还要结合簇内平方和(inertia_)和业务解释性。实操中容易漏掉的关键点:
- 每次 fit 前必须重置 KMeans 实例(否则 warm_start 可能复用上一轮中心,导致 score 失真)
- 轮廓系数对初始化敏感,建议对每个 k 运行多次(如
n_init=10),取silhouette_score的均值而非单次结果 - 当 k 接近样本数时(如 n=100,k=50),平均轮廓系数会虚高——因为多数点自然成为“孤立点”,a(i)≈0,b(i)略大于 0,s(i)趋近 1;此时应限制 k 上限(如 ≤ n/3)
简单循环示例:
from sklearn.metrics import silhouette_score
from sklearn.cluster import KMeans
scores = []
for k in range(2, 10):
km = KMeans(n_clusters=k, n_init=10, random_state=42)
labels = km.fit_predict(X)
score = silhouette_score(X, labels)
scores.append(score)
轮廓系数是个有用的诊断工具,但它的分母是“最近邻簇的平均距离”,这个定义隐含了对簇几何结构的强假设。实际用的时候,看到 0.7 就高兴、0.3 就否定,反而容易错过更合适的聚类方案。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











