不同于分类模型和回归,聚类算法的模型评估不是一件简单的事。在分类中,有直接结果(标签)的输出,并且分类的结果有正误之分,所以我们使用预测的准确度等指标来进行评估,但无论如何评估,都是在”模型找到正确答案“的能力。回归的评估也类似分类,都是基于标签的评估。但这些衡量指标都不能够使用于聚类。

那么如何衡量聚类算法的效果?

记得我们说过,KMeans的目标是确保“簇内差异小,簇外差异大”,我们就可以通过衡量簇内差异来衡量聚类的效果。我们刚才说过,Inertia是用距离来衡量簇内差异的指标,因此,我们可以使用Inertia来作为聚类的衡量指标,但是这个指标的缺点和极限太大。

  1. 它没有上界。我们只知道,Inertia是越小越好,是0最好,但我们不知道,一个较小的Inertia究竟有没有达到模型的极限,能否继续提高。我们也无法说一个数字对于当前模型到底是大还是小
  2. 它的计算太容易受到特征数目的影响,数据维度很大的时候,Inertia的计算量会陷入维度诅咒之中,计算量会爆炸,不适合用来一次次评估模型。
  3. 它会受到超参数K的影响,在我们之前的尝试中已经发现,随着K越大,Inertia注定会越来越小,但这并不代表模型的效果越来越好了
  4. Inertia对数据的分布有假设,它假设数据满足凸分布(即数据在二维平面图像上看起来是一个凸函数的样子),并且它假设数据是各向同性的(isotropic),即是说数据的属性在不同方向上代表着相同的含义。但是现实中的数据往往不是这样。所以使用Inertia作为评估指标,会让聚类算法在一些细长簇,环形簇,或者不规则形状的流形时表现不佳:

【菜菜的sklearn课堂笔记】聚类算法Kmeans-聚类算法的模型评估指标-LMLPHP

那我们可以使用什么指标呢?分两种情况来看。

当真实标签已知的时候

在现实中,拥有真实标签的情况非常少见(几乎是不可能的)。如果拥有真实标签,我们更倾向于使用分类算法。但不排除我们依然可能使用聚类算法的可能性。如果我们有样本真实聚类情况的数据,我们可以对于聚类算法的结果和真实结果来衡量聚类的效果。常用的有以下三种方法:

当真实标签未知的时候:轮廓系数

在99%的情况下,我们是对没有真实标签的数据进行探索,也就是对不知道真正答案的数据进行聚类。这样的聚类,是完全依赖于评价簇内的稠密程度(簇内差异小)和簇间的离散程度(簇外差异大)来评估聚类的效果。
轮廓系数是最常用的聚类算法的评价指标。它是对每个样本来定义的,它能够同时衡量:

  • 样本与其自身所在的簇中的其他样本的相似度a,等于样本与同一簇中所有其他点之间的平均距离
  • 样本与其他簇中的样本的相似度b,等于样本与下一个最近的簇中的所有点之间的平均距离

根据聚类的要求”簇内差异小,簇外差异大“,我们希望b永远大于a,并且大得越多越好。
单个样本的轮廓系数计算为:
s = b − a max ⁡ ( a , b ) s=\frac{b-a}{\max (a,b)} s=max(a,b)ba
这个公式可以被解析为:
s = { 1 − a b a < b 0 a = b b a − 1 a > b s=\left\{\begin{aligned}&1- \frac{a}{b}&a<b\\&0&a=b\\& \frac{b}{a}-1&a>b\end{aligned}\right. s=1ba0ab1a<ba=ba>b
很容易理解轮廓系数范围是(-1,1),其中值越接近1表示样本与自己所在的簇中的样本很相似,并且与其他簇中的样本不相似,当样本点与簇外的样本更相似的时候,轮廓系数就为负。当轮廓系数为0时,则代表两个簇中的样本相似度一致,两个簇本应该是一个簇。可以总结为轮廓系数越接近于1越好,负数则表示聚类效果非常差
如果许多样本点具有低轮廓系数甚至负值,则聚类是不合适的,聚类的超参数K可能设定得太大或者太小。

在sklearn中,我们使用模块metrics中的类silhouette_score来计算轮廓系数,它返回的是一个数据集中,所有样本的轮廓系数的均值。但我们还有同在metrics模块中的silhouette_sample,它的参数与轮廓系数一致,但返回的是数据集中每个样本自己的轮廓系数。

from sklearn.metrics import silhouette_score
from sklearn.metrics import silhouette_samples

silhouette_score(X,y_pred)
---
0.5882004012129721

silhouette_samples(X,y_pred)
---
array([ 0.62982017,  0.5034877 ,  0.56148795,  0.84881844,  0.56034142,
        0.78740319,  0.39254042,  0.4424015 ,  0.48582704,  0.41586457,
……

silhouette_samples(X,y_pred).shape
---
(500,)

silhouette_samples(X,y_pred).mean()
---
0.5882004012129721

silhouette_score(X,cluster_.labels_) # n_cluster = 4
---
0.6505186632729437

silhouette_score(X,cluster_.labels_) # n_cluster = 5
---
0.5737098048695828

silhouette_score(X,cluster_.labels_) # n_cluster = 6
---
0.4532882033128697

轮廓系数有很多优点,它在有限空间中取值,使得我们对模型的聚类效果有一个“参考”。并且,轮廓系数对数据的分布没有假设,因此在很多数据集上都表现良好。但它在每个簇的分割比较清晰时表现最好。
但轮廓系数也有缺陷,它在凸型的类上表现会虚高,比如基于密度进行的聚类,或通过DBSCAN获得的聚类结果,如果使用轮廓系数来衡量,则会表现出比真实聚类效果更高的分数。

当真实标签未知的时候:卡林斯基-哈拉巴斯指数

除了轮廓系数是最常用的,我们还有卡林斯基-哈拉巴斯指数(Calinski-Harabaz Index,简称CHI,也被称为方差比标准),戴维斯-布尔丁指数(Davies-Bouldin)以及权变矩阵(Contingency Matrix)可以使用。

在这里我们重点来了解一下卡林斯基-哈拉巴斯指数。Calinski-Harabaz指数越高越好。对于有k个簇的聚类而言, Calinski-Harabaz指数s(k)写作如下公式:
s ( k ) = T r ( B k ) T r ( W k ) ⋅ N − k k − 1 s(k)=\frac{Tr(B_{k})}{Tr(W_{k})}\cdot \frac{N-k}{k-1} s(k)=Tr(Wk)Tr(Bk)k1Nk
其中N为数据集中的样本量,k为簇的个数(即类别的个数), B k B_{k} Bk是组间离散矩阵,即不同簇之间的协方差矩阵, W k W_{k} Wk是簇内离散矩阵,即一个簇内数据的协方差矩阵,而 T r Tr Tr表示矩阵的迹。
数据之间的离散程度越高,协方差矩阵的迹就会越大。组内离散程度低,协方差的迹就会越小, T r ( W k ) Tr(W_{k}) Tr(Wk)也就越小,同时,组间离散程度大,协方差的的迹也会越大, T r ( B k ) Tr(B_{k}) Tr(Bk)就越大,这正是我们希望的,因此Calinski-harabaz指数越高越好。

from sklearn.metrics import calinski_harabasz_score

calinski_harabasz_score(X,y_pred)
---
1809.991966958033

虽然calinski-Harabaz指数没有界,在凸型的数据上的聚类也会表现虚高。但是比起轮廓系数,它有一个巨大的优点,就是计算非常快速。

from time import time

t0 = time()
calinski_harabasz_score(X,y_pred)
time() - t0
---
0.0010504722595214844

t0 = time()
silhouette_score(X,y_pred)
time() - t0
---
0.01594376564025879
12-04 06:35