Courses
训练完一个聚类模型后,如何判断这些簇是否“好”?
没有放之四海而皆准的答案。在传统的监督学习中,您可以将预测与真实标签对比并得到准确率。但聚类不行。您选了 k=5,但是否换一个 k 会更好?没有标签,您无法判断算法是真的发现了结构,还是只是把数据随意切成几块。
轮廓系数就是您要找的度量。它衡量每个点相对于次近簇而言,与其被分配簇的匹配程度,是最常用的指标之一。它不需要标签,而且容易解读。
本文将带您理解其公式与解读方式,给出基于 scikit-learn 的 Python 示例,并讨论何时适用、何时并非最佳选择。
刚接触聚类?阅读我们的scikit-learn k-Means 聚类入门教程,了解算法原理及其在 Python 中的用法。
什么是轮廓系数?
轮廓系数是介于 -1 到 1 之间的数值,用来衡量每个点与其所属簇的贴合程度。
该数值反映两方面:
- 该点与其所在簇内其他点的接近程度
- 该点与最近的其他簇之间的远离程度
若某点稳居其簇内部并远离其他簇,得分就高。若位于边界,距离相邻簇比距离本簇还近,得分就低。
简而言之,轮廓系数体现了两种平衡:簇内点的紧密程度与簇间的分离程度。两者同样重要。仅簇内紧密但簇彼此重叠并无意义;仅簇间分离良好但簇内松散也不理想。

良好分离的簇 vs. 重叠的簇
分数越高,簇定义越清晰。接近 1 表示点在本簇内分布紧密且远离其他簇;接近 0 表示簇间有重叠或边界模糊;负值表示该点距离另一个簇更近,可能被误分配。
轮廓系数的工作原理
每个点都有自己的轮廓系数,该分数由两种距离决定。
第一种是该点与其所属簇内其他点的平均距离。距离小则说明靠近同簇其他点;距离大则说明与本簇的联系较松散。
第二种是该点与最近邻簇内各点的平均距离。距离大说明与其他簇分离明显;距离小则说明靠近边界。

某点到各簇的距离
轮廓值比较这两个数。深处本簇且远离他簇的点得分高;在边界附近的点得分低;更靠近他簇的点得分为负。
要记住,单独看其中任一距离都不够。
紧凑的簇也可能紧挨另一个簇;分离良好的簇内部也可能分散。两种距离兼顾,才能信任分配结果。
轮廓系数公式
单个点的公式如下:

轮廓系数公式
其中:
-
a为该点到同簇内所有其他点的平均距离 -
b为该点到最近邻簇中所有点的平均距离
该公式用二者之差 b 与 a 的差,除以二者较大者。
通常可按以下思路解读分数:
-
当
b远大于a:该点远离他簇且接近本簇。分子近似b,分母也是b,得分接近 1 -
当
a远大于b:该点更接近他簇而非本簇。分子为接近-a的较大负数,分母为a,得分接近 -1 -
当
a与b大致相等:该点位于两簇边界。分子接近 0,分数也接近 0
更多细节如下。
如何解读轮廓系数
以下是一个大致的解读指南:
- 接近 1: 点稳居其簇内部,且远离其他簇
- 约 0.7: 聚类较好,簇彼此可区分,点也靠近同簇其他点
- 约 0.5: 聚类尚可,簇间存在一定重叠,但仍能区分
- 接近 0: 簇重叠或边界不清,结构可能并不真实
- 低于 0: 点更接近错误的簇。可能是簇数选择不当,或数据本就不易聚类。
上述阈值只是粗略参考。何为“好”的轮廓系数取决于具体数据集。
稀疏且高维的数据,即使聚类不错,分数也常偏低;密集且分离良好的数据,分数可能高于 0.7。应在同一数据集上比较不同模型的分数,而非对照通用阈值。
如何计算轮廓系数
轮廓系数按点逐个计算。以下以单个点为例说明。
步骤 1: 计算 a,即该点到同簇内其他各点的平均距离。若该点所在簇还有 4 个点,距离分别为 1.2、1.5、1.0、1.3:

轮廓系数计算(1)
步骤 2: 找到最近的邻近簇(除本簇外,对该点的平均距离最低者)。然后计算 b,即该点到该簇内所有点的平均距离。若最近簇有 5 个点,与该点的距离为 2.4、2.8、3.0、2.6、2.7:

轮廓系数计算(2)
步骤 3: 将 a 与 b 代入公式:

轮廓系数计算(3)
步骤 4: 对数据集中的每个点重复上述过程。该次聚类的整体轮廓系数为所有点分数的平均值。
Python 中的轮廓系数示例
Scikit-learn 在 sklearn.metrics 中提供了两个计算轮廓系数的函数:
-
silhouette_score()返回整个聚类的平均分数 -
silhouette_samples()返回每个点的分数
以下是在合成数据集上配合 KMeans 的用法:
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
from sklearn.metrics import silhouette_score, silhouette_samples
# Synthetic data with 4 clusters
X, _ = make_blobs(n_samples=500, centers=4, cluster_std=1.0, random_state=42)
# Clustering model
kmeans = KMeans(n_clusters=4, random_state=42, n_init=10)
labels = kmeans.fit_predict(X)
# Overall silhouette score
score = silhouette_score(X, labels)
print(f"Overall silhouette score: {score:.3f}")
# Per-point silhouette values
sample_scores = silhouette_samples(X, labels)
print(f"First 5 point scores: {sample_scores[:5]}")
运行以上代码片段,您将得到如下输出:

Python 示例输出
整体得分 0.791 表示这四个簇定义清晰、彼此分离良好。
逐点分数便于对每个数据点进行分析。
单点分数高的点稳居其簇内部;分数低或为负的点位于边界或被误分配,这有助于识别离群点或复核边界样本。
选择最优簇数
轮廓系数最常见的用途之一是为 KMeans 选择合适的 k。
操作包含三步:
- 针对一系列 k 值拟合 KMeans
- 计算每次拟合的轮廓系数
- 选择分数最高的
k
对应代码如下:
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
from sklearn.metrics import silhouette_score
X, _ = make_blobs(n_samples=500, centers=4, cluster_std=1.0, random_state=42)
# Fit KMeans for k=2 to k=10 and keep track of the silhouette score for each
k_values = range(2, 11)
scores = []
for k in k_values:
kmeans = KMeans(n_clusters=k, random_state=42, n_init=10)
labels = kmeans.fit_predict(X)
scores.append(silhouette_score(X, labels))
print(f"k={k}: silhouette = {scores[-1]:.3f}")
对于每个 k,您会看到类似这样的分数输出:

不同 k 值下的轮廓系数
最高分数出现在 k=4。这就是应选择的值。
需要注意的是,轮廓系数并不能替代领域知识。若业务需要 5 个客户细分,而轮廓系数在 4 时最高,也别立刻选 4。该指标展示的是数据结构最清晰的地方,但有时“正确”的簇数是与您的业务意义相符的那个。
轮廓系数与其他聚类指标的比较
轮廓系数可能是最常用的聚类评估指标,但并非唯一。以下是与其他方法的对比。
轮廓系数 vs. 肘部法
肘部法是为 KMeans 选择 k 的可视化技巧。对一系列 k 拟合模型,绘制惯性(簇内平方和)与 k 的关系曲线,选择曲线出现明显“肘部”的位置。
肘部法的优势在于快速易用。但“肘部”并不总是明显。数据嘈杂时,曲线可能很平滑,难以判断拐点。
轮廓系数为每个 k 提供了具体数值,便于更有意义的比较。虽然计算更慢,但不必依赖主观的视觉判断。
用肘部法做快速健全性检查;当您需要更可据以决策的答案时,用轮廓系数。
轮廓系数 vs. Davies-Bouldin 指数
Davies-Bouldin 指数(DBI)衡量每个簇与其最相似簇之间的平均相似度。DBI 越低越好,完美分数为 0。
DBI 使用簇质心来计算相似度,因此在大数据集上比轮廓系数更快。同时,它也遵循“簇内紧凑、簇间分离”的直觉。
但 DBI 只给出整体聚类的表现。轮廓系数还能给出逐点分数,便于发现边界点和离群点。
当数据集很大且速度关键时用 DBI;当您想查看单个点时用轮廓系数。
轮廓系数 vs. Calinski-Harabasz 指数
Calinski-Harabasz 指数(CH,又称方差比准则)是簇间离散度与簇内离散度的比值。分数越高越好,且无上限。
CH 只涉及簇级统计,因此速度快,适合大数据集。与轮廓系数和 DBI 类似,它也更适用于凸且分离良好的簇。
CH 没有自然量纲,因此只能在同一数据集上比较不同模型的 CH 分数,不能跨数据集比较。
当数据集很大且需要快速得到结果时用 CH;当您需要可解释性与单点洞察时用轮廓系数。
内部指标 vs. 外部指标
上述指标(轮廓系数、DBI、CH、肘部法)都属于内部指标,只利用数据自身、无需真值标签来评估聚类。这也是真实世界聚类问题(通常没有标签)的标准选择。
外部指标会将聚类分配与已知标签比较。常见的有调整兰德指数(ARI)、归一化互信息(NMI)和同质性等。当您有标签并想检验聚类算法能否还原它们时使用外部指标。
没有标签(通常如此)时用内部指标;在带标签数据上对聚类算法做基准测试时用外部指标。
以下是各指标的并列回顾:
| 方法 | 取值范围 | 最佳值 | 速度 | 适用场景 |
|---|---|---|---|---|
| 轮廓系数 | -1 到 1 | 越接近 1 越好 | 大数据集上较慢 | 可解释性与逐点洞察 |
| 肘部法 | 0 到无穷 | 寻找“肘部” | 快 | 快速检查 |
| Davies-Bouldin 指数 | 0 到无穷 | 越接近 0 越好 | 快 | 大型数据集 |
| Calinski-Harabasz 指数 | 0 到无穷 | 越高越好 | 快 | 大型、无标签数据集 |
轮廓系数与替代方法的比较
轮廓系数的局限性
轮廓系数有一些值得注意的局限:
- 假设基于距离的聚类: 轮廓系数默认基于欧氏距离。它适用于 KMeans 等质心型算法,但并不适配 DBSCAN 等基于密度、形状任意且无明确质心的方法
- 更适合小且分离良好的簇: 该指标偏好紧密、近似球形、彼此遥远的簇。若数据中的簇彼此接近或重叠,即使聚类正确,分数也会偏低
- 不擅长不规则簇形: 真实世界的簇不总是凸的。若您的数据并非如此,尽管分配正确,簇的轮廓系数也可能较低
- 大数据集上的计算代价: 计算轮廓系数需要点对点的两两距离,复杂度为
O(n^2)。在上百万点的数据集上,成本会很高 - 对距离度量的敏感性:选择欧氏、曼哈顿、余弦或其他度量都会影响分数。若数据不符合欧氏假设,轮廓系数可能误导您
使用轮廓系数的最佳实践
遵循以下基本做法,轮廓系数的效果最佳:
- 比较多种聚类方案:不要只算一个轮廓系数。请用不同的
k(或不同算法)拟合聚类,并将分数并排比较 - 结合可视化一起查看: 单一数值讲不全故事。请绘制聚类结果,判断形状是否合理
- 不要只为分数最高而优化: 即便问题中 5 个簇更有意义,k=2 的聚类常常会有更高分数
- 结合领域知识: 分数告诉您数据结构何处最“干净”,领域知识告诉您何处最有用。两者并用
- 评估多种聚类指标: 轮廓系数、DBI、CH 等从不同角度衡量质量。当它们一致时,方向正确;不一致时,请回到数据本身
结论
轮廓系数是评估聚类最直观的方法之一,因为它同时体现簇内紧密度与簇间分离度。
它按点计算,取平均后得到一个介于 -1 与 1 的数。越接近 1,簇定义越清晰;接近或低于 0,则说明该“结构”可能并不成立。
不过,轮廓系数只是起点。应将其与聚类可视化以及最重要的领域知识结合。只有当这些线索指向同一方向时,您才能更放心地信任结果。
轮廓系数是Python 无监督学习全景的一部分。立即加入课程,学习如何在无标签数据上进行聚类、变换、可视化并提炼洞察。