前言:度量聚类算法的性能不是简单的统计错误的数量或计算监督分类算法中的 precision (准确率)和 recall (召回率)。聚类算法的评价指标有很多,本文主要是基于sklearn机器学习库,里面提供了一系列的度量函数,在这些度量函数里面,有的需要知道真实的样本类别,然后有的聚类本来就没有真实的样本类别,甚至像DBSCAN这样的聚类方法,连到底有几个类别都不确定,那怎么去评价聚类的好坏呢,本文专门来看一下“无真实标签的聚类评价”

一、sklearn中的聚类评价指标

1.1 聚类简介

聚类是一种无监督学习算法,训练样本的标记未知,按照某个标准或数据的内在性质及规律,将样本划分为若干个不相交的子集,每个子集称为一个簇(cluster),每个簇中至少包含一个对象,每个对象属于且仅属于一个簇;簇内部的数据相似度较高,簇之间的数据相似度很低。聚类可以作为分类等其他学习任务的前驱过程。

基于不同的学习策略,聚类算法可分为多种类型:

sklearn 聚类算法的紧密度 sklearn 聚类评价指标_sklearn.metrics

1.2 sklearn 聚类评价函数大全

sklearn中的聚类评价指标有很多,参见下面

metrics.adjusted_mutual_info_score(…[, …])	
metrics.adjusted_rand_score(labels_true, …)	
metrics.calinski_harabasz_score(X, labels)	
metrics.davies_bouldin_score(X, labels)
metrics.completeness_score(labels_true, …)	
metrics.cluster.contingency_matrix(…[, …])	
metrics.fowlkes_mallows_score(labels_true, …)	
metrics.homogeneity_completeness_v_measure(…)	
metrics.homogeneity_score(labels_true, …)	
metrics.mutual_info_score(labels_true, …)	
metrics.normalized_mutual_info_score(…[, …])	
metrics.silhouette_score(X, labels[, …])	
metrics.silhouette_samples(X, labels[, metric])
metrics.v_measure_score(labels_true, labels_pred)

在这些评价函数里面,有一部分需要“参数label_true”的,但是有很多时候聚类是没有真实类别的,所以本文先不考虑这种类型,只考虑不需要label_true的三种评价函数。

外部度量:需要知道真实标签的度量

内部度量:在真实的分群label不知道的情况下

二、三种评价函数(无label_true)

2.1 轮廓系数(Silhouette Coefficient)

轮廓系数(Silhouette Coefficient),是聚类效果好坏的一种评价方式。最早由 Peter J. Rousseeuw 在 1986 提出。它结合内聚度分离度两种因素。可以用来在相同原始数据的基础上用来评价不同算法、或者算法不同运行方式对聚类结果所产生的影响。

具体方法:

(1)计算样本i到同簇其他样本的平均距离ai。ai 越小,说明样本i越应该被聚类到该簇。将ai 称为样本i的簇内不相似度。某一个簇C中所有样本的a i 均值称为簇C的簇不相似度

(2)计算样本i到其他某簇Cj 的所有样本的平均距离bij,称为样本i与簇Cj 的不相似度。定义为样本i的簇间不相似度:bi =min{bi1, bi2, ..., bik},即某一个样本的簇间不相似度为该样本到所有其他簇的所有样本的平均距离中最小的那一个。

bi越大,说明样本i越不属于其他簇。

(3)根据样本i的簇内不相似度a i 和簇间不相似度b i ,定义某一个样本样本i的轮廓系数:

sklearn 聚类算法的紧密度 sklearn 聚类评价指标_sklearn.metrics_02

(4)判断:

si接近1,则说明样本i聚类合理;

si接近-1,则说明样本i更应该分类到另外的簇;

若si 近似为0,则说明样本i在两个簇的边界上。

(5) 所有样本的轮廓系数S

所有样本的s i 的均值称为聚类结果的轮廓系数,定义为S,是该聚类是否合理、有效的度量。聚类结果的轮廓系数的取值在【-1,1】之间,值越大,说明同类样本相距约近,不同样本相距越远,则聚类效果越好。

轮廓系数的优缺点总结:

优点

  • 对于不正确的 clustering (聚类),分数为 -1 , highly dense clustering (高密度聚类)为 +1 。零点附近的分数表示 overlapping clusters (重叠的聚类)。
  • 当 clusters (簇)密集且分离较好时,分数更高,这与 cluster (簇)的标准概念有关。

缺点

  • convex clusters(凸的簇)的 Silhouette Coefficient 通常比其他类型的 cluster (簇)更高,例如通过 DBSCAN 获得的基于密度的 cluster(簇)。

sklearn中的接口:

轮廓系数以及其他的评价函数都定义在sklearn.metrics模块中,

sklearn中函数silhouette_score()计算所有点的平均轮廓系数,而silhouette_samples()返回每个点的轮廓系数。后面会给出具体的例子的。它的定义如下:

def silhouette_score(X, labels, metric='euclidean', sample_size=None,
random_state=None, **kwds):
'''
X:表示要聚类的样本数据,一般形如(samples,features)的格式
labels:即聚类之后得到的label标签,形如(samples,)的格式
metric:默认是欧氏距离
'''

2.2 CH分数(Calinski Harabasz Score )

也称之为 Calinski-Harabaz Index

这个计算简单直接,得到的Calinski-Harabasz分数值ss越大则聚类效果越好。Calinski-Harabasz分数值ss的数学计算公式是(理论介绍来源于:用scikit-learn学习K-Means聚类):

sklearn 聚类算法的紧密度 sklearn 聚类评价指标_sklearn.metrics_03

sklearn 聚类算法的紧密度 sklearn 聚类评价指标_sklearn.metrics_04

其中

Bk称之为 between-clusters dispersion mean (簇间色散平均值)

Wk称之为 within-cluster dispersion(群内色散之间)

它们的计算公式如下:

sklearn 聚类算法的紧密度 sklearn 聚类评价指标_sklearn.metrics_05

类别内部数据的协方差越小越好,类别之间的协方差越大越好,这样的Calinski-Harabasz分数会高。 总结起来一句话:

CH index的数值越大越好。

在真实的分群label不知道的情况下,可以作为评估模型的一个指标。 同时,数值越小可以理解为:组间协方差很小,组与组之间界限不明显。 与轮廓系数的对比,笔者觉得最大的优势:快!相差几百倍!毫秒级,

优缺点总结:

优点

  • 当 cluster (簇)密集且分离较好时,分数更高,这与一个标准的 cluster(簇)有关。
  • 得分计算很快

缺点

  • 凸的簇的 Calinski-Harabaz index(Calinski-Harabaz 指数)通常高于其他类型的 cluster(簇),例如通过 DBSCAN 获得的基于密度的 cluster(簇)。

在sklearn中的接口:

在scikit-learn中, Calinski-Harabasz Index对应的方法是metrics.calinski_harabaz_score. 它的定义如下:

def calinski_harabasz_score(X, labels):
'''
X:表示要聚类的样本数据,一般形如(samples,features)的格式
labels:即聚类之后得到的label标签,形如(samples,)的格式
'''

2.3 戴维森堡丁指数(DBI)——davies_bouldin_score

戴维森堡丁指数(DBI),又称为分类适确性指标,是由大卫L·戴维斯和唐纳德·Bouldin提出的一种评估聚类算法优劣的指标。首先假设我们有m个时间序列,这些时间序列聚类为n个簇。m个时间序列设为输入矩阵X,n个簇类设为N作为参数传入算法。使用下列公式进行计算:

sklearn 聚类算法的紧密度 sklearn 聚类评价指标_sklearn聚类评价指标_06

这个公式的含义是度量每个簇类最大相似度的均值。

接下来是算法的具体计算步骤:

(1)计算Si。DBI定义了一个分散度的值Si:表示第i个类中,度量数据点的分散程度,

    DBI计算公式中首先定义了Si变量,Si计算的是类内数据到簇质心的平均距离,代表了簇类i中各样本的分散程度,计算公式为:

sklearn 聚类算法的紧密度 sklearn 聚类评价指标_sklearn聚类评价指标_07

其中Xj代表簇类i中第j个数据点,也就是一个样本点,Ai是簇类i的质心,Ti是簇类i中数据的个数,p在通常情况下取2,这样就可以计算独立的数据点和质心的欧式距离(euclidean metric),此时表示各点到中心距离的标准差,它们都可以用来衡量分散程度当然在考察流型和高维数据的时候,欧氏距离也许不是最佳的距离计算方式,但也是比较典型的了。

(2)计算Mij。DBI定义了一个距离值Mij:表示第i类与第j类的距离,

分子之和计算完后,需计算分母Mij,定义为簇类i与簇类j的距离,计算公式为:

sklearn 聚类算法的紧密度 sklearn 聚类评价指标_sklearn.metrics_08

ak,i代表簇类i质心点的第k个值,Mij就是簇类i与簇类j质心的距离。即aki 表示第i类的中心点的第K个属性的值,Mij则就是第i类与第j类中心的距离。

(3)计算Rij。DBI定义了一个相似度的值Rij:
Rij 衡量第i类与第j类的相似度。计算公式为:

sklearn 聚类算法的紧密度 sklearn 聚类评价指标_sklearn.metrics_09

(4)计算DBI

   通过以上公式的计算,我们再从Rij中选出最大值Ri=max(Rij),即,第i类与其他类的相似度中最大的相似度的值。

最后计算每个类的这些最大相似度的均值,便得到了DBI指数:

sklearn 聚类算法的紧密度 sklearn 聚类评价指标_sklearn.metrics_10

sklearn 聚类算法的紧密度 sklearn 聚类评价指标_sklearn.metrics_11值越小,分类效果越好。

注意:DBI的值最小是0,值越小,代表聚类效果越好。

DBI的sklearn中的定义:

def davies_bouldin_score(X, labels):
'''
X:表示要聚类的样本数据,一般形如(samples,features)的格式
labels:即聚类之后得到的label标签,形如(samples,)的格式
'''

三、实际案例

本次的实际案例会展示这三个评价指标的用法,使用的聚类算法是DBSCAN算法,

3.1 原始样本如下:

sklearn 聚类算法的紧密度 sklearn 聚类评价指标_sklearn聚类评价指标_12

数据维度为 (7283,2),即每一个点都是一个2维的向量,至于这个数据怎么来的,这实际上是我自己在项目中的一个数据,自己可以生成或者找一些其他的数据也可以。

3.2 使用DBSCAN聚类

聚类之后的结果为:

sklearn 聚类算法的紧密度 sklearn 聚类评价指标_sklearn聚类评价指标_13

由此可见一共分成了3个类别,黑色的代表噪声点,有关DBSCAN聚类的原理这里就不再展开了,

3.3 评价函数

这里就参考完整的代码即可

import numpy as np
import matplotlib.pyplot as plt
import glog as log

from sklearn.cluster import DBSCAN  # 进行DBSCAN聚类
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import silhouette_score ,calinski_harabasz_score,davies_bouldin_score # 计算 轮廓系数,CH 指标,DBI 


# 定义一个进行DBSCAN的函数
def DBSCAN_Cluster(embedding_image_feats):
    """
    dbscan cluster
    :param embedding_image_feats:  # 比如形状是(9434,4)表示9434个像素点
    :return:
    """
    db = DBSCAN(eps=0.35, min_samples=600)
    try:
        features = StandardScaler().fit_transform(embedding_image_feats)  # 将特征进行归一化
        db.fit(features)
    except Exception as err:
        log.error(err)   
        ret = {
            'origin_features': None,
            'cluster_nums': 0,
            'db_labels': None,
            'cluster_center': None
            }
        return ret

    db_labels = db.labels_                  # 获取聚类之后没一个样本的类别标签
    unique_labels = np.unique(db_labels)    # 获取唯一的类别

    num_clusters = len(unique_labels)
    cluster_centers = db.components_

    ret = {
            'origin_features': features,      #(9434,4)
            'cluster_nums': num_clusters,     # 5  它是一个标量,表示5类,包含背景
            'db_labels': db_labels,           #(9434,)
            'unique_labels': unique_labels,   #(5,)
            'cluster_center': cluster_centers #(6425,4)
        }

    return ret

# 画出聚类之后的结果
def plot_dbscan_result(features,db_labels,unique_labels,num_clusters):
    colors = plt.cm.Spectral(np.linspace(0, 1, len(unique_labels)))
    for k, color in zip(unique_labels, colors):
        if k == -1:
           color = 'k'  # 黑色的,这代表噪声点

        index=np.where(db_labels==k)   #  获取每一个类别的索引位置
        x=features[index]

        plt.plot(x[:, 0], x[:, 1], 'o', markerfacecolor=color,markeredgecolor='k', markersize=6)

    plt.title('Estimated number of clusters: %d' % num_clusters)
    plt.show()


if __name__=='__main__':
    embedding_features=np.load("./tools/features_logits/lane_embedding_feats.npy")  # 导入数据,数据格式为(samples,)

    ret=DBSCAN_Cluster(embedding_features)  # 进行 DBSCAN聚类

    plot_dbscan_result(ret['origin_features'],ret['db_labels'],ret['unique_labels'],ret['cluster_nums']) # 展示聚类之后的结果

    
    s1=silhouette_score(embedding_features, ret['db_labels'], metric='euclidean') # 计算轮廓系数
    s2=calinski_harabasz_score(embedding_features,ret['db_labels']) # 计算CH score
    s3=davies_bouldin_score(embedding_features,ret['db_labels'])    # 计算 DBI

    print(s1)
    print(s2)
    print(s3)

'''运行结果为:
0.7971864
48375.80213812995
0.8799878743935938
'''