您的位置:首页 > 其它

聚类-birch(层次方法的平衡迭代规约和聚类)

2017-02-04 11:54 465 查看

birch简述

birch全名利用层次方法的平衡迭代规约和聚类。
birch只需要单遍扫描数据集就可以进行聚类,它最小化IO,天生来应对大数据。brich是通过聚类特征树(CF-tree/ClusterFeature-tree)实现的,单遍扫描数据集后建立一棵存放于内存中的CF-tree,可以看作是数据的多层压缩。


聚类特征

每一个CF是一个三元组,可以用(N,LS,SS)表示。其中N代表这个CF中拥有的样本点的数量;LS代表了这个CF中拥有的样本点各特征维度的和向量,SS代表了这个CF中拥有的样本点各特征维度的平方和。
CF有一个很好的性质,就是满足线性关系,也就是CF1+CF2=(N1+N2,LS1+LS2,SS1+SS2)。
一棵CF树,还需要几个重要参数:
B:内部节点平衡因子,每个内部节点的子节点最大个数
L:叶子节点平衡因子,每个叶子节点的子节点最大个数
T:簇直径阈值,每个簇直径最大阈值,超过后,簇分裂


聚类特征树构造

一个聚类特征树样例:



算法最初,扫描数据集,拿到第一个数据点,创建一个空的Leaf和MinCluster,MinCluster座位Leaf的一个孩子。

当后续点需要插入树中时,把这个点封装为一个MinCluster,把新到的数据点记为CF_new,从树的根节点开始,根据D2(欧式距离)来找到CF_new与那个节点最近,就把CF_new加入那个子树上面去。这是一个递归的过程。递归的终止点是要把CF_new加入到一个MinCluster中,如果加入之后MinCluster的直径没有超过T,则直接加入,否则CF_new要单独作为一个簇,成为MinCluster的兄弟结点。插入之后注意更新该节点及其所有祖先节点的CF值。

插入新节点后,可能有些节点的孩子数大于了B(或L),此时该节点要分裂对于Leaf,它现在有L+1个MinCluster,我们要新创建一个Leaf,使它作为原Leaf的兄弟结点,同时注意每新创建一个Leaf都要把它插入到双向链表中。L+1个MinCluster要分到这两个Leaf中,怎么分呢?找出这L+1个MinCluster中距离最远的两个Cluster(根据D2),剩下的Cluster看离哪个近就跟谁站在一起。分好后更新两个Leaf的CF值,其祖先节点的CF值没有变化,不需要更新。这可能导致祖先节点的递归分裂,因为Leaf分裂后恰好其父节点的孩子数超过了B。

代码描述

private ClusteringFeature buildCFTree() {
NonLeafNode rootNode = null;
LeafNode leafNode = null;
Cluster cluster = null;

for (String[] record : totalDataRecords) {
cluster = new Cluster(record);

if (rootNode == null) {
// CF树只有1个节点的时候的情况
if (leafNode == null) {
leafNode = new LeafNode();
}
leafNode.addingCluster(cluster);
if (leafNode.getParentNode() != null) {
rootNode = leafNode.getParentNode();
}
} else {
if (rootNode.getParentNode() != null) {
rootNode = rootNode.getParentNode();
}

// 从根节点开始,从上往下寻找到最近的添加目标叶子节点
LeafNode temp = rootNode.findedClosestNode(cluster);
temp.addingCluster(cluster);
}
}

// 从下往上找出最上面的节点,返回根节点
LeafNode node = cluster.getParentNode();
NonLeafNode upNode = node.getParentNode();
if (upNode == null) {
return node;
} else {
while (upNode.getParentNode() != null) {
upNode = upNode.getParentNode();
}

return upNode;
}
}


可以看出,插入过程是从根节点开始找到最近的添加目标叶子节点,然后调用叶子节点的addingCluster方法把节点添加到树中。

leafNode的addingCluster方法

public void addingCluster(ClusteringFeature clusteringFeature) {
//更新聚类特征值
directAddCluster(clusteringFeature);

// 寻找到的目标集群
Cluster findedCluster = null;
Cluster cluster = (Cluster) clusteringFeature;

double disance = Integer.MAX_VALUE;
double errorDistance = 0;
boolean needDivided = false;
if (clusterChilds == null) {
clusterChilds = new ArrayList<>();
clusterChilds.add(cluster);
cluster.setParentNode(this);
} else {
for (Cluster c : clusterChilds) {
errorDistance = ClusteringFeature.computerClusterDistance(c, cluster);
if (errorDistance < disance) {
// 选出簇间距离最近的
disance = errorDistance;
findedCluster = c;
}
}

ArrayList<double[]> data1 = (ArrayList<double[]>) findedCluster.getData().clone();
ArrayList<double[]> data2 = cluster.getData();
data1.addAll(data2);
// 如果添加后的聚类的簇间距离超过给定阈值,需要额外新建簇
if (ClusteringFeature.computerInClusterDistance(data1) > BIRCHTool.T) {
// 如果添加后簇的簇间距离超过T,当前簇作为新的簇
clusterChilds.add(cluster);
cluster.setParentNode(this);
// 叶子节点的孩子数不能超过平衡因子L
if (clusterChilds.size() > BIRCHTool.L) {
needDivided = true;
}
} else {
findedCluster.directAddCluster(cluster);
cluster.setParentNode(this);
}
}

if(needDivided){
if(parentNode == null){
parentNode = new NonLeafNode();
}else{
parentNode.getLeafChilds().remove(this);
}

LeafNode[] nodeArray = divideLeafNode();
for(LeafNode n: nodeArray){
parentNode.addingCluster(n);
}
}
}


先找到距离最近的簇,把当前簇添加到最近的簇中,如果添加后簇的簇间距离超过T,当前簇作为新的簇,如果叶子节点的孩子数超过平衡因子L,则叶子节点需要分裂。分裂后分裂为2个叶子节点,然后调用非叶子节点的addingCluster方法,依次向上更新父节点。

NonLeafNode的addingCluster方法:

public void addingCluster(ClusteringFeature clusteringFeature) {
LeafNode leafNode = null;
NonLeafNode nonLeafNode = null;
NonLeafNode[] nonLeafNodeArrays;
boolean neededDivide = false;
// 更新聚类特征值
directAddCluster(clusteringFeature);

if (clusteringFeature instanceof LeafNode) {
leafNode = (LeafNode) clusteringFeature;
} else {
nonLeafNode = (NonLeafNode) clusteringFeature;
}

if (nonLeafNode != null) {
neededDivide = addingNeededDivide(nonLeafNode);

if (neededDivide) {
if (parentNode == null) {
parentNode = new NonLeafNode();
} else {
parentNode.nonLeafChilds.remove(this);
}

nonLeafNodeArrays = this.nonLeafNodeDivided();
for (NonLeafNode n1 : nonLeafNodeArrays) {
parentNode.addingCluster(n1);
}
}
} else {
neededDivide = addingNeededDivide(leafNode);

if (neededDivide) {
if (parentNode == null) {
parentNode = new NonLeafNode();
} else {
parentNode.nonLeafChilds.remove(this);
}

nonLeafNodeArrays = this.leafNodeDivided();
for (NonLeafNode n2 : nonLeafNodeArrays) {
parentNode.addingCluster(n2);
}
}
}
}


非叶子节点分裂与叶子节点分裂类似。

详细代码参考:

https://github.com/zhanggw/algorithm/tree/master/machine-learning/Birch-Cluster/src/main/java/com/zhanggw/cluster/birch

参考:

http://blog.csdn.net/androidlushangderen/article/details/43532111

http://blog.csdn.net/jiutianhe/article/details/39313333

http://www.cnblogs.com/pinard/p/6179132.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法