如果有了类别标签,那么聚类结果也可以像分类那样计算准确率和召回率。但是不应该将分类标签作为聚类结果的评价指标,除非你有相关的先验知识或某种假设,知道这种分类类内差距更小。但是它还是给出了几种评价标准。
7.9.1 调整兰德系数 (Adjusted Rand index)
1. 数学原理
兰德系数(Rand index)需要给定实际类别信息C,假设K是聚类结果,a表示在C与K中都是同类别的元素对数,b表示在C与K中都是不同类别的元素对数,则兰德指数为:
对于以上公式,
分子:属性一致的样本数,即同属于这一类或都不属于这一类。a是真实在同一类、预测也在同一类的样本数;b是真实在不同类、预测也在不同类的样本数;
分母:任意两个样本为一类有多少种组合,是数据集中可以组成的总元素对数;
RI取值范围为[0,1],值越大意味着聚类结果与真实情况越吻合。
对于随机结果,RI并不能保证分数接近零。为了实现“在聚类结果随机产生的情况下,指标应该接近零”,调整兰德系数(Adjusted rand index)被提出,它具有更高的区分度:
ARI取值范围为[-1,1],值越大意味着聚类结果与真实情况越吻合。从广义的角度来讲,ARI衡量的是两个数据分布的吻合程度。
2. 优缺点
优点:
对任意数量的聚类中心和样本数,随机聚类的ARI都非常接近于0;
取值在[-1,1]之间,负数代表结果不好,越接近于1越好;
可用于聚类算法之间的比较。
缺点:
ARI需要真实标签
3. python代码实现
labels_true, labels_pred = check_clusterings(labels_true, labels_pred)
n_samples = labels_true.shape[0]
classes = np.unique(labels_true)
clusters = np.unique(labels_pred)
# Special limit cases: no clustering since the data is not split;
# or trivial clustering where each document is assigned a unique cluster.
# These are perfect matches hence return 1.0.
if (classes.shape[0] == clusters.shape[0] == 1 or classes.shape[0] == clusters.shape[0] == 0 or classes.shape[0] == clusters.shape[0] == len(labels_true)):
return 1.0
contingency = contingency_matrix(labels_true, labels_pred)
# Compute the ARI using the contingency data
sum_comb_c = sum(comb2(n_c) for n_c in contingency.sum(axis=1))
sum_comb_k = sum(comb2(n_k) for n_k in contingency.sum(axis=0))
sum_comb = sum(comb2(n_ij) for n_ij in contingency.flatten())
prod_comb = (sum_comb_c * sum_comb_k) / float(comb(n_samples, 2))
mean_comb = (sum_comb_k + sum_comb_c) / 2.
return ((sum_comb - prod_comb) / (mean_comb - prod_comb))
4. metrics类使用方法
设定已知先验知识的标签为labels_true,利用聚类算法预测的样本标签为label_pred,Adjusted Rand index函数是在计算样本预测值和真实值之间的相似度similarity:同属于这一类或都不属于这一类,而不考虑数据元素顺序和归一化。示例代码:
>>> from sklearn import metrics
>>> labels_true = [0, 0, 0, 1, 1, 1]
>>> labels_pred = [0, 0, 1, 1, 2, 2]
>>> metrics.adjusted_rand_score(labels_true, labels_pred)
0.24...
我们也可以调整预测集label_pred中元素0和1的位置,以及将数据集中为2的属性改名为3,其结果不受影响,示例代码:
>>> labels_pred = [1, 1, 0, 0, 3, 3]
>>> metrics.adjusted_rand_score(labels_true, labels_pred)
0.24...
此外,调整adjusted_rand_score函数中labels_true和labels_pred的位置,对结果没有影响,示例代码:
>>> metrics.adjusted_rand_score(labels_pred, labels_true)
0.24...
利用此函数评估模型最好的值为1,示例代码:
>>> labels_pred = labels_true[:]
>>> metrics.adjusted_rand_score(labels_true, labels_pred)
1.0
评估模型最差的值(与labels_true不相关),其结果为负值或接近0值,示例代码:
>>> labels_true = [0, 1, 2, 0, 3, 4, 5, 1]
>>> labels_pred = [1, 1, 0, 0, 2, 2, 2, 2]
>>> metrics.adjusted_rand_score(labels_true, labels_pred)
-0.12...
7.9.2 互信息评分(Mutual Information based scores)
1. 数学原理
互信息(Mutual Information)也是用来衡量两个数据分布的吻合程度。假设U与V是对N个样本标签的分配情况,则两种分布的熵(熵表示的是不确定程度)分别为:
其中:
U与V之间的互信息(MI)定义为:
其中:
标准化后的互信息(Normalized mutual information)为:
不管标签分配之间的“互信息”的实际数量如何,互信息或者标准化后的值不会因此而调整,而会随着标签(簇)数量的增加而增加。
互信息的期望值可以用如下公式来计算:
其中:
ai和bj分别对应着元素属于Ui和Vj的数量。
与ARI类似,调整互信息( Adjusted mutual information)定义为:
利用基于互信息的方法来衡量聚类效果需要实际类别信息,MI与NMI取值范围为[0,1],AMI取值范围为[-1,1],它们都是值越大意味着聚类结果与真实情况越吻合。
2. 优缺点
优点:除取值范围在[0,1]之间,其他同ARI,可用于聚类模型选择;
缺点:需要先验知识。
3. python代码实现
if contingency is None:
labels_true, labels_pred = check_clusterings(labels_true, labels_pred)
contingency = contingency_matrix(labels_true, labels_pred)
contingency = np.array(contingency, dtype='float')
contingency_sum = np.sum(contingency)
pi = np.sum(contingency, axis=1)
pj = np.sum(contingency, axis=0)
outer = np.outer(pi, pj)
nnz = contingency != 0.0
# normalized contingency
contingency_nm = contingency[nnz]
log_contingency_nm = np.log(contingency_nm)
contingency_nm /= contingency_sum
# log(a / b) should be calculated as log(a) - log(b) for
# possible loss of precision
log_outer = -np.log(outer[nnz]) + log(pi.sum()) + log(pj.sum())
mi = (contingency_nm * (log_contingency_nm - log(contingency_sum))+ contingency_nm * log_outer)
return mi.sum()
4. metrics类使用方法
设定已知先验知识的标签为labels_true,利用聚类算法预测的样本标签为label_pred,互信息是衡量两种预测的一致性,忽略排列的顺序。互信息评估有两种方法,标准化的互信息Normalized Mutual Information(NMI) 和调整后的互信息Adjusted Mutual Information(AMI)。示例代码:
>>> from sklearn import metrics
>>> labels_true = [0, 0, 0, 1, 1, 1]
>>> labels_pred = [0, 0, 1, 1, 2, 2]
>>> metrics.adjusted_mutual_info_score(labels_true, labels_pred)
0.22504...
我们也可以调整预测集label_pred中元素0和1的位置,以及将数据集中为2的属性改名为3,其结果不受影响,示例代码:
>>> labels_pred = [1, 1, 0, 0, 3, 3]
>>> metrics.adjusted_mutual_info_score(labels_true, labels_pred)
0.22504..
互信息评分中mutual_info_score,adjusted_mutual_info_score和normalized_mutual_info_score函数其参数都是对称的,交换的参数位置不会改变评分值,示例代码:
>>> metrics.adjusted_mutual_info_score(labels_pred, labels_true)
0.22504...
利用此函数评估模型最好的值为1,示例代码:
>>> labels_pred = labels_true[:]
>>> metrics.adjusted_mutual_info_score(labels_true, labels_pred)
1.0
>>> metrics.normalized_mutual_info_score(labels_true, labels_pred)
1.0
评估模型最差的值(与labels_true不相关),其结果为非正值,示例代码:
>>> labels_true = [0, 1, 2, 0, 3, 4, 5, 1]
>>> labels_pred = [1, 1, 0, 0, 2, 2, 2, 2]
>>> metrics.adjusted_mutual_info_score(labels_true, labels_pred)
-0.10526...
7.9.3 同质性Homogeneity 完整性completeness 调和平均V-measure
1. 数学原理
同质性homogeneity:每个群集只包含单个类的成员;
完整性completeness:给定类的所有成员都分配给同一个群集。
同质性和完整性分数基于以下公式得出:
其中H(C|K)是给定给定簇赋值的类的条件熵,由以下公式求得:
H(C)是类熵,公式为:
其中,n是样本总数,nc和nk分别属于类c和类k的样本数,而nc,k是从类c划分到类k的样本数量。
条件熵H(K|C)和类熵H(K),根据以上公式对称求得。
V-measure是同质性homogeneity和完整性completeness的调和平均数,公式:
2. 优缺点
优点:
分数明确:从0到1反应出最差到最优的表现;
解释直观:差的调和平均数可以在同质性和完整性方面做定性的分析;
对簇结构不作假设:可以比较两种聚类算法如k均值算法和谱聚类算法的结果。
缺点:
以前引入的度量在随机标记方面没有规范化,这意味着,根据样本数,集群和先验知识,完全随机标签并不总是产生相同的完整性和均匀性的值,所得调和平均值V-measure也不相同。特别是,随机标记不会产生零分,特别是当簇的数量很大时。
当样本数大于一千,聚类数小于10时,可以安全地忽略该问题。对于较小的样本量或更大数量的集群,使用经过调整的指数(如调整兰德指数)更为安全。
这些指标要求的先验知识,在实践中几乎不可用或需要手动分配的人作注解者(如在监督学习环境中)。
3. sklearn实现方法
sklearn.metrics类的homogeneity_score和completeness_score方法分别用来求得同质性和完整性分数,示例代码:
>>> from sklearn import metrics
>>> labels_true = [0, 0, 0, 1, 1, 1]
>>> labels_pred = [0, 0, 1, 1, 2, 2]
>>> metrics.homogeneity_score(labels_true, labels_pred)
0.66...
>>> metrics.completeness_score(labels_true, labels_pred)
0.42...
两者的调和平均V-measure,示例代码:
>>> metrics.v_measure_score(labels_true, labels_pred)
0.51...
metrics类的homogeneity_completeness_v_measure融合了以上方法,分别能求得相关值,示例代码:
>>> metrics.homogeneity_completeness_v_measure(labels_true, labels_pred)
...
(0.66..., 0.42..., 0.51...)
以下预测的评分略好,体现在同质性而非完整性,示例代码:
>>> labels_true = [0, 0, 0, 1, 1, 1]
>>> labels_pred = [0, 0, 0, 1, 2, 2]
>>> metrics.homogeneity_completeness_v_measure(labels_true, labels_pred)
...
(1.0, 0.68..., 0.81...)
7.9.4 Fowlkes-Mallows scores
Fowlkes-Mallows指数是针对训练集和验证集数据之间求得的查全率和查准率的几何平均值,其公式为:
示例代码:
>>> from sklearn import metrics
>>> labels_true = [0, 0, 0, 1, 1, 1]
>>> labels_pred = [0, 0, 1, 1, 2, 2]
>>>
>>> metrics.fowlkes_mallows_score(labels_true, labels_pred)
0.47140...
7.9.5 轮廓系数 Silhouette Coefficient
轮廓系数适用于实际类别信息未知的情况。对于单个样本,设a是与它同类别中其他样本的平均距离,b是与它距离最近不同类别中样本的平均距离,其轮廓系数为:
对于一个样本集合,它的轮廓系数是所有样本轮廓系数的平均值。
轮廓系数的取值范围是[-1,1],同类别样本距离越相近不同类别样本距离越远,分数越高。
示例代码:
>>> from sklearn import metrics
>>> from sklearn.metrics import pairwise_distances
>>> from sklearn import datasets
>>> dataset = datasets.load_iris()
>>> X = dataset.data
>>> y = dataset.target
>>> import numpy as np
>>> from sklearn.cluster import KMeans
>>> kmeans_model = KMeans(n_clusters=3, random_state=1).fit(X)
>>> labels = kmeans_model.labels_
>>> metrics.silhouette_score(X, labels, metric='euclidean')
...
0.55...