第52课 聚类评估:轮廓系数与 Calinski-Harabasz
所属模块:无监督学习
难度:中级
标签:聚类, 评估, 机器学习
上一课:DBSCAN 密度聚类
下一课:t-SNE 高维数据可视化
1. 学习目标
完成本课学习后,你将能够:
- 理解为什么聚类算法需要外部评估指标。
- 解释轮廓系数和 Calinski-Harabasz 指数的核心思想与计算逻辑。
- 运用 Scikit-learn 计算并解读这两个指标。
- 实践在给定数据集上,利用评估指标辅助选择最佳的聚类数量或算法。
2. 核心概念
与有监督学习不同,聚类是“无标签”的学习,我们没有明确的“正确答案”来衡量聚类效果。然而,我们仍然可以通过衡量聚类结果本身的“质量”来评估好坏。好的聚类应该是:“簇内紧密,簇间分离”。
- 簇内紧密 (Cohesion):同一个簇内的样本点彼此靠近。
- 簇间分离 (Separation):不同簇的样本点彼此远离。
我们今天学习的两个指标就是从这两个角度来量化评估的。
轮廓系数 (Silhouette Coefficient)
轮廓系数结合了凝聚度和分离度。对于样本空间中的每个点 i:
- 计算
a(i): 点i到同一簇内所有其他点的平均距离(衡量簇内紧密度)。 - 计算
b(i): 点i到最近的一个非自身簇中所有点的平均距离(衡量簇间分离度)。 - 点
i的轮廓系数为:最终的轮廓系数是所有样本s(i) = (b(i) - a(i)) / max(a(i), b(i))s(i)的平均值。
解读:
- 接近 +1:表示样本
i离自己的簇很近,离相邻的簇很远,聚类效果好。 - 接近 0:表示样本
i在两个簇的边界上。 - 接近 -1:表示样本
i可能被分到了错误的簇。
Calinski-Harabasz 指数 (CH Index)
也称为方差比准则。它直接计算簇间方差(衡量分离度)与簇内方差(衡量凝聚度)的比值。
CH = (簇间方差 / (K-1)) / (簇内方差 / (N-K))
其中,K 是簇的数量,N 是样本总数。
解读:
- 值越大,表示簇间越分散(分离度好),簇内越紧凑(凝聚度好),即聚类效果越好。
- 该指数没有上界,通常用于比较不同聚类结果(例如,不同
K值的结果)。
3. 代码示例
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score, calinski_harabasz_score
# 1. 生成示例数据:创建三个清晰分开的簇
X, y_true = make_blobs(n_samples=300, centers=4, cluster_std=0.8, random_state=42)
# 2. 初始化一个评估函数,方便复用
def evaluate_clustering(X, labels):
"""计算并返回轮廓系数和CH指数"""
sil_score = silhouette_score(X, labels)
ch_score = calinski_harabasz_score(X, labels)
return sil_score, ch_score
# 3. 比较不同聚类数量 (K=2, 3, 4, 5) 的效果
k_values = [2, 3, 4, 5]
sil_scores = []
ch_scores = []
plt.figure(figsize=(12, 5))
for i, k in enumerate(k_values):
kmeans = KMeans(n_clusters=k, random_state=42, n_init=10)
labels = kmeans.fit_predict(X)
sil, ch = evaluate_clustering(X, labels)
sil_scores.append(sil)
ch_scores.append(ch)
# 绘制子图展示聚类结果
plt.subplot(1, len(k_values), i+1)
plt.scatter(X[:, 0], X[:, 1], c=labels, cmap='viridis', s=30)
plt.title(f'K={k}\nSilhouette: {sil:.3f}\nCH Index: {ch:.1f}')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.tight_layout()
plt.show()
# 4. 打印评估指标,并找出最佳K值
print("聚类评估结果:")
for k, sil, ch in zip(k_values, sil_scores, ch_scores):
print(f"K={k}: 轮廓系数={sil:.3f}, CH指数={ch:.1f}")
# 根据轮廓系数选择最佳K
best_k_sil = k_values[np.argmax(sil_scores)]
# 根据CH指数选择最佳K
best_k_ch = k_values[np.argmax(ch_scores)]
print(f"\n根据轮廓系数,建议的聚类数量为: {best_k_sil}")
print(f"根据CH指数,建议的聚类数量为: {best_k_ch}")
输出解释: 运行上述代码,你会看到四个子图和评估结果。对于这个精心生成的、有4个中心的数据集,K=4 时两个指标通常都会达到或接近最优值。这证明了评估指标可以帮助我们识别数据中固有的簇数量。
4. 实践练习
练习1:基础计算
使用以下数据 X 和标签 labels,手动计算其轮廓系数和 CH 指数(使用 Scikit-learn 函数),并解释结果。
from sklearn.metrics import silhouette_score, calinski_harabasz_score
import numpy as np
X = np.array([[1, 1], [1.5, 2], [3, 4], [5, 7], [3.5, 5], [4.5, 5], [3.5, 4.5]])
labels = np.array([0, 0, 1, 2, 1, 1, 1]) # 分成了3个簇
要求:计算指标值,并判断这个聚类结果是“好”还是“一般”。
练习2:K值选择实战
给定下面的二维数据 X_exercise,请你完成:
- 对 K 从 2 到 6 进行 K-Means 聚类。
- 计算每个 K 下的轮廓系数和 CH 指数。
- 绘制出两个指标随 K 变化的折线图。
- 根据图表,你会选择哪个 K 值?为什么?
from sklearn.datasets import make_moons
X_exercise, _ = make_moons(n_samples=200, noise=0.1, random_state=42)
# 注意:make_moons生成的数据形状比较特殊,对K-Means是个挑战。
练习3:对比不同算法
在练习2的数据集 X_exercise 上,分别使用 K-Means (K=2) 和 DBSCAN (eps=0.2, min_samples=5) 进行聚类,并计算它们的轮廓系数。根据结果,哪种算法更适合该数据集?为什么?
5. 常见错误
- 忽略数据标准化:计算距离时,量纲大的特征会主导距离。在计算轮廓系数和CH指数前,务必对数据进行标准化(
StandardScaler)。 - 仅依赖单一指标:不同指标可能给出不同建议。应结合可视化、业务理解和多个指标综合判断。
- 忽略样本量和簇数的影响:轮廓系数在簇数
K接近样本数N时容易失真;CH指数随K增大而减小的趋势也需要谨慎解读。通常寻找指标的“肘点”或最大值。 - 对噪声敏感:特别是 DBSCAN 这类可能产生噪声点(标签为-1)的算法。Scikit-learn 的评估函数通常不接受包含 -1 的标签,需要先过滤掉噪声点再进行评估。
6. 小结
本课我们学习了如何客观地评估聚类结果的质量:
- 为什么需要评估:聚类无监督,需要量化指标来指导决策(如选择K值、比较算法)。
- 轮廓系数:综合衡量簇内紧密和簇间分离,值在[-1, 1]之间,越接近1越好。
- Calinski-Harabasz 指数:基于方差比,衡量簇间离散度与簇内紧密度的比值,值越大越好。
- 实战流程:通过生成/选择数据 -> 尝试不同聚类参数 -> 计算评估指标 -> 可视化对比 -> 选择最优结果。
记住,这些指标是强大的工具,但不是绝对的真理。最终的选择应结合领域知识和实际需求。
下一课预告:当数据维度很高时,我们如何直观地查看聚类效果?我们将学习 t-SNE 这一强大的降维可视化技术。