Scikit-Learn 1.4使用指南:无监督学习 聚类 Clustering


聚类是对无标签数据进行的一种分析,可以使用 sklearn.cluster模块来进行。

每个聚类算法都有两种变体:一种是类,它实现了fit方法来学习训练数据上的聚类,另一种是函数,它在给定训练数据的情况下返回一个整数标签数组,对应于不同的聚类。对于类,训练数据上的标签可以在labels_属性中找到。

sklearn.cluster

输入数据

需要注意的一点是,该模块实现的算法可以接受不同类型的矩阵作为输入。所有方法都接受形状为(n_samples, n_features)的标准数据矩阵。这些可以从sklearn.feature_extraction模块中的类中获得。对于AffinityPropagationSpectralClusteringDBSCAN,还可以输入形状为(n_samples, n_samples)的相似性矩阵。这些可以从sklearn.metrics.pairwise模块的函数中获得。

聚类方法概述

scikit-learn中聚类算法的比较

方法名称参数可扩展性用例几何形状(使用的度量)
K-Means <k_means>聚类数非常大的n_samples,中等的n_clustersMiniBatch code <mini_batch_kmeans>一起使用通用,即使聚类大小,平坦几何,不太多的聚类,归纳点之间的距离
Affinity propagation <affinity_propagation>阻尼,样本偏好随着n_samples的增加,不可扩展许多聚类,不均匀的聚类大小,非平坦几何,归纳图距离(例如最近邻图)
Mean-shift <mean_shift>带宽随着n_samples的增加,不可扩展许多聚类,不均匀的聚类大小,非平坦几何,归纳点之间的距离
Spectral clustering <spectral_clustering>聚类数中等的n_samples,小的n_clusters少量聚类,均匀的聚类大小,非平坦几何,转导图距离(例如最近邻图)
Ward hierarchical clustering <hierarchical_clustering>聚类数或距离阈值大的n_samplesn_clusters许多聚类,可能的连接约束,转导点之间的距离
Agglomerative clustering <hierarchical_clustering>聚类数或距离阈值,链接类型,距离大的n_samplesn_clusters许多聚类,可能的连接约束,非欧几里得距离,转导任意两点之间的距离
DBSCAN <dbscan>邻域大小非常大的n_samples,中等的n_clusters非平坦几何,不均匀的聚类大小,异常值移除,转导最近点之间的距离
HDBSCAN <hdbscan>最小聚类成员数,最小点邻居数大的n_samples,中等的n_clusters非平坦几何,不均匀的聚类大小,异常值移除,转导,分层,可变聚类密度最近点之间的距离
OPTICS <optics>最小聚类成员数非常大的n_samples,大的n_clusters非平坦几何,不均匀的聚类大小,可变聚类密度,异常值移除,转导点之间的距离
Gaussian mixtures <mixture>很多不可扩展平坦几何,适用于密度估计,归纳到中心的马氏距离
BIRCH <birch>分支因子,阈值,可选的全局聚类器大的n_clustersn_samples大数据集,异常值移除,数据降维,归纳点之间的欧几里得距离
Bisecting K-Means <bisect_k_means>聚类数非常大的n_samples,中等的n_clusters通用,即使聚类大小,平坦几何,没有空聚类,归纳,分层点之间的距离

非平坦几何聚类在聚类具有特定形状(即非平坦流形)且标准欧几里得距离不是正确度量时非常有用。这种情况出现在上图的前两行中。

高斯混合模型是一种用于聚类的方法,在文档中的另一章节 <mixture>中有详细描述。KMeans可以看作是具有相等协方差的高斯混合模型的特例。

转导 <transductive> 聚类方法(与归纳聚类方法相对)不适用于新的、未见过的数据。

K-means

KMeans算法通过尝试将样本分为n个具有相等方差的组,最小化一个称为惯性或簇内平方和的准则来对数据进行聚类(见下文)。该算法需要指定聚类数。它适用于大量样本,并已在许多不同领域的许多应用领域中使用。

k-means算法将一组 N N N个样本 X X X划分为 K K K个不相交的簇 C C C,每个簇由簇中样本的均值 μ j \mu_j μj描述。这些均值通常被称为簇的“质心”;请注意,它们通常不是来自 X X X的点,尽管它们存在于相同的空间中。

K-means算法旨在选择最小化惯性簇内平方和准则的质心:

∑ i = 0 n min ⁡ μ j ∈ C ( ∣ ∣ x i − μ j ∣ ∣ 2 ) \sum_{i=0}^{n}\min_{\mu_j \in C}(||x_i - \mu_j||^2) i=0nμjCmin(∣∣xiμj2)

  • 惯性假设聚类是凸的和各向同性的,但并不总是如此。它对于细长的聚类或具有不规则形状的流形响应较差。
  • 惯性不是一种归一化的度量方法:我们只知道较低的值更好,零是最优的。但在高维空间中,欧氏距离往往会膨胀(这是所谓的“维度灾难”的一个例子)。在运行 k-means 聚类之前,运行一个降维算法,如 PCA,可以缓解这个问题并加快计算速度。

image

有关上述问题的更详细描述和如何解决它们的方法,请参考示例 sphx_glr_auto_examples_cluster_plot_kmeans_assumptions.pysphx_glr_auto_examples_cluster_plot_kmeans_silhouette_analysis.py

K-means 经常被称为 Lloyd 算法。简单来说,该算法有三个步骤。第一步选择初始质心,最基本的方法是从数据集 X X X 中选择 k k k 个样本。初始化后,K-means 在两个其他步骤之间循环。第一步将每个样本分配给其最近的质心。第二步通过取分配给每个先前质心的所有样本的平均值来创建新的质心。计算旧质心和新质心之间的差异,并且算法重复这两个步骤,直到该值小于阈值。换句话说,直到质心不再显著移动为止。

image

K-means 等价于具有小的、全等的、对角协方差矩阵的期望最大化算法。

该算法还可以通过 Voronoi 图的概念来理解。首先,使用当前质心计算点的 Voronoi 图。Voronoi 图中的每个线段成为一个单独的聚类。其次,更新质心为每个线段的平均值。然后,算法重复此过程,直到满足停止准则。通常,当迭代之间的目标函数的相对减少小于给定的容差值时,算法停止。在此实现中不是这种情况:当质心移动小于容差时,迭代停止。

给定足够的时间,K-means 总是会收敛,但这可能是到达局部最小值。这在很大程度上取决于质心的初始化。因此,通常会多次进行计算,使用不同的质心初始化。帮助解决这个问题的一种方法是 k-means++ 初始化方案,它已经在 scikit-learn 中实现(使用 init='k-means++' 参数)。这将质心初始化为(通常)彼此相距较远,从而产生比随机初始化更好的结果,如参考文献所示。有关比较不同初始化方案的详细示例,请参考 sphx_glr_auto_examples_cluster_plot_kmeans_digits.py

K-means++ 也可以独立调用,以选择其他聚类算法的种子,请参阅 sklearn.cluster.kmeans_plusplus 以获取详细信息和示例用法。

该算法支持样本权重,可以通过参数 sample_weight 给出。这允许在计算聚类中心和惯性值时为某些样本分配更多的权重。例如,将样本的权重设置为 2 等效于将该样本的副本添加到数据集 X X X 中。

K-means 可以用于向量量化。这可以通过使用训练模型的 KMeanstransform 方法来实现。有关在图像上执行向量量化的示例,请参考 sphx_glr_auto_examples_cluster_plot_color_quantization.py

示例:

  • sphx_glr_auto_examples_cluster_plot_cluster_iris.py:使用 iris 数据集的 KMeans 示例用法
  • sphx_glr_auto_examples_text_plot_document_clustering.py:基于稀疏数据使用 KMeansMiniBatchKMeans 进行文档聚类

低级并行性

通过 Cython,KMeans 利用基于 OpenMP 的并行性。小块数据(256 个样本)在并行处理中,这还可以降低内存占用。有关如何控制线程数的详细信息,请参阅我们的 parallelism 注释。

示例:

  • sphx_glr_auto_examples_cluster_plot_kmeans_assumptions.py:演示 k-means 何时表现符合直觉,何时不符合
  • sphx_glr_auto_examples_cluster_plot_kmeans_digits.py:对手写数字进行聚类

参考文献:

Mini Batch K-Means

MiniBatchKMeansKMeans 算法的一种变体,它使用小批量数据来减少计算时间,同时仍然尝试优化相同的目标函数。小批量是在每次训练迭代中随机从输入数据中抽样的子集。这些小批量大大减少了收敛到局部解所需的计算量。与减少 k-means 收敛时间的其他算法不同,小批量 k-means 产生的结果通常只比标准算法稍差一点。

该算法在两个主要步骤之间迭代,类似于普通的 k-means。在第一步中,从数据集中随机抽取 b b b 个样本,形成一个小批量。然后将它们分配给最近的质心。在第二步中,更新质心。与 k-means 不同,这是基于每个样本进行的。对于小批量中的每个样本,通过对样本和所有先前分配给该质心的样本进行流式平均来更新分配的质心。这会导致质心随时间的变化速率降低。直到收敛或达到预定的迭代次数为止,执行这些步骤。

MiniBatchKMeans 收敛速度比 KMeans 快,但结果的质量降低。实际上,这种质量差异可能非常小,如示例和引用所示。

示例:

  • sphx_glr_auto_examples_cluster_plot_mini_batch_kmeans.pyKMeansMiniBatchKMeans 的比较
  • sphx_glr_auto_examples_text_plot_document_clustering.py:基于稀疏数据使用 KMeansMiniBatchKMeans 进行文档聚类
  • sphx_glr_auto_examples_cluster_plot_dict_face_patches.py

参考文献:

亲和传播

AffinityPropagation 通过在样本对之间发送消息直到收敛来创建聚类。然后使用少量的样本作为代表性样本来描述数据集,这些样本被识别为最能代表其他样本的样本。对于样本对之间的消息表示一个样本成为另一个样本的代表的适合度,该适合度在响应于其他样本对的值时进行更新。这种更新是迭代进行的,直到收敛,此时选择最终的代表样本,从而给出最终的聚类。


Affinity Propagation 的主要缺点是复杂性。该算法的时间复杂度为 O ( N 2 T ) O(N^2 T) O(N2T),其中 N N N 是样本数量, T T T 是迭代次数直到收敛。此外,如果使用密集的相似度矩阵,内存复杂度为 O ( N 2 ) O(N^2) O(N2),但如果使用稀疏的相似度矩阵,则可以降低内存复杂度。这使得 Affinity Propagation 最适合处理中小型数据集。

示例:

  • sphx_glr_auto_examples_cluster_plot_affinity_propagation.py:在具有 3 个类别的合成 2D 数据集上进行 Affinity Propagation。
  • sphx_glr_auto_examples_applications_plot_stock_market.py:在金融时间序列上进行 Affinity Propagation,以找到公司的分组。

算法描述: 点之间发送的消息属于两个类别之一。第一个类别是责任 r ( i , k ) r(i, k) r(i,k),表示样本 k k k 应该是样本 i i i 的代表的累积证据。第二个类别是可用性 a ( i , k ) a(i, k) a(i,k),表示样本 i i i 应该选择样本 k k k 作为其代表的累积证据,并考虑到所有其他样本选择 k k k 作为代表的值。通过这种方式,如果样本足够相似于许多样本,并且被许多样本选择为自己的代表,那么样本就会选择代表。

更正式地说,样本 k k k 作为样本 i i i 的代表的责任由以下公式给出:

r ( i , k ) ← s ( i , k ) − m a x [ a ( i , k ′ ) + s ( i , k ′ ) ∀ k ′ ≠ k ] r(i, k) \leftarrow s(i, k) - max [ a(i, k') + s(i, k') \forall k' \neq k ] r(i,k)s(i,k)max[a(i,k)+s(i,k)k=k]

其中 s ( i , k ) s(i, k) s(i,k) 是样本 i i i k k k 之间的相似度。样本 k k k 作为样本 i i i 的代表的可用性由以下公式给出:

a ( i , k ) ← m i n [ 0 , r ( k , k ) + ∑ i ′   s . t .   i ′ ∉ { i , k } r ( i ′ , k ) ] a(i, k) \leftarrow min [0, r(k, k) + \sum_{i'~s.t.~i' \notin \{i, k\}}{r(i', k)}] a(i,k)min[0,r(k,k)+i s.t. i/{i,k}r(i,k)]

首先,所有 r r r a a a 的值都设置为零,并且每次迭代的计算会持续进行直到收敛。如上所述,为了避免在更新消息时出现数值振荡,引入了阻尼因子 λ \lambda λ 到迭代过程中:

r t + 1 ( i , k ) = λ ⋅ r t ( i , k ) + ( 1 − λ ) ⋅ r t + 1 ( i , k ) r_{t+1}(i, k) = \lambda\cdot r_{t}(i, k) + (1-\lambda)\cdot r_{t+1}(i, k) rt+1(i,k)=λrt(i,k)+(1λ)rt+1(i,k)

a t + 1 ( i , k ) = λ ⋅ a t ( i , k ) + ( 1 − λ ) ⋅ a t + 1 ( i , k ) a_{t+1}(i, k) = \lambda\cdot a_{t}(i, k) + (1-\lambda)\cdot a_{t+1}(i, k) at+1(i,k)=λat(i,k)+(1λ)at+1(i,k)

其中 t t t 表示迭代次数。

均值漂移

MeanShift 聚类旨在发现平滑样本密度中的“斑点”。它是一种基于质心的算法,通过更新质心候选点为给定区域内点的均值来工作。然后,在后处理阶段对这些候选点进行过滤,以消除近似重复的点,形成最终的质心集合。

质心候选点的位置使用一种称为“爬山”的技术进行迭代调整,该技术找到估计概率密度的局部最大值。给定迭代 t t t 的候选质心 x x x,根据以下公式更新候选点:

x t + 1 = x t + m ( x t ) x^{t+1} = x^t + m(x^t) xt+1=xt+m(xt)

其中 m m m 是称为“均值漂移”向量的向量,对于每个指向点密度最大增加区域的质心计算。为了计算 m m m,我们定义 N ( x ) N(x) N(x) 为距离 x x x 一定距离内的样本的邻域。然后,使用以下公式计算 m m m,有效地将质心更新为其邻域内样本的均值:

m ( x ) = 1 ∣ N ( x ) ∣ ∑ x j ∈ N ( x ) x j − x m(x) = \frac{1}{|N(x)|} \sum_{x_j \in N(x)}x_j - x m(x)=N(x)1xjN(x)xjx

一般来说, m m m 的公式取决于用于密度估计的核函数。通用公式为:

m ( x ) = ∑ x j ∈ N ( x ) K ( x j − x ) x j ∑ x j ∈ N ( x ) K ( x j − x ) − x m(x) = \frac{\sum_{x_j \in N(x)}K(x_j - x)x_j}{\sum_{x_j \in N(x)}K(x_j - x)} - x m(x)=xjN(x)K(xjx)xjN(x)K(xjx)xjx

在我们的实现中,如果 x x x 足够小,则 K ( x ) K(x) K(x) 等于 1,否则等于 0。实际上, K ( y − x ) K(y - x) K(yx) 表示 y y y 是否在 x x x 的邻域内。

该算法会自动设置聚类的数量,而不是依赖于参数 bandwidth,该参数决定了要搜索的区域的大小。此参数可以手动设置,但可以使用提供的 estimate_bandwidth 函数进行估计,如果未设置带宽,则会调用该函数。

该算法的可扩展性不高,因为在执行算法期间需要进行多次最近邻搜索。然而,该算法保证收敛,但当质心的变化很小时,算法将停止迭代。

对于给定的样本,通过找到最近的质心来进行标记。

示例:

  • sphx_glr_auto_examples_cluster_plot_mean_shift.py:在具有 3 个类别的合成 2D 数据集上进行均值漂移聚类。

参考文献:

  • [“Mean shift: A robust approach toward feature space analysis”
    <10.1109/34.1000236>]
    4. Comaniciu and P. Meer, IEEE Transactions on Pattern Analysis and Machine Intelligence (2002)

谱聚类

SpectralClustering 在样本之间的相似度矩阵上执行低维嵌入,然后对低维空间中的特征向量进行聚类,例如使用 KMeans。如果相似度矩阵是稀疏的,并且使用 amg 求解器来解决特征值问题,则它的计算效率特别高(注意,amg 求解器需要安装 pyamg 模块)。

目前的 SpectralClustering 版本需要预先指定聚类的数量。它适用于少量聚类,但不建议用于许多聚类。

对于两个聚类,SpectralClustering 在相似性图上解决了“归一化割”问题的凸松弛:将图分割为两个部分,使得被切割的边的权重相对于每个聚类内部的边的权重较小。当处理图像时,这个标准特别有趣,其中图的顶点是像素,相似性图的边的权重使用图像梯度的函数计算。

noisy_img

noisy_img

[!WARNING]
将距离转换为良好的相似度

请注意,如果相似度矩阵的值分布不均匀,例如具有负值或距离矩阵而不是相似度矩阵,则谱问题将是奇异的,问题无法解决。在这种情况下,建议对矩阵的条目应用转换。例如,在有符号距离矩阵的情况下,通常会应用热核:

similarity = np.exp(-beta * distance / distance.std())

请参阅示例以了解此类应用。

示例:

  • sphx_glr_auto_examples_cluster_plot_segmentation_toy.py:使用谱聚类从噪声背景中分割对象。
  • sphx_glr_auto_examples_cluster_plot_coin_segmentation.py:使用谱聚类将硬币图像分割成区域。

不同的标签分配策略

可以使用不同的标签分配策略,对应于 SpectralClusteringassign_labels 参数。"kmeans" 策略可以匹配更细的细节,但可能不稳定。特别是,除非控制 random_state,否则无法从运行到运行重现,因为它取决于随机初始化。另一种选择的 "discretize" 策略是 100% 可重现的,但倾向于创建相当均匀和几何形状的分区。最近添加的 "cluster_qr" 选项是一种确定性的替代方案,倾向于在下面的示例应用中创建视觉上最佳的分区。

assign_labels="kmeans"assign_labels="discretize"assign_labels="cluster_qr"
coin_kmeanscoin_discretizecoin_cluster_qr

参考文献:

谱聚类图

谱聚类也可以通过其谱嵌入来对图进行分区。在这种情况下,亲和矩阵是图的邻接矩阵,而 SpectralClustering 使用 `affinity=‘precomputed’` 进行初始化:

from sklearn.cluster import SpectralClustering
sc = SpectralClustering(3, affinity='precomputed', n_init=100, assign_labels='discretize')
sc.fit_predict(adjacency_matrix)  # doctest: +SKIP

参考文献:

层次聚类

层次聚类是一类通过逐步合并或分割来构建嵌套聚类的聚类算法。这些聚类的层次结构被表示为一棵树(或者树状图)。树的根节点是将所有样本聚集在一起的唯一聚类,叶子节点是只包含一个样本的聚类。更多细节请参考维基百科页面

`AgglomerativeClustering` 对象使用自底向上的方法进行层次聚类:每个观测开始时都在自己的聚类中,然后逐步合并聚类。连接标准决定了用于合并策略的度量:

  • Ward 最小化所有聚类内平方差的和。它是一种最小化方差的方法,在这个意义上类似于 k-means 目标函数,但是采用了自底向上的层次聚类方法。
  • Maximumcomplete linkage 最小化聚类对中观测之间的最大距离。
  • Average linkage 最小化聚类对中所有观测之间的平均距离。
  • Single linkage 最小化聚类对中最近观测之间的距离。

当与连接矩阵一起使用时,`AgglomerativeClustering` 还可以扩展到大量的样本,但是当没有添加连接约束时,计算成本很高:它在每一步都考虑所有可能的合并。

FeatureAgglomeration

`FeatureAgglomeration` 使用凝聚聚类将非常相似的特征分组在一起,从而减少特征的数量。它是一种降维工具,详见 `data_reduction`。

不同的连接类型:Ward、complete、average 和 single linkage

`AgglomerativeClustering` 支持 Ward、single、average 和 complete 连接策略。

image

凝聚聚类具有“富者越富”(rich get richer)的行为,导致聚类大小不均匀。在这方面,single linkage 是最差的策略,而 Ward 给出了最规则的大小。然而,Ward 不能改变亲和度(或者用于聚类的距离),因此对于非欧几里德度量,average linkage 是一个很好的选择。Single linkage 虽然对噪声数据不稳健,但是计算非常高效,因此在提供更大数据集的层次聚类时很有用。Single linkage 在非球形数据上也可以表现良好。

示例:

  • `sphx_glr_auto_examples_cluster_plot_digits_linkage.py`:在真实数据集中探索不同的连接策略。

聚类层次的可视化

可以将表示聚类层次合并的树状图(或者树)可视化为树状图。通过视觉检查,可以更好地理解数据的结构,尤其是在样本数量较少的情况下。

image

添加连接约束

`AgglomerativeClustering` 的一个有趣之处是可以添加连接约束到该算法中(只有相邻的聚类可以合并在一起),通过一个连接矩阵来定义每个样本的邻居样本,遵循给定数据结构的规则。例如,在下面的瑞士卷示例中,连接约束禁止合并不在瑞士卷上相邻的点,从而避免形成跨越瑞士卷重叠部分的聚类。

unstructured

这些约束对于强加一定的局部结构很有用,但是它们也使算法更快,特别是当样本数量很大时。

连接约束是通过连接矩阵来实现的:一个 scipy 稀疏矩阵,只在数据集的行和列的交叉点上有元素,这些行和列的索引指示应该连接的数据集的样本。可以根据先验信息构建这个矩阵:例如,您可能希望通过只合并具有相互链接的页面来对网页进行聚类。它也可以从数据中学习,例如使用 `sklearn.neighbors.kneighbors_graph` 来将合并限制为最近邻,就像 `this example` 中那样,或者使用 `sklearn.feature_extraction.image.grid_to_graph` 来仅允许在图像上相邻的像素进行合并,就像 `coin example` 中那样。

示例:

  • `sphx_glr_auto_examples_cluster_plot_coin_ward_segmentation.py`:使用 Ward 聚类将硬币图像分割成区域。
  • `sphx_glr_auto_examples_cluster_plot_ward_structured_vs_unstructured.py`:在瑞士卷上使用 Ward 算法的示例,比较结构化方法和非结构化方法。
  • `sphx_glr_auto_examples_cluster_plot_feature_agglomeration_vs_univariate_selection.py`:基于 Ward 层次聚类的特征凝聚的降维示例。
  • `sphx_glr_auto_examples_cluster_plot_agglomerative_clustering.py`

[!WARNING]
使用 single、average 和 complete linkage 的连接约束

使用连接约束和 single、complete 或 average linkage 可以增强凝聚聚类的“富者越富”特性,特别是如果使用 `sklearn.neighbors.kneighbors_graph` 构建它们。在小数量的聚类极限情况下,它们倾向于产生一些宏观上占据的聚类和几乎为空的聚类(参见 `sphx_glr_auto_examples_cluster_plot_agglomerative_clustering.py` 中的讨论)。在这个问题上,single linkage 是最脆弱的连接选项。

距离度量的变化

可以使用单一、平均和完全连接方法与各种距离(或相似度)一起使用,特别是欧氏距离(l2)、曼哈顿距离(或城市街区距离,l1)、余弦距离或任何预先计算的相似度矩阵。

  • l1 距离通常适用于稀疏特征或稀疏噪声:即许多特征为零,例如在使用罕见单词的文本挖掘中。
  • 余弦 距离很有意思,因为它对信号的全局缩放是不变的。

选择度量的指导原则是使用最大化不同类别样本之间的距离,并最小化每个类别内的距离。

image

image

image

示例:

  • sphx_glr_auto_examples_cluster_plot_agglomerative_clustering_metrics.py

二分 K-Means

BisectingKMeansKMeans 的迭代变体,使用分层聚类进行分割。它不是一次性创建所有质心,而是根据先前的聚类逐步选择质心:将一个簇反复分割成两个新的簇,直到达到目标簇的数量。

当簇的数量较大时,BisectingKMeansKMeans 更高效,因为它每次分割只处理数据的一个子集,而 KMeans 总是处理整个数据集。

尽管 BisectingKMeans 无法从“k-means++”初始化的优势中受益,但在计算成本更低的情况下,它仍然会以与使用随机初始化的 KMeans(init=“k-means++”) 相当的惯性产生可比较的结果,并且可能会产生比使用随机初始化的 KMeans 更好的结果。

与数据点数量相比,如果簇的数量较小,则此变体对于凝聚聚类更有效。

此变体还不会产生空簇。

选择要分割的簇有两种策略:

  • bisecting_strategy="largest_cluster" 选择具有最多点的簇
  • bisecting_strategy="biggest_inertia" 选择具有最大惯性的簇(具有最大平方误差和的簇)

在大多数情况下,通过选择最多数据点的方法可以产生与通过惯性选择的方法一样准确的结果,并且速度更快(特别是对于数据点较多的情况,计算误差可能很昂贵)。

通过选择最多数据点的方法还可能产生大小相似的簇,而 KMeans 则会产生不同大小的簇。

Bisecting K-Means 和常规 K-Means 之间的差异可以在示例 sphx_glr_auto_examples_cluster_plot_bisect_kmeans.py 中看到。尽管常规 K-Means 算法倾向于创建不相关的簇,但 Bisecting K-Means 的簇是有序的,并且创建了相当明显的层次结构。

参考文献:

DBSCAN

DBSCAN 算法将聚类视为高密度区域与低密度区域之间的分隔区域。由于这种相对通用的观点,DBSCAN 找到的聚类可以是任何形状,而 k-means 则假设聚类是凸形状的。DBSCAN 的核心组件是核心样本的概念,它们是在高密度区域中的样本。因此,一个聚类是一组核心样本,彼此之间接近(通过某种距离度量),以及一组非核心样本,它们接近聚类中的核心样本(但它们本身不是核心样本)。算法有两个参数,min_sampleseps,它们在形式上定义了我们所说的密集。较高的 min_samples 或较低的 eps 表示形成聚类所需的更高密度。

更正式地说,我们将核心样本定义为数据集中的样本,使得在 eps 距离内存在 min_samples 个其他样本,这些样本被定义为核心样本的邻居。这告诉我们核心样本位于向量空间的密集区域中。聚类是通过递归地采取核心样本、找到所有它们的核心样本邻居、找到所有它们的核心样本邻居,依此类推来构建的。聚类还有一组非核心样本,它们是聚类中核心样本的邻居样本,但它们本身不是核心样本。直观地说,这些样本位于聚类的边缘。

任何核心样本都是聚类的一部分,根据定义。任何不是核心样本的样本,且与任何核心样本的距离至少为 eps,被算法视为异常值。

尽管参数 min_samples 主要控制算法对噪声的容忍程度(在嘈杂和大型数据集上,可能希望增加此参数),但参数 eps 对于数据集和距离函数来说是选择适当的关键,通常不能保持默认值。它控制点的局部邻域。当选择得太小时,大多数数据根本不会被聚类(并标记为“噪声”的 -1)。当选择得太大时,它会导致紧密的聚类合并为一个聚类,最终将整个数据集作为一个聚类返回。关于选择此参数的一些启发式方法已在文献中讨论过,例如基于最近邻距离图中的“膝部”(如下面的参考文献中所讨论的)。

在下面的图中,颜色表示聚类成员资格,大圆圈表示算法找到的核心样本。较小的圆圈是仍然属于聚类的非核心样本。此外,异常值由下面的黑点表示。

dbscan_results

示例:

  • sphx_glr_auto_examples_cluster_plot_dbscan.py

实现

DBSCAN 算法是确定性的,当以相同的顺序给定相同的数据时,总是生成相同的聚类。然而,当以不同的顺序提供数据时,结果可能会有所不同。首先,即使核心样本始终被分配到相同的聚类中,但这些聚类的标签将取决于在数据中遇到这些样本的顺序。其次,非核心样本被分配到的聚类可能会因数据顺序而异。当非核心样本与不同聚类中的两个核心样本的距离小于 eps 时,就会发生这种情况。根据三角不等式,这两个核心样本之间的距离必须大于 eps,否则它们将在同一个聚类中。非核心样本被分配到通过数据的一次遍历中首先生成的聚类中,因此结果将取决于数据的顺序。
大样本量的内存消耗

默认情况下,这个实现不是内存高效的,因为在无法使用kd树或球树(例如,对于稀疏矩阵)的情况下,它会构建一个完整的成对相似度矩阵。这个矩阵将占用 n 2 n^2 n2个浮点数的内存。有几种方法可以解决这个问题:

  • 在使用OPTICS <optics>聚类时,结合使用extract_dbscan方法。OPTICS聚类也会计算完整的成对矩阵,但每次只在内存中保留一行(内存复杂度为n)。
  • 可以以内存高效的方式预先计算稀疏半径邻域图(其中假定缺失的条目超出了eps的范围),然后使用metric='precomputed'在此图上运行dbscan。参见sklearn.neighbors.NearestNeighbors.radius_neighbors_graph
  • 可以对数据集进行压缩,例如通过删除数据中的完全重复项,或者使用BIRCH。然后,对于大量的点,只需保留相对较小数量的代表点。在拟合DBSCAN时,可以提供sample_weight参数。

参考文献:

  • “A Density-Based Algorithm for Discovering Clusters in Large Spatial Databases with Noise” Ester, M., H. P. Kriegel, J. Sander, and X. Xu, In Proceedings of the 2nd International Conference on Knowledge Discovery and Data Mining, Portland, OR, AAAI Press, pp. 226–231. 1996
  • "DBSCAN revisited, revisited: why and how you should (still) use DBSCAN." <10.1145/3068335> Schubert, E., Sander, J., Ester, M., Kriegel, H. P., & Xu, X. (2017). In ACM Transactions on Database Systems (TODS), 42(3), 19.

HDBSCAN

HDBSCAN算法可以看作是DBSCANOPTICS的扩展。具体而言,DBSCAN假设聚类准则(即密度要求)是全局均匀的。换句话说,DBSCAN可能难以成功捕捉具有不同密度的聚类。HDBSCAN通过构建聚类问题的另一种表示来缓解这个假设。

[!NOTE]
这个实现是基于原始的HDBSCAN实现scikit-learn-contrib/hdbscan进行改编的,基于[LJ2017]

互相可达图

HDBSCAN首先定义了样本 x p x_p xp核心距离 d c ( x p ) d_c(x_p) dc(xp),即到其第min_samples个最近邻的距离(包括自身)。例如,如果min_samples=5 x ∗ x_* x x p x_p xp的第5个最近邻,则核心距离为:

d c ( x p ) = d ( x p , x ∗ ) . d_c(x_p)=d(x_p, x_*). dc(xp)=d(xp,x).

接下来,它定义了两个点 x p , x q x_p, x_q xp,xq之间的互相可达距离 d m ( x p , x q ) d_m(x_p, x_q) dm(xp,xq)

d m ( x p , x q ) = max ⁡ { d c ( x p ) , d c ( x q ) , d ( x p , x q ) } d_m(x_p, x_q) = \max\{d_c(x_p), d_c(x_q), d(x_p, x_q)\} dm(xp,xq)=max{dc(xp),dc(xq),d(xp,xq)}

这两个概念使我们能够构建互相可达图 G m s G_{ms} Gms,对于固定的min_samples选择,将每个样本 x p x_p xp与图的一个顶点相关联,因此点 x p , x q x_p, x_q xp,xq之间的边是它们之间的互相可达距离 d m ( x p , x q ) d_m(x_p, x_q) dm(xp,xq)。我们可以构建这个图的子集,记为 G m s , ε G_{ms,\varepsilon} Gms,ε,通过删除原始图中值大于 ε \varepsilon ε的任何边来实现。核心距离小于 ε \varepsilon ε的点在这个阶段被标记为噪声。然后,剩下的点通过找到修剪后的图的连通分量进行聚类。

[!NOTE]
对修剪后的图 G m s , ε G_{ms,\varepsilon} Gms,ε进行连通分量的提取等价于使用min_samples ε \varepsilon ε运行DBSCAN*。DBSCAN*是DBSCAN的一个稍微修改的版本,详见[CM2013]

分层聚类

HDBSCAN可以看作是在所有 ε \varepsilon ε值上执行DBSCAN*聚类的算法。如前所述,这等价于找到所有 ε \varepsilon ε值的互相可达图的连通分量。为了高效地实现这一点,HDBSCAN首先从完全连接的互相可达图中提取最小生成树(MST),然后贪心地剪掉权重最高的边。HDBSCAN算法的大致步骤如下:

  1. 提取 G m s G_{ms} Gms的MST。
  2. 通过为每个顶点添加一个“自身边”,权重等于底层样本的核心距离,来扩展MST。
  3. 初始化一个单独的聚类和标签用于MST。
  4. 从MST中删除权重最大的边(同时删除并列的边)。
  5. 为包含现在被移除的边的端点的连通分量分配聚类标签。如果该组件没有至少一条边,则将其标记为“null”噪声标签。
  6. 重复步骤4-5,直到没有更多的连通分量。

因此,HDBSCAN能够以分层方式获得DBSCAN*在固定的min_samples选择下的所有可能的划分。这使得HDBSCAN能够在多个密度上进行聚类,因此不再需要将 ε \varepsilon ε作为超参数给定。它仅依赖于min_samples的选择,这往往是一个更稳健的超参数。

hdbscan_ground_truth

hdbscan_results

HDBSCAN可以使用额外的超参数min_cluster_size进行平滑,该参数指定在分层聚类过程中,具有少于min_cluster_size个样本的组件被视为噪声。实际上,可以将minimum_cluster_size设置为min_samples以将这两个参数关联起来并简化超参数空间。

参考文献:

OPTICS

OPTICS算法与DBSCAN算法有许多相似之处,可以看作是将eps要求从单个值放宽为值范围的DBSCAN的泛化。DBSCAN和OPTICS之间的关键区别在于OPTICS算法构建了一个可达性图,该图为每个样本分配了reachability_距离和聚类中的位置ordering_属性;这两个属性在模型拟合时被赋值,并用于确定聚类成员资格。如果使用默认值inf设置max_eps运行OPTICS,则可以使用cluster_optics_dbscan方法以线性时间重复执行DBSCAN风格的聚类提取,对于任何给定的eps值。将max_eps设置为较低的值将导致更短的运行时间,并且可以被视为从每个点到其他潜在可达点的最大邻域半径。

optics_results
示例:

  • sphx_glr_auto_examples_cluster_plot_optics.py

与 DBSCAN 的比较

OPTICS 的 cluster_optics_dbscan 方法和 DBSCAN 的结果非常相似,但并不总是完全相同,尤其是对于边缘点和噪声点的标记。这部分是因为 OPTICS 处理的每个密集区域的第一个样本在其区域内与其他点非常接近,因此有时会被标记为噪声而不是边缘。这会影响到被考虑为边缘或噪声的相邻点。

需要注意的是,对于任何单个的 eps 值,DBSCAN 的运行时间往往比 OPTICS 短;然而,对于不同的 eps 值进行重复运行时,OPTICS 的单次运行可能需要比 DBSCAN 更少的累计运行时间。还需要注意的是,只有当 epsmax_eps 接近时,OPTICS 的输出才接近于 DBSCAN。

计算复杂度

空间索引树用于避免计算完整的距离矩阵,并且可以在大型样本集上实现高效的内存使用。可以通过 metric 关键字提供不同的距离度量。

对于大型数据集,可以通过 HDBSCAN 获得类似(但不完全相同)的结果。HDBSCAN 的实现是多线程的,并且具有比 OPTICS 更好的算法运行时间复杂度,但内存扩展性较差。对于使用 HDBSCAN 耗尽系统内存的极大数据集,OPTICS 将保持 n n n(而不是 n 2 n^2 n2)的内存扩展性;然而,可能需要调整 max_eps 参数以在合理的时间内得到解决方案。

参考文献:

  • “OPTICS: ordering points to identify the clustering structure.” Ankerst, Mihael, Markus M. Breunig, Hans-Peter Kriegel, and Jörg Sander. In ACM Sigmod Record, vol. 28, no. 2, pp. 49-60. ACM, 1999.

BIRCH

Birch 构建了一个称为聚类特征树(CFT)的树来处理给定的数据。数据实际上被压缩成一组聚类特征节点(CF 节点)。CF 节点有一些子聚类称为聚类特征子聚类(CF 子聚类),这些位于非终端 CF 节点中的 CF 子聚类可以有 CF 节点作为子节点。

CF 子聚类保存了进行聚类所需的信息,这样就不需要将整个输入数据保存在内存中。这些信息包括:

  • 子聚类中的样本数。
  • 线性和 - 一个 n 维向量,保存所有样本的和。
  • 平方和 - 所有样本的平方 L2 范数的和。
  • 质心 - 为了避免重新计算线性和 / 样本数。
  • 质心的平方范数。

BIRCH 算法有两个参数,阈值和分支因子。分支因子限制了节点中的子聚类数,而阈值限制了进入样本与现有子聚类之间的距离。

这个算法可以看作是一个实例或数据减少方法,因为它将输入数据减少到一组子聚类中,这些子聚类直接从 CFT 的叶子节点获得。这个减少后的数据可以通过将其输入到全局聚类器中进一步处理。可以通过 n_clusters 设置全局聚类器。如果 n_clusters 设置为 None,则直接读取叶子节点的子聚类,否则全局聚类步骤将这些子聚类标记为全局聚类(标签),并将样本映射到最近子聚类的全局标签。

算法描述:

  • 将新样本插入到 CF 树的根节点中,该节点是一个 CF 节点。然后将其与根节点的子聚类合并,合并后具有最小半径的子聚类,受到阈值和分支因子条件的限制。如果子聚类有任何子节点,则重复此过程,直到达到叶子节点。在找到叶子节点中最近的子聚类之后,递归更新该子聚类和父子聚类的属性。
  • 如果通过合并新样本和最近的子聚类获得的子聚类的半径大于阈值的平方,并且子聚类的数量大于分支因子,则为该新样本临时分配空间。取最远的两个子聚类,并根据这些子聚类之间的距离将子聚类分为两组。
  • 如果此分割节点具有父子聚类并且有空间容纳新的子聚类,则将父子聚类分为两个。如果没有空间,则再次将此节点分为两个,并继续递归进行该过程,直到达到根节点。

BIRCH 还是 MiniBatchKMeans?

  • BIRCH 在高维数据上的扩展性不是很好。作为经验法则,如果 n_features 大于二十,通常最好使用 MiniBatchKMeans。
  • 如果需要减少数据实例的数量,或者需要大量的子聚类作为预处理步骤或其他用途,BIRCH 比 MiniBatchKMeans 更有用。

如何使用 partial_fit?

为了避免进行全局聚类的计算,建议用户在每次调用 partial_fit 时:

  1. 最初将 n_clusters 设置为 None。
  2. 通过多次调用 partial_fit 训练所有数据。
  3. 使用 brc.set_params(n_clusters=n_clusters)n_clusters 设置为所需的值。
  4. 最后调用 partial_fit,即 brc.partial_fit(),执行全局聚类。

image

参考文献:

聚类性能评估

评估聚类算法的性能并不像计算错误数量或监督分类算法的精确度和召回率那样简单。特别是,任何评估指标都不应考虑聚类标签的绝对值,而是应该考虑该聚类是否定义了与某个基准真实类别集合相似的数据分离,或者是否满足某些假设,即属于同一类的成员比不同类的成员更相似,根据某种相似度度量。

sklearn.metrics

Rand 指数

给定地面真实类别分配 labels_true 和我们的聚类算法对相同样本的分配 labels_pred 的知识,(调整或未调整的)Rand 指数 是一个衡量两个分配相似性的函数,忽略排列:

>>> from sklearn import metrics
>>> labels_true = [0, 0, 0, 1, 1, 1]
>>> labels_pred = [0, 0, 1, 1, 2, 2]
>>> metrics.rand_score(labels_true, labels_pred)
0.66...

Rand 指数不能保证获得接近 0.0 的值来表示随机标记。调整后的 Rand 指数校正了偶然性,并给出了这样的基线。

>>> metrics.adjusted_rand_score(labels_true, labels_pred)
0.24...

与所有聚类度量一样,可以在预测的标签中对 0 和 1 进行排列,将 2 重命名为 3,并获得相同的分数:

>>> labels_pred = [1, 1, 0, 0, 3, 3]
>>> metrics.rand_score(labels_true, labels_pred)
0.66...
>>> metrics.adjusted_rand_score(labels_true, labels_pred)
0.24...

此外,rand_scoreadjusted_rand_score 都是对称的:交换参数不会改变分数。因此,它们可以用作一致性度量

>>> metrics.rand_score(labels_pred, labels_true)
0.66...
>>> metrics.adjusted_rand_score(labels_pred, labels_true)
0.24...

完美的标记得分为 1.0:

>>> labels_pred = labels_true[:]
>>> metrics.rand_score(labels_true, labels_pred)
1.0

1.0

标签不一致(例如独立的标签)得分较低,对于调整后的兰德指数(adjusted Rand index),得分将为负数或接近零。然而,对于未调整的兰德指数(unadjusted Rand index),得分虽然较低,但不一定接近零。

labels_true = [0, 0, 0, 0, 0, 0, 1, 1]
labels_pred = [0, 1, 2, 3, 4, 5, 5, 6]
metrics.rand_score(labels_true, labels_pred)
0.39...
metrics.adjusted_rand_score(labels_true, labels_pred)
-0.07...
优点
  • 可解释性:未调整的兰德指数与在labels_pred和labels_true中标签相同或不同的样本对的数量成正比。
  • 随机(均匀)标签分配的调整后兰德指数接近于0.0,对于任何n_clustersn_samples的值都成立(这对于未调整的兰德指数或V-measure来说并非如此)。
  • 有界范围:较低的值表示不同的标签分配,相似的聚类具有较高的(调整或未调整的)兰德指数,1.0是完美匹配的得分。未调整的兰德指数的得分范围为[0, 1],调整后的兰德指数的得分范围为[-1, 1]。
  • 不对聚类结构做任何假设:(调整或未调整的)兰德指数可用于比较各种聚类算法,并可用于将假设具有各向同性斑点形状的k-means聚类算法的结果与可以找到具有“折叠”形状的聚类的谱聚类算法的结果进行比较。
缺点
  • 与惯性不同,(调整或未调整的)兰德指数需要了解地面真实类别,这在实践中几乎不可用,或者需要人工标注员手动分配(如在监督学习设置中)。

    然而,(调整或未调整的)兰德指数在纯无监督设置中也可以作为用于聚类模型选择的一种基础。

  • 未调整的兰德指数通常接近于1.0,即使聚类本身差异很大。当将兰德指数解释为聚类结果的元素对标签的准确性时,这一点可以理解:实际上,通常有大多数元素对在预测和地面真实聚类下都被分配为“不同”对标签,从而导致高比例的一致对标签,进而导致高得分。

示例:

  • sphx_glr_auto_examples_cluster_plot_adjusted_for_chance_measures.py:分析数据集大小对随机分配的聚类度量值的影响。
数学公式

如果C是地面真实类别分配,K是聚类分配,则定义 a a a b b b为:

  • a a a,在C和K中都属于同一集合的元素对的数量
  • b b b,在C和K中都属于不同集合的元素对的数量

则未调整的兰德指数为:

RI = a + b C 2 n s a m p l e s \text{RI} = \frac{a + b}{C_2^{n_{samples}}} RI=C2nsamplesa+b

其中 C 2 n s a m p l e s C_2^{n_{samples}} C2nsamples是数据集中可能的元素对的总数。计算可以在有序对或无序对上进行,只要计算一致即可。

然而,兰德指数不能保证随机标签分配的值接近于零(尤其是如果聚类数与样本数的数量级相同)。

为了抵消这种影响,我们可以通过以下方式对随机标签分配的预期RI E [ RI ] E[\text{RI}] E[RI] 进行折扣,定义调整后的兰德指数如下:

ARI = RI − E [ RI ] max ⁡ ( RI ) − E [ RI ] \text{ARI} = \frac{\text{RI} - E[\text{RI}]}{\max(\text{RI}) - E[\text{RI}]} ARI=max(RI)E[RI]RIE[RI]

参考文献

基于互信息的度量

给定地面真实类别分配labels_true和我们的聚类算法对相同样本的分配labels_pred的知识,互信息(Mutual Information)是一种衡量这两个分配之间一致性的函数,忽略排列。这个度量有两个不同的归一化版本可用,归一化互信息(Normalized Mutual Information,NMI)调整后的互信息(Adjusted Mutual Information,AMI)。NMI通常在文献中使用,而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...

可以对预测的标签中的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_scoreadjusted_mutual_info_scorenormalized_mutual_info_score都是对称的:交换参数不会改变得分。因此,它们可以用作共识度量

metrics.adjusted_mutual_info_score(labels_pred, labels_true)
0.22504...

完美的标签得分为1.0:

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

对于mutual_info_score来说,情况并非如此,这使得它更难判断:

metrics.mutual_info_score(labels_true, labels_pred)
0.69...

差(例如独立的标签分配)得分为非正数:

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...
优点
  • 随机(均匀)标签分配的AMI得分接近于0.0,对于任何n_clustersn_samples的值都成立(这对于原始互信息或V-measure来说并非如此)。
  • 上界为1:接近零的值表示两个标签分配在很大程度上是独立的,而接近于1的值表示显著一致。此外,AMI为1表示两个标签分配相等(无论是否有排列)。
缺点
  • 与惯性不同,基于互信息的度量需要了解地面真实类别,这在实践中几乎不可用,或者需要人工标注员手动分配(如在监督学习设置中)。

    然而,基于互信息的度量在纯无监督设置中也可以作为用于聚类模型选择的一种基础。

  • NMI和MI没有针对随机情况进行调整。

示例:

  • sphx_glr_auto_examples_cluster_plot_adjusted_for_chance_measures.py:分析数据集大小对随机分配的聚类度量值的影响。该示例还包括调整后的兰德指数。
数学公式

假设有两个标签分配(相同N个对象), U U U V V V。它们的熵是分区集的不确定性量,定义为:

H ( U ) = − ∑ i = 1 ∣ U ∣ P ( i ) log ⁡ ( P ( i ) ) H(U) = - \sum_{i=1}^{|U|}P(i)\log(P(i)) H(U)=i=1UP(i)log(P(i))

其中 P ( i ) = ∣ U i ∣ / N P(i) = |U_i| / N P(i)=Ui∣/N是从 U U U中随机选择的对象落入类别 U i U_i Ui的概率。对于 V V V也是如此:

H ( V ) = − ∑ j = 1 ∣ V ∣ P ′ ( j ) log ⁡ ( P ′ ( j ) ) H(V) = - \sum_{j=1}^{|V|}P'(j)\log(P'(j)) H(V)=j=1VP(j)log(P(j))

其中 P ′ ( j ) = ∣ V j ∣ / N P'(j) = |V_j| / N P(j)=Vj∣/N U U U V V V之间的互信息(MI)通过以下方式计算:
其中, P ( i , j ) = ∣ U i ∩ V j ∣ / N P(i, j) = |U_i \cap V_j| / N P(i,j)=UiVj∣/N 是一个随机选择的对象同时属于类别 U i U_i Ui V j V_j Vj 的概率。

它也可以用集合基数的形式表示:

MI ( U , V ) = ∑ i = 1 ∣ U ∣ ∑ j = 1 ∣ V ∣ ∣ U i ∩ V j ∣ N log ⁡ ( N ∣ U i ∩ V j ∣ ∣ U i ∣ ∣ V j ∣ ) \text{MI}(U, V) = \sum_{i=1}^{|U|} \sum_{j=1}^{|V|} \frac{|U_i \cap V_j|}{N}\log\left(\frac{N|U_i \cap V_j|}{|U_i||V_j|}\right) MI(U,V)=i=1Uj=1VNUiVjlog(Ui∣∣VjNUiVj)

归一化互信息定义为:

NMI ( U , V ) = MI ( U , V ) mean ( H ( U ) , H ( V ) ) \text{NMI}(U, V) = \frac{\text{MI}(U, V)}{\text{mean}(H(U), H(V))} NMI(U,V)=mean(H(U),H(V))MI(U,V)

互信息和归一化互信息的值都没有进行随机调整,随着不同标签(聚类)的数量增加,它们的值会增加,而不考虑标签分配之间的实际“互信息”量。

可以使用以下方程式计算互信息的期望值 [VEB2009]。在该方程式中, a i = ∣ U i ∣ a_i = |U_i| ai=Ui U i U_i Ui 中的元素数量)和 b j = ∣ V j ∣ b_j = |V_j| bj=Vj V j V_j Vj 中的元素数量)。

E [ MI ( U , V ) ] = ∑ i = 1 ∣ U ∣ ∑ j = 1 ∣ V ∣ ∑ n i j = ( a i + b j − N ) + min ⁡ ( a i , b j ) n i j N log ⁡ ( N . n i j a i b j ) a i ! b j ! ( N − a i ) ! ( N − b j ) ! N ! n i j ! ( a i − n i j ) ! ( b j − n i j ) ! ( N − a i − b j + n i j ) ! E[\text{MI}(U,V)]=\sum_{i=1}^{|U|} \sum_{j=1}^{|V|} \sum_{n_{ij}=(a_i+b_j-N)^+ }^{\min(a_i, b_j)} \frac{n_{ij}}{N}\log \left( \frac{ N.n_{ij}}{a_i b_j}\right) \frac{a_i!b_j!(N-a_i)!(N-b_j)!}{N!n_{ij}!(a_i-n_{ij})!(b_j-n_{ij})! (N-a_i-b_j+n_{ij})!} E[MI(U,V)]=i=1Uj=1Vnij=(ai+bjN)+min(ai,bj)Nnijlog(aibjN.nij)N!nij!(ainij)!(bjnij)!(Naibj+nij)!ai!bj!(Nai)!(Nbj)!

使用期望值,可以使用与调整后的兰德指数类似的形式计算调整后的互信息:

AMI = MI − E [ MI ] mean ( H ( U ) , H ( V ) ) − E [ MI ] \text{AMI} = \frac{\text{MI} - E[\text{MI}]}{\text{mean}(H(U), H(V)) - E[\text{MI}]} AMI=mean(H(U),H(V))E[MI]MIE[MI]

对于归一化互信息和调整后的互信息,归一化值通常是每个聚类熵的某种“广义”均值。存在各种广义均值,并没有确定的规则来优先选择其中之一。决策主要是基于领域的基础;例如,在社区检测中,最常见的是算术平均值。每种归一化方法都提供了“定性相似的行为” [YAT2016]。在我们的实现中,这由 average_method 参数控制。

Vinh 等人(2010)根据其平均方法为 NMI 和 AMI 命名了变体 [VEB2010]。他们的“sqrt”和“sum”平均值分别是几何平均值和算术平均值;我们使用这些更常见的名称。

参考文献

同质性、完整性和 V-measure

根据样本的真实类别分配,可以使用条件熵分析定义一些直观的度量指标。

特别是,Rosenberg 和 Hirschberg(2007)为任何聚类分配定义了以下两个理想目标:

  • 同质性:每个聚类只包含一个类的成员。
  • 完整性:给定类的所有成员都被分配到同一个聚类中。

我们可以将这些概念转化为得分 homogeneity_scorecompleteness_score。两者的取值范围在 0.0 到 1.0 之间(越高越好):

>>> 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,可以通过 v_measure_score 计算:

>>> metrics.v_measure_score(labels_true, labels_pred)
0.51...

该函数的公式如下:

v = ( 1 + β ) × homogeneity × completeness ( β × homogeneity + completeness ) v = \frac{(1 + \beta) \times \text{homogeneity} \times \text{completeness}}{(\beta \times \text{homogeneity} + \text{completeness})} v=(β×homogeneity+completeness)(1+β)×homogeneity×completeness

beta 默认值为 1.0,但对于使用小于 1 的 beta 值:

>>> metrics.v_measure_score(labels_true, labels_pred, beta=0.6)
0.54...

将更多的权重归属于同质性,而对于使用大于 1 的值:

>>> metrics.v_measure_score(labels_true, labels_pred, beta=1.8)
0.48...

将更多的权重归属于完整性。

实际上,V-measure 等同于上面讨论的互信息(NMI),聚合函数为算术平均值 [B2011]

可以使用 homogeneity_completeness_v_measure 一次计算同质性、完整性和 V-measure,如下所示:

>>> metrics.homogeneity_completeness_v_measure(labels_true, labels_pred)
(0.66..., 0.42..., 0.51...)

以下聚类分配稍微更好,因为它是同质的但不完整:

>>> labels_pred = [0, 0, 0, 1, 2, 2]
>>> metrics.homogeneity_completeness_v_measure(labels_true, labels_pred)
(1.0, 0.68..., 0.81...)

[!NOTE]
v_measure_score对称的:它可以用于评估在同一数据集上的两个独立分配的一致性

对于 completeness_scorehomogeneity_score,情况并非如此:两者之间存在以下关系:

homogeneity_score(a, b) == completeness_score(b, a)

优点
  • 有界得分:0.0 是最差的得分,1.0 是完美的得分。
  • 直观解释:可以通过同质性和完整性对具有较差 V-measure 的聚类进行定性分析,以更好地了解分配所犯的“错误类型”。
  • 不对聚类结构做任何假设:可以用于比较聚类算法,例如假设各向同性斑点形状的 k-means 算法与可以找到具有“折叠”形状的聚类的谱聚类算法的结果。
缺点
  • 先前介绍的度量指标没有针对随机标签进行归一化:这意味着根据样本数、聚类数和真实类别数的不同,完全随机的标签分配不会始终产生相同的同质性、完整性和因此 V-measure 值。特别是当聚类数较大时,随机标签不会产生零分

    当样本数超过一千且聚类数小于 10 时,可以安全地忽略此问题。对于较小的样本大小或较大的聚类数,最好使用调整后的指数,如调整后的兰德指数(ARI)

  • 这些度量指标需要知道真实类别,而在实践中几乎不可用,或者需要人工标注员手动分配(如在监督学习设置中)。

示例:

  • sphx_glr_auto_examples_cluster_plot_adjusted_for_chance_measures.py:分析数据集大小对随机分配的聚类度量值的影响。
数学公式

同质性和完整性得分的形式化定义如下:

h = 1 − H ( C ∣ K ) H ( C ) h = 1 - \frac{H(C|K)}{H(C)} h=1H(C)H(CK)

c = 1 − H ( K ∣ C ) H ( K ) c = 1 - \frac{H(K|C)}{H(K)} c=1H(K)H(KC)

其中 H ( C ∣ K ) H(C|K) H(CK)给定聚类分配的类别的条件熵,定义如下:

H ( C ∣ K ) = − ∑ c = 1 ∣ C ∣ ∑ k = 1 ∣ K ∣ n c , k n ⋅ log ⁡ ( n c , k n k ) H(C|K) = - \sum_{c=1}^{|C|} \sum_{k=1}^{|K|} \frac{n_{c,k}}{n} \cdot \log\left(\frac{n_{c,k}}{n_k}\right) H(CK)=c=1Ck=1Knnc,klog(nknc,k)

H ( C ) H(C) H(C)类别的熵,定义如下:

H ( C ) = − ∑ c = 1 ∣ C ∣ n c n ⋅ log ⁡ ( n c n ) H(C) = - \sum_{c=1}^{|C|} \frac{n_c}{n} \cdot \log\left(\frac{n_c}{n}\right) H(C)=c=1Cnnclog(nnc)

其中 n n n 是样本总数, n c n_c nc n k n_k nk 分别是属于类别 c c c 和聚类 k k k 的样本数,最后 n c , k n_{c,k} nc,k 是从类别 c c c 分配到聚类 k k k 的样本数。

给定类别的聚类的条件熵 H ( K ∣ C ) H(K|C) H(KC)聚类的熵 H ( K ) H(K) H(K) 以对称的方式定义。

Rosenberg 和 Hirschberg 进一步将V-measure定义为同质性和完整性的调和平均值

v = 2 ⋅ h ⋅ c h + c v = 2 \cdot \frac{h \cdot c}{h + c} v=2h+chc

参考文献

Fowlkes-Mallows 分数

Fowlkes-Mallows 指数 (sklearn.metrics.fowlkes_mallows_score) 可以在已知样本的真实类别分配时使用。Fowlkes-Mallows 分数 FMI 定义为成对精确度和召回率的几何平均值:

FMI = TP ( TP + FP ) ( TP + FN ) \text{FMI} = \frac{\text{TP}}{\sqrt{(\text{TP} + \text{FP}) (\text{TP} + \text{FN})}} FMI=(TP+FP)(TP+FN) TP

其中 TP真正例的数量(即在真实标签和预测标签中都属于相同聚类的点对的数量),FP假正例的数量(即在真实标签中属于相同聚类但在预测标签中不属于相同聚类的点对的数量),FN假反例的数量(即在预测标签中属于相同聚类但在真实标签中不属于相同聚类的点对的数量)。

该分数的取值范围为 0 到 1。较高的值表示两个聚类之间的相似性较好。

>>> 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...

可以通过对预测标签中的 0 和 1 进行置换,将 2 重命名为 3,得到相同的分数:

>>> labels_pred = [1, 1, 0, 0, 3, 3]

>>> metrics.fowlkes_mallows_score(labels_true, labels_pred)
0.47140...

完美的标签得分为 1.0:

>>> labels_pred = labels_true[:]
>>> metrics.fowlkes_mallows_score(labels_true, labels_pred)
1.0

不好的(例如独立的标签分配)得分为零:

>>> labels_true = [0, 1, 2, 0, 3, 4, 5, 1]
>>> labels_pred = [1, 1, 0, 0, 2, 2, 2, 2]
>>> metrics.fowlkes_mallows_score(labels_true, labels_pred)
0.0
优点
  • 随机(均匀)的标签分配对于任何 n_clustersn_samples 的 FMI 分数接近于 0.0(这在原始互信息或 V-measure 中并不是这样)。
  • 上界为 1:接近于零的值表示两个标签分配之间几乎独立,接近于一的值表示显著一致。此外,值为 0 表示纯粹独立的标签分配,值为 1 表示两个标签分配相等(无论是否有置换)。
  • 不对聚类结构做任何假设:可以用于比较聚类算法,如假设具有各向同性斑点形状的 k-means 算法和可以找到具有“折叠”形状的聚类的谱聚类算法的结果。
缺点
  • 与惯性不同,基于 FMI 的度量需要知道真实类别,而在实践中几乎不可用,或者需要人工标注员手动分配(如在监督学习设置中)。

参考文献

Silhouette 系数

如果不知道真实标签,则必须使用模型本身进行评估。Silhouette 系数 (sklearn.metrics.silhouette_score) 就是这样一种评估方法,其中较高的 Silhouette 系数得分与具有更好定义的聚类模型相关。Silhouette 系数针对每个样本定义了两个分数:

  • a:样本与同一类中所有其他点的平均距离。
  • b:样本与次最近聚类中所有其他点的平均距离。

然后,单个样本的 Silhouette 系数 s 定义为:

s = b − a max ⁡ ( a , b ) s = \frac{b - a}{\max(a, b)} s=max(a,b)ba

一组样本的 Silhouette 系数是每个样本的 Silhouette 系数的均值。

>>> from sklearn import metrics
>>> from sklearn.metrics import pairwise_distances
>>> from sklearn import datasets
>>> X, y = datasets.load_iris(return_X_y=True)

在正常使用中,Silhouette 系数应用于聚类分析的结果。

>>> 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...

参考文献

  • Peter J. Rousseeuw (1987). “Silhouettes: a Graphical Aid to the
    Interpretation and Validation of Cluster Analysis”. Computational and Applied Mathematics 20: 53–65. <10.1016/0377-0427(87)90125-7>
优点
  • 该分数的取值范围在 -1(表示错误的聚类)到 +1(表示高度密集的聚类)之间。接近零的分数表示重叠的聚类。
  • 当聚类密集且分离良好时,该分数较高,这与聚类的标准概念相关。
缺点
  • Silhouette 系数对于凸聚类比其他聚类概念(例如通过 DBSCAN 获得的基于密度的聚类)更高。

示例:

  • sphx_glr_auto_examples_cluster_plot_kmeans_silhouette_analysis.py:在此示例中,使用 Silhouette 分析选择最佳的 n_clusters 值。

Calinski-Harabasz 指数

如果不知道真实标签,则可以使用 Calinski-Harabasz 指数 (sklearn.metrics.calinski_harabasz_score) - 也称为方差比准则 - 来评估模型,其中较高的 Calinski-Harabasz 分数与具有更好定义的聚类模型相关。

该指数是所有聚类的类间离散度之和与类内离散度之和的比值(其中离散度定义为距离平方和):

>>> from sklearn import metrics
>>> from sklearn.metrics import pairwise_distances
>>> from sklearn import datasets
>>> X, y = datasets.load_iris(return_X_y=True)

在正常使用中,Calinski-Harabasz 指数应用于聚类分析的结果:

>>> 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.calinski_harabasz_score(X, labels)
561.59...
优点
  • 该分数在聚类密集且分离良好时较高,这与聚类的标准概念相关。
  • 该分数计算速度快。
缺点
  • Calinski-Harabasz 指数对于凸聚类比其他聚类概念(例如通过 DBSCAN 获得的基于密度的聚类)更高。
数学公式

对于大小为 n E n_E nE 的数据集 E E E,已将其聚类为 k k k 个聚类,Calinski-Harabasz 分数 s s s 定义为类间离散度之和与类内离散度之和的比值:

s = t r ( B k ) t r ( W k ) × n E − k k − 1 s = \frac{\mathrm{tr}(B_k)}{\mathrm{tr}(W_k)} \times \frac{n_E - k}{k - 1} s=tr(Wk)tr(Bk)×k1nEk

其中 t r ( B k ) \mathrm{tr}(B_k) tr(Bk) 是类间组离散度矩阵的迹, t r ( W k ) \mathrm{tr}(W_k) tr(Wk) 是由以下定义的类内离散度矩阵的迹:

W k = ∑ q = 1 k ∑ x ∈ C q ( x − c q ) ( x − c q ) T W_k = \sum_{q=1}^k \sum_{x \in C_q} (x - c_q) (x - c_q)^T Wk=q=1kxCq(xcq)(xcq)T

B k = ∑ q = 1 k n q ( c q − c E ) ( c q − c E ) T B_k = \sum_{q=1}^k n_q (c_q - c_E) (c_q - c_E)^T Bk=q=1knq(cqcE)(cqcE)T

其中 C q C_q Cq 是聚类 q q q 中的点集, c q c_q cq 是聚类 q q q 的中心, c E c_E cE E E E 的中心, n q n_q nq 是聚类 q q q 中的点数。

参考文献

  • Caliński, T., & Harabasz, J. (1974). “A Dendrite Method for Cluster Analysis”. Communications in Statistics-theory and Methods 3: 1-27. <10.1080/03610927408827101>

Davies-Bouldin 指数

如果不知道真实标签,则可以使用 Davies-Bouldin 指数 (sklearn.metrics.davies_bouldin_score) 来评估模型,其中较低的 Davies-Bouldin 指数与具有更好分离的聚类模型相关。
零是最低可能的分数。离零越近的值表示更好的分区。

在正常使用中,Davies-Bouldin指数应用于聚类分析的结果,如下所示:

from sklearn import datasets
iris = datasets.load_iris()
X = iris.data
from sklearn.cluster import KMeans
from sklearn.metrics import davies_bouldin_score
kmeans = KMeans(n_clusters=3, random_state=1).fit(X)
labels = kmeans.labels_()
davies_bouldin_score(X, labels)
优点
  • Davies-Bouldin的计算比Silhouette分数简单。
  • 该指数仅基于数据集固有的数量和特征,其计算仅使用点间距离。
缺点
  • Davies-Bouldin指数对于凸聚类通常比其他聚类概念(如从DBSCAN获得的基于密度的聚类)更高。
  • 使用质心距离将距离度量限制为欧几里德空间。
数学公式

该指数定义为每个聚类 C i C_i Ci i = 1 , . . . , k i=1, ..., k i=1,...,k)与其最相似聚类 C j C_j Cj之间的平均相似度。在该指数的上下文中,相似度定义为一个度量 R i j R_{ij} Rij,其权衡了以下因素:

  • s i s_i si,聚类 i i i中每个点与该聚类的质心之间的平均距离,也称为聚类直径。
  • d i j d_{ij} dij,聚类质心 i i i j j j之间的距离。

构建非负对称的 R i j R_{ij} Rij的一个简单选择是:

R i j = s i + s j d i j R_{ij} = \frac{s_i + s_j}{d_{ij}} Rij=dijsi+sj

然后,Davies-Bouldin指数定义为:

D B = 1 k ∑ i = 1 k max ⁡ i ≠ j R i j DB = \frac{1}{k} \sum_{i=1}^k \max_{i \neq j} R_{ij} DB=k1i=1ki=jmaxRij

参考文献

  • Davies, David L.; Bouldin, Donald W. (1979). “A Cluster Separation Measure” <10.1109/TPAMI.1979.4766909> IEEE Transactions on Pattern Analysis and Machine Intelligence. PAMI-1 (2): 224-227.
  • Halkidi, Maria; Batistakis, Yannis; Vazirgiannis, Michalis (2001). “On Clustering Validation Techniques” <10.1023/A:1012801612483> Journal of Intelligent Information Systems, 17(2-3), 107-145.
  • Davies-Bouldin指数的维基百科页面

条件矩阵

条件矩阵(sklearn.metrics.cluster.contingency_matrix)报告了每个真实/预测聚类对的交集基数。条件矩阵为所有聚类度量提供了足够的统计信息,其中样本是独立且同分布的,并且不需要考虑一些实例未被聚类。

以下是一个示例:

from sklearn.metrics.cluster import contingency_matrix
x = ["a", "a", "a", "b", "b", "b"]
y = [0, 0, 1, 1, 2, 2]
contingency_matrix(x, y)

输出数组的第一行表示有三个真实聚类为"a"的样本。其中,两个在预测聚类0中,一个在1中,没有在2中。第二行表示有三个真实聚类为"b"的样本。其中,没有在预测聚类0中,一个在1中,两个在2中。

对于分类的混淆矩阵是一个方形的条件矩阵,其中行和列的顺序对应于类别列表。

优点
  • 允许检查每个真实聚类在预测聚类中的分布情况,反之亦然。
  • 计算的条件表通常用于计算两个聚类之间的相似性统计量(如本文档中列出的其他统计量)。
缺点
  • 对于少数聚类而言,条件矩阵易于解释,但对于大量聚类而言,解释起来非常困难。
  • 它不提供单个度量作为聚类优化的目标。

参考文献

对偶混淆矩阵

对偶混淆矩阵(sklearn.metrics.cluster.pair_confusion_matrix)是一个2x2的相似度矩阵,通过考虑所有样本对并计算在真实和预测聚类下分配到相同或不同聚类的样本对的数量来计算。

它具有以下条目:

C 00 C_{00} C00:两个聚类中的样本都没有被聚类在一起的样本对数

C 10 C_{10} C10:真实标签聚类中的样本被聚类在一起,但另一个聚类中的样本没有被聚类在一起的样本对数

C 01 C_{01} C01:真实标签聚类中的样本没有被聚类在一起,但另一个聚类中的样本被聚类在一起的样本对数

C 11 C_{11} C11:两个聚类中的样本都被聚类在一起的样本对数

将被聚类在一起的样本对视为正样本,那么与二元分类相同,真负样本的计数是 C 00 C_{00} C00,假负样本是 C 10 C_{10} C10,真正样本是 C 11 C_{11} C11,假正样本是 C 01 C_{01} C01

完全匹配的标签分配在对角线上具有所有非零条目,而不考虑实际标签值:

from sklearn.metrics.cluster import pair_confusion_matrix
pair_confusion_matrix([0, 0, 1, 1], [0, 0, 1, 1])

输出数组为:

array([[8, 0],
       [0, 4]])
pair_confusion_matrix([0, 0, 1, 1], [1, 1, 0, 0])

输出数组为:

array([[8, 0],
       [0, 4]])

将所有类成员分配到相同聚类中的标签分配是完整的,但可能不总是纯净的,因此会受到惩罚,并且具有一些非对角线的非零条目:

pair_confusion_matrix([0, 0, 1, 2], [0, 0, 1, 1])

输出数组为:

array([[8, 2],
       [0, 2]])

该矩阵不对称:

pair_confusion_matrix([0, 0, 1, 1], [0, 0, 1, 2])

输出数组为:

array([[8, 0],
       [2, 2]])

如果类成员完全分散在不同的聚类中,则分配是完全不完整的,因此矩阵的对角线条目都为零:

pair_confusion_matrix([0, 0, 0, 0], [0, 1, 2, 3])

输出数组为:

array([[ 0,  0],
       [12,  0]])

参考文献

  • “Comparing Partitions” <10.1007/BF01908075>
    12. Hubert and P. Arabie, Journal of Classification 1985
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数智笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值