聚类性能评估中的随机性调整#

本笔记本探讨了均匀分布的随机标签对某些聚类评估指标行为的影响。为此,我们使用固定数量的样本,将指标计算为估计器所分配聚类数量的函数。该示例分为两个实验:

  • 第一个实验:使用固定的“基础事实标签”(因此类别数量固定)和随机生成的“预测标签”;

  • 第二个实验:使用变化范围内的“基础事实标签”和随机生成的“预测标签”。“预测标签”与“基础事实标签”具有相同数量的类和聚类。

# Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause

定义评估指标列表#

聚类算法本质上是无监督学习方法。然而,由于我们在本示例中为合成聚类分配了类标签,因此可以使用利用这些“监督”基础事实信息的评估指标来量化所得聚类的质量。此类指标的示例如下:

  • V-measure,即完整性和同质性的调和平均值;

  • 兰德指数 (Rand index),用于衡量数据点对根据聚类算法结果和基础事实类分配进行一致分组的频率;

  • 调整后的兰德指数 (ARI),一种经过随机性调整的兰德指数,使得随机聚类分配的预期 ARI 为 0.0;

  • 互信息 (MI) 是一种信息论度量,用于量化两个标签之间的相关程度。请注意,完美标签下 MI 的最大值取决于聚类的数量和样本量;

  • 标准化互信息 (NMI),定义在 0(无互信息,在大数据点极限下)和 1(完美匹配的标签分配,标签置换除外)之间。它未经随机性调整:当聚类数据点的数量不足时,随机标签的 MI 或 NMI 的期望值可能显著偏离零;

  • 调整后的互信息 (AMI),一种经过随机性调整的互信息。与 ARI 类似,随机聚类分配的预期 AMI 为 0.0。

更多信息,请参阅 聚类性能评估 模块。

from sklearn import metrics

score_funcs = [
    ("V-measure", metrics.v_measure_score),
    ("Rand index", metrics.rand_score),
    ("ARI", metrics.adjusted_rand_score),
    ("MI", metrics.mutual_info_score),
    ("NMI", metrics.normalized_mutual_info_score),
    ("AMI", metrics.adjusted_mutual_info_score),
]

第一个实验:固定的基础事实标签和不断增加的聚类数量#

我们首先定义一个创建均匀分布随机标签的函数。

import numpy as np

rng = np.random.RandomState(0)


def random_labels(n_samples, n_classes):
    return rng.randint(low=0, high=n_classes, size=n_samples)

另一个函数将使用 random_labels 函数来创建一组固定的基础事实标签(labels_a,分布在 n_classes 中),然后对几组随机“预测”的标签(labels_b)进行评分,以评估给定指标在特定 n_clusters 下的变异性。

def fixed_classes_uniform_labelings_scores(
    score_func, n_samples, n_clusters_range, n_classes, n_runs=5
):
    scores = np.zeros((len(n_clusters_range), n_runs))
    labels_a = random_labels(n_samples=n_samples, n_classes=n_classes)

    for i, n_clusters in enumerate(n_clusters_range):
        for j in range(n_runs):
            labels_b = random_labels(n_samples=n_samples, n_classes=n_clusters)
            scores[i, j] = score_func(labels_a, labels_b)
    return scores

在第一个示例中,我们将类别数量(真实的聚类数量)设置为 n_classes=10。聚类数量在 n_clusters_range 提供的值范围内变化。

import matplotlib.pyplot as plt
import seaborn as sns

n_samples = 1000
n_classes = 10
n_clusters_range = np.linspace(2, 100, 10).astype(int)
plots = []
names = []

sns.color_palette("colorblind")
plt.figure(1)

for marker, (score_name, score_func) in zip("d^vx.,", score_funcs):
    scores = fixed_classes_uniform_labelings_scores(
        score_func, n_samples, n_clusters_range, n_classes=n_classes
    )
    plots.append(
        plt.errorbar(
            n_clusters_range,
            scores.mean(axis=1),
            scores.std(axis=1),
            alpha=0.8,
            linewidth=1,
            marker=marker,
        )[0]
    )
    names.append(score_name)

plt.title(
    "Clustering measures for random uniform labeling\n"
    f"against reference assignment with {n_classes} classes"
)
plt.xlabel(f"Number of clusters (Number of samples is fixed to {n_samples})")
plt.ylabel("Score value")
plt.ylim(bottom=-0.05, top=1.05)
plt.legend(plots, names, bbox_to_anchor=(0.5, 0.5))
plt.show()
Clustering measures for random uniform labeling against reference assignment with 10 classes

n_clusters > n_classes 时,兰德指数会趋于饱和。其他非调整度量(如 V-Measure)显示出聚类数量与样本数量之间的线性依赖关系。

经过随机性调整的度量(如 ARI 和 AMI)显示出围绕 0.0 平均分的随机波动,且独立于样本和聚类的数量。

第二个实验:变化的类数量和聚类数量#

在本节中,我们定义了一个类似的函数,使用多个指标对 2 个均匀分布的随机标签进行评分。在这种情况下,对于 n_clusters_range 中的每个可能值,类数量和分配的聚类数量都是匹配的。

def uniform_labelings_scores(score_func, n_samples, n_clusters_range, n_runs=5):
    scores = np.zeros((len(n_clusters_range), n_runs))

    for i, n_clusters in enumerate(n_clusters_range):
        for j in range(n_runs):
            labels_a = random_labels(n_samples=n_samples, n_classes=n_clusters)
            labels_b = random_labels(n_samples=n_samples, n_classes=n_clusters)
            scores[i, j] = score_func(labels_a, labels_b)
    return scores

在这种情况下,我们使用 n_samples=100 来展示当聚类数量与样本数量相似或相等时产生的影响。

n_samples = 100
n_clusters_range = np.linspace(2, n_samples, 10).astype(int)

plt.figure(2)

plots = []
names = []

for marker, (score_name, score_func) in zip("d^vx.,", score_funcs):
    scores = uniform_labelings_scores(score_func, n_samples, n_clusters_range)
    plots.append(
        plt.errorbar(
            n_clusters_range,
            np.median(scores, axis=1),
            scores.std(axis=1),
            alpha=0.8,
            linewidth=2,
            marker=marker,
        )[0]
    )
    names.append(score_name)

plt.title(
    "Clustering measures for 2 random uniform labelings\nwith equal number of clusters"
)
plt.xlabel(f"Number of clusters (Number of samples is fixed to {n_samples})")
plt.ylabel("Score value")
plt.legend(plots, names)
plt.ylim(bottom=-0.05, top=1.05)
plt.show()
Clustering measures for 2 random uniform labelings with equal number of clusters

我们观察到的结果与第一个实验类似:经随机性调整的指标始终保持在接近零的水平,而其他指标则随着标签颗粒度的细化而趋于增大。随机标签的平均 V-measure 随着聚类数量接近用于计算该指标的样本总数而显著增加。此外,原始互信息是没有上限的,其尺度取决于聚类问题的维度和基础事实类别的基数。这就是曲线图超出范围的原因。

因此,只有调整后的指标才能安全地用作共识指数,用于评估聚类算法在数据集的各种重叠子样本上针对给定 k 值的平均稳定性。

非调整的聚类评估指标可能会产生误导,因为它们会为细粒度标签输出较大的值,这可能会让人误以为标签捕捉到了有意义的组,而实际上它们可能是完全随机的。特别是不应使用此类非调整指标来比较输出不同数量聚类的不同聚类算法的结果。

脚本运行总时间:(0 分 1.139 秒)

相关示例

DBSCAN 聚类算法演示

DBSCAN 聚类算法演示

使用 k-means 对文本文档进行聚类

使用 k-means 对文本文档进行聚类

使用 KMeans 聚类的轮廓分析选择簇的数量

使用 KMeans 聚类的轮廓分析选择簇的数量

使用不同度量的 Agglomerative 聚类

使用不同度量的 Agglomerative 聚类

由 Sphinx-Gallery 生成的图库