11.8 聚类内部评价指标#
在第11.6节内容中,我们详细介绍了聚类算法中几种常见的评价指标,包括纯度(准确率)、精确率、召回率、兰德系数和F值等。虽然这些评价指标都能很好的评估聚类结果的优劣,但是它们都有着一个共同的强使用条件,那就是需要知道训练样本的真实标签(Ground Truth),而这在现实情况中却很难做到。
对于聚类结果的评价方法一般可以分为内部评价指标(Internal Evaluation)与外部评价指标。所谓外部评价指标是指在知道真实标签的情况下来评估聚类结果的好坏,即上面提到几种评价指标。一般来说在做论文,或者是有少量的标注数据时,都可以用外部评价指标选择一个相对最优的聚类模型,然后再将其应用到其它未被标记的数据中。所谓内部评价指标则是不借助于外部信息,仅仅只是依靠聚类结果和样本本身的属性(空间分布特性等)来进行评估的办法。常见的内部评价指标有轮廓系数(Silhouette Coefficient)、卡林斯基-哈拉巴斯指数(Calinski-Harabasz Index, CHI)和戴维斯-布尔丁指数(Davies-Bouldin Index, DBI)等。在接下来的这节内容中,我们将会分别就这3种常见的内部评价指标进行介绍。
11.8.1 轮廓系数#
所谓轮廓系数[1]它本质上衡量的是每个样本点到其簇内样本的距离与其最近簇结构之间距离的比值。如果该比值越小,则说明该样本点所在的簇结构与其最近簇结构之间的距离越远,从而说明此时的聚类结果越好。
1. 计算原理
在介绍轮廓系数原理之前,首先需要明白的是对于聚类结果的轮廓系数,它实际上是所有样本点轮廓系数的平均值,也就是说聚类样本中的每个样本点对应都有一个轮廓系数值,而总的轮廓系数则是所有样本点轮廓系数的平均值。

如图11-16所示,数据样本中一共包含有3个簇结构,对于最左边这个簇中(圆形)的样本点$i$来说,距离其最近的簇为中间(方形)这个簇。现在定义样本$i$到簇中每个样本距离的均值为$a(i)$,到最近簇中每个样本距离的均值为$b(i)$,即此时根据图11-16所示的结果有
$$ \begin{aligned} a(i) &= \frac{a_1+a_2+a_3+a_4}{4}\\[2ex] b(i) &=\frac{b_1+b_2+b_3+b_4}{4} \end{aligned}\tag{11-40} $$这里需要注意的一点是,对于同一个簇中的每个样本点来说,距离自己最近的簇可能并不是同一个;同时,在寻找距离当前样本点最近的簇结构时,计算的是当前样本点到各个簇中心的最短距离,而不是计算当前样本点所在簇的簇中心到其它每个簇中心的最短距离。
此时,样本$i$的轮廓系数$s(i)$定义为
$$ s(i) = \begin{cases} 1-a(i)/b(i), & \text{if} \;a(i) < b(i) \\[2ex] 0, & \text{if} \;a(i) = b(i) \\[2ex] b(i)/a(i)-1 & \text{if} \;a(i) > b(i) \end{cases}\tag{11-41} $$从式(11-41)可以看出,当$a(i)$越小而$b(i)$越大时,此时对应的情况是样本$i$所在的簇中所有样本点之间距离都比较近(簇内距离较小),样本$i$所在的簇距离其最近簇的距离较远(因为此时样本$i$所在的簇中簇内距离较小,所以样本$i$到其它簇的距离可以近似地看做样本$i$所在簇到其它簇之间的距离),所以$s(i)$此时也就越接近于1,即聚类效果越好。
类似地,当$b(i)$越小而$a(i)$越大时,此时对应的情况是样本$i$所在的簇中所有样本点之间距离都比较远(簇内距离较大),样本$i$所在的簇距离其最近簇的距离较近,所以$s(i)$此时也就越接近于-1,即聚类效果越差。
最后,当样本$i$所在的簇中只有样本$i$一个样本时,那么对于样本$i$到底属于哪个簇就变得不清楚了,此时可以粗略地认为属于离它最近的簇中,所以此时有$a(i) = b(i)$,即$s(i)=0$表示一种中立的情况。
由此可知$s(i)$的取值范围应该是$-1\leq s(i)\leq 1$,且进一步式(11-41)可以改为
$$ s(i)=\frac{b(i)-a(i)}{\max\{a(i),b(i)\}}\tag{11-42} $$最终,整个聚类结果的轮廓系数结果为
$$ s=\frac{1}{n}\sum_{i=1}^ns(i)\tag{11-43} $$其中$n$表示样本点的个数。
因此,轮廓系数的取值范围为$[-1,1]$,并且值越大表示聚类结果越好。
2. 代码实现
在介绍完轮廓系数的原理和计算方式后,下面我们就来介绍如何编程实现这一过程,示例代码如下:
1 def get_silhouette_coefficient(X, labels):
2 n_clusters = np.unique(labels).shape[0]
3 s = []
4 for k in range(n_clusters): # 遍历每一个簇
5 index = (labels == k) # 取对应簇所有样本的索引
6 x_in_cluster = X[index] # 去对应簇中的所有样本
7 for sample in x_in_cluster: # 计算每个样本的轮廓系数
8 a = ((sample - x_in_cluster) ** 2).sum(axis=1)
9 a = np.sqrt(a).sum() / (len(a) - 1) # 去掉当前样本点与当前样本点的组合计数
10 nearest_cluster_id = None
11 min_dist2 = np.inf
12 for c in range(n_clusters): # 寻找距离当前样本点最近的簇
13 if k == c:
14 continue
15 centroid = X[labels == c].mean(axis=0)
16 dist2 = ((sample - centroid) ** 2).sum()
17 if dist2 < min_dist2:
18 nearest_cluster_id = c
19 min_dist2 = dist2
20 x_nearest_cluster = X[labels == nearest_cluster_id]
21 b = ((sample - x_nearest_cluster) ** 2).sum(axis=1)
22 b = np.sqrt(b).mean()
23 s.append((b - a) / np.max([a, b]))
24 return np.mean(s)在上述代码中,第1行X和labels分别是原始训练集和聚类得到的标签。第2行是取聚类结果的簇数量。第4~6行是遍历每个簇并取对应中所有的样本。第7行开始是遍历每一个样本点来计算轮廓系数。第8~9行是计算当前样本距离其簇中每个样本点之间的平均距离,其中第9行分母减去1是因为要去掉当前样本与当前样本之间距离为0的情况。第10~19行是计算得到里当前样本点最近的簇,其中15行是计算簇中心。第20行是取距离当前样本点最近的簇对应的所有样本点。第21~22行是计算当前样本点到其最近簇中所有样本的平均距离。第23行是计算当前样本点对应的轮廓系数并保存。第24行是返回所有样本对应的轮廓系数的均值。
完成上述代码实现之后,便可以通过如下方式进行使用,示例代码如下:
1 import numpy as np
2 from sklearn.cluster import KMeans
3 from sklearn.datasets import load_iris
4 from sklearn.metrics import silhouette_score
5
6 def test_silhouette_score():
7 x, y = load_iris(return_X_y=True)
8 model = KMeans(n_clusters=3)
9 model.fit(x)
10 y_pred = model.predict(x)
11 print(f"轮廓系数 by sklearn: {silhouette_score(x, y_pred)}")
12 print(f"轮廓系数 by ours: {get_silhouette_coefficient(x, y_pred)}")
13
14
15 if __name__ == '__main__':
16 test_silhouette_score()上述代码运行结束后的结果如下:
1 轮廓系数 by sklearn: 0.5528
2 轮廓系数 by ours: 0.552811.8.2 卡林斯基-哈拉巴斯指数#
在介绍完轮廓系数后我们再来看第2种内部评价指标卡林斯基-哈拉巴斯指数[2]。由于卡林斯基-哈拉巴斯指数指数的本质是簇间距离与簇内距离的比值,且整体计算过程与方差计算方式类似,所以又将其称之为方差比准则。