BIRCH介绍:
BIRCH算法于1996年首次提出,目的是减少大数据聚类问题的计算复杂性,同时不牺牲聚类质量。
BIRCH是一种高效的层次聚类算法,专门用于大规模数据集的分析处理。该算法的核心在于使用一种特殊的数据结构——CF(Clustering Feature)树,来实现数据的压缩和有效聚类。它运行速度很快,只需要单遍扫描数据集就能进行聚类。
什么是CF树?
每个节点包括叶子节点都有若干个CF,而内部节点的CF有指向孩子节点的指针,所有的叶子节点用一个双向链表链接起来。重要参数:
1.每个内部节点的最大CF数B
2.每个叶子节点的最大CF数L
3.叶节点每个CF的最大样本半径阈值T
CF Tree
聚类特征树CF Tree的生成
它是一个三元组用(N,LS,SS)其中N代表该簇中的样本点数量,LS是这些样本点在各个特征维度上的和向量,SS则是每个特征维度上样本点平方和。例如,假设一个CF包含样本点(4,5)(5,6)(2,9)(4,1)(8,3)则其对应的N=5,LS=(23,24),SS=277
CF有一个很好的性质,就是满足线性关系,也就是CF=CF1+CF2=(N1+N2,LS1+LS2,SS1+SS2)这种特性使得在CF Tree的构建和更新过程中能够高效地汇总节点信息。
CF树生成
在初始阶段,CF Tree是空的。随着数据点的不断插入,树逐渐生长。每个新数据点按照一定的规则插入到树的正确位置,这个规则通常是基于现有节点与新数据点距离的度量。如果一个数据点能被插入到某个节点而不超过预设的半径阈值T,它就会被插入到这个节点;否则,会尝试在树中其他位置找到合适的节点或者创建新的节点。
当数据点被插入到叶节点时,如果导致该节点的直径(即包含该节点所有点的最小超球体的直径)超过阈值T,则尝试分裂该节点。分裂过程是通过选取节点内两个相距最远的点作为种子点,然后根据其他点到这两个种子点的距离,将节点内的点重新分配到两个新的节点中。这种方法确保了每个节点内的点都紧密地聚集在一起,不会超过半径阈值T。
在非叶节点进行插入操作时,如果导致非叶节点的直径超过阈值T,也需要进行分裂。分裂非叶节点相对复杂,因为不仅要考虑子节点的分布,还要保持树的平衡。一种常见的方法是选取具有最大直径的子节点进行分裂,并确保分裂后的节点仍然能够满足直径小于T的条件。
BIRCH算法
将所有的训练集样本建立了CF Tree,一个基本的BIRCH算法就完成了。
BIRCH算法除了建立CF Tree来聚类,其实还有一些可选的算法步骤的,现在我们就来看看 BIRCH算法的流程。
1) 将所有的样本依次读入,在内存中建立一颗CF Tree, 建立的方法参考上一节。
2)(可选)将第一步建立的CF Tree进行筛选,去除一些异常CF节点,这些节点一般里面的样本点很少。对于一些超球体距离非常近的元组进行合并
3)(可选)利用其它的一些聚类算法比如K-Means对所有的CF元组进行聚类,得到一颗比较好的CF Tree.这一步的主要目的是消除由于样本读入顺序导致的不合理的树结构,以及一些由于节点CF个数限制导致的树结构分裂。
4)(可选)利用第三步生成的CF Tree的所有CF节点的质心,作为初始质心点,对所有的样本点按距离远近进行聚类。这样进一步减少了由于CF Tree的一些限制导致的聚类不合理的情况。
从上面可以看出,BIRCH算法的关键就是步骤1,也就是CF Tree的生成,其他步骤都是为了优化最后的聚类结果。
BIRCH类参树
1.threshold:该参数即叶节点每个CF的最大样本半径阈值T,它决定了每个CF里所有样本形成的超球体的半径阈值。
影响:threshold越小,CF Tree建立阶段的规模会越大,所花费的时间和内存也越多。如果数据的方差较大,一般需要增大threshold的默认值0.5以达到较好的聚类效果。
2.branching_factor:该参数表示CF Tree内部节点的最大CF数B和叶子节点的最大CF数L。在scikit-learn中,这两个参数被统一设置为同一个值。
影响:branching_factor越大,CF Tree中每个节点允许的最大CF数也越多。当样本量特别大(比如大于10万)时,增大branching_factor可以改善聚类效果。当然,具体选择多大还需要结合threshold进行调参决定。
3.n_clusters:该参数代表类别数K,在BIRCH算法中是可选的。如果不输入K,则算法会根据CF Tree里各叶子节点的样本情况自动决定类别数;如果输入K值,则算法会对叶子节点进行合并,直到类别数为K。
影响:在某些情况下,不输入类别数会导致聚类效果不佳。因此,如果有先验知识推荐输入一个合适的类别数。默认值为3。
4.compute_labels:布尔值,表示是否标示类别输出,默认是True。一般使用默认值挺好,这样可以看到聚类效果。
BIRCH运用实例
首先,我们载入一些随机数据,并看看数据的分布图:
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
from sklearn.datasets._samples_generator import make_blobs
# X为样本特征,Y为样本簇类别, 共1000个样本,每个样本2个特征,共4个簇,簇中心在[-1,-1], [0,0],[1,1], [2,2]
X, y = make_blobs(n_samples=1000, n_features=2, centers=[[-1,-1], [0,0], [1,1], [2,2]], cluster_std=[0.4, 0.3, 0.4, 0.3],
random_state =9)
plt.scatter(X[:, 0], X[:, 1], marker='o')
plt.show()
输出图如下:
现在我们用BIRCH算法来聚类,首先我们选择不输入可选的类别数K,看看聚类效果和Calinski-Harabasz 分数。
from sklearn.cluster import Birch
y_pred = Birch(n_clusters = None).fit_predict(X)
plt.scatter(X[:, 0], X[:, 1], c=y_pred)
plt.show()
from sklearn import metrics
print ("Calinski-Harabasz Score", metrics.calinski_harabasz_score(X, y_pred))
输出图如下:
对应的Calinski-Harabasz 分数输出为:
Calinski-Harabasz Score 2220.95253905
由于我们知道数据是4个簇随机产生的,因此我们可以通过输入可选的类别数4来看看BIRCH聚类的输出。代码如下:
y_pred = Birch(n_clusters = 4).fit_predict(X)
plt.scatter(X[:, 0], X[:, 1], c=y_pred)
plt.show()
print ("Calinski-Harabasz Score", metrics.calinski_harabasz_score(X, y_pred))
输出图如下:
对应的Calinski-Harabasz 分数输出为:
Calinski-Harabasz Score 2220.95253905
可见如果我们不输入类别数的话,在某些时候BIRCH算法的聚类效果并不一定好,因此这个可选的类别数K一般还是需要调参的。
对于threshold和branching_factor我们前面还没有去调参,使用了默认的threshold值0.5和默认的branching_factor值50.
现在我们将threshold从0.5降低为0.3,让BIRCH算法第一阶段的CF Tree规模变大,并观察Calinski-Harabasz 分数。
y_pred = Birch(n_clusters = 4, threshold = 0.3).fit_predict(X)
print ("Calinski-Harabasz Score", metrics.calinski_harabasz_score(X, y_pred))
对应的Calinski-Harabasz 分数输出为:
Calinski-Harabasz Score 3295.63492273
可见此时的聚类效果有了进一步的提升,那么是不是threshold越小越好呢?我们看看threshold从0.3降低为0.1时的情况。
y_pred = Birch(n_clusters = 4, threshold = 0.1).fit_predict(X)
print ("Calinski-Harabasz Score", metrics.calinski_harabasz_score(X, y_pred))
对应的Calinski-Harabasz 分数输出为:
Calinski-Harabasz Score 2155.10021808
也就是说threshold不是越小聚类效果越好。
我们基于threshold为0.3的情况,调试下branching_factor,将branching_factor从50降低为20.让BIRCH算法第一阶段的CF Tree规模变大。
y_pred = Birch(n_clusters = 4, threshold = 0.3, branching_factor = 20).fit_predict(X)
print ("Calinski-Harabasz Score", metrics.calinski_harabasz_score(X, y_pred))
对应的Calinski-Harabasz 分数输出为:
Calinski-Harabasz Score 3301.80231064
可见调试branching_factor也可以让聚类分数提高。那么和threshold类似,是不是branching_factor越小越好呢?我们将branching_factor从20降低为10,观察聚类分数:
y_pred = Birch(n_clusters = 4, threshold = 0.3, branching_factor = 10).fit_predict(X)
print ("Calinski-Harabasz Score", metrics.calinski_harabasz_score(X, y_pred))
对应的Calinski-Harabasz 分数输出为:
Calinski-Harabasz Score 2800.87840962 也就是说和threshold类似,branching_factor不是越小聚类效果越好,需要调参。
参考文章链接:刘建平机器学习BIRCH