数据挖掘->Canopy 聚类

来源:互联网 发布:网络兼职宣传语 编辑:程序博客网 时间:2024/06/05 18:34

K-means需要输入聚类个数K以及每个类的聚类中心。K和聚类中心的不同会导致聚类效果的不同,所以取K和聚类中心是个问题。

我们可以先用效率较高的粗聚类,先对数据集进行一个大概的聚类,再将粗聚类得到的聚类个数和聚类中心作为K-means的输入。


Canopy就是一个简单、快速的粗聚类方法(粗:不太准确)

该算法需一种快速的两点之间距离度量方法

可用:

•Jaccardsimilarity coefficient
•Cosinesimilarity

两个距离阈值T1,T2且满足T1>T2。

(本文最后有关于T1\T2取值的一些补充)



上图很好的解释了canopy算法的精髓:(canopy算法中,一个类即为一个canopy)

假设点x与聚类中心的距离为d

d>T1的点,点x不在这个canopy中

d<T1的点,点x在这个canopy中,且在该范围内的点还可以作为其他canopy的聚类中心。

d<T2的点,点x在这个canopy中,但是在该范围内的点不能作为其他canopy的聚类中心了。


单机版算法流程:

输入:点集D,阈值 T1,T2

1.随机取一个点d作为新canopy的聚类中心点,并将d从D中删除

2.计算D中所有点到d的距离

3.距离<T1的点都归到以d为中心的canopy中(注:由于T1>T2  ,则<T1也包括了<T2的点)

4.将所有<T2的点从D中删除(这些点不能再成为新的canopy的中心点了)

5.重复1-4,知道D为空。


这样就能选取Canopy聚类算法得到的canopies的聚类中心作为K-means的输入。



mahout中的Canopy聚类实现:

我没有深入研究源码,把我理解的思路说一下

输入文件为以下形式:每一行代表一个点,每个特征已空格隔开。

1 -0.213  -0.956  -0.003  0.056  0.091  0.017  -0.024  1
1 3.147  2.129  -0.006  -0.056  -0.063  -0.002  0.109  0
1 -2.165  -2.718  -0.008  0.043  -0.103  -0.156  -0.024  1
1 -4.337  -2.686  -0.012  0.122  0.082  -0.021  -0.042  1

...

hadoop将你输入的这个大文件,分割为很多小文件,交给多个mapper并行处理。


CanopyMapper类

1.在其setup阶段,定义一个Canopy集合,设置T1,T2和一个clusterFilter

(canopy中的点需要大于clusterFilter设置的值,该canopy才输出)

2.在map阶段,核心:addPointToCanopies()。 

public void addPointToCanopies(Vector point, Collection<Canopy> canopies) {boolean pointStronglyBound = false;for (Canopy canopy : canopies) {// 遍历所有的Canopy,当向量与Canopy的距离小于T1时,将向量加入这个Canopydouble dist = measure.distance(canopy.getCenter().getLengthSquared(), canopy.getCenter(), point);if (dist < t1) {if (log.isDebugEnabled()) {log.debug("Added point: {} to canopy: {}",AbstractCluster.formatVector(point, null),canopy.getIdentifier());}canopy.observe(point);//将point的特征(坐标)来更新canopy的s0 s1 s2,canopy中并没有list等容器来存放属于该canopy中的点。}pointStronglyBound = pointStronglyBound || dist < t2;}// 当canopies为空时将新建canopy,这个用的真奇妙!!if (!pointStronglyBound) {// 当距离大于T2时,新建一个canopyif (log.isDebugEnabled()) {log.debug("Created new Canopy:{} at center:{}", nextCanopyId,AbstractCluster.formatVector(point, null));}canopies.add(new Canopy(point, nextCanopyId++, measure));}}
并行会出现一个问题:mapper1中产生的聚类中心C1和mapper2产生的聚类中心C2,他们之间的距离小于T2。如果单机运行的话C2或者C1就不可能两者都作为聚类中心,所以reduce阶段在输出所有mapper的聚类中心前需要解决这一问题。如何解决?那就把所有mapper输出的聚类中心作为输入,再做一次addToCanopies哇。这样距离<T2的就不会再成为聚类中心了。

3.在cleanup阶段,只是简单的将样本向量添加到CanopyClusterer中,在cleanup阶段才更新每个canopy的参数,并将簇样本个数大于阈值的簇输出。

输出 key:"centroid" value:canopy1的聚类中心

key:"centroid" value:canopy2的聚类中心

key:"centroid" value:canopy3的聚类中心……


CanopyReducer类:

1.setup阶段,和map几乎一样,定义一个全新的Canopy集合,只是使用了T3和T4.

2.reduce阶段,因为map阶段输出的key都一样,而且我们在任务设定的时候,指定的reduce个数为1(参见前面Map-Reduce),所以,所有的数据都会传递给唯一的一个reduce。首先addToCanopies(),再来更新每个canopy,再将样本数目大于阈值的簇输出。

输出:canopy的中心点。

canopy中并没有list或其他容器来存放属于该canopy中的点,所以Mahout中通过函数clusterData计算点的归属簇。



map reduce阶段只是得到了各个canopy的聚类中心。


关于T1、T2:

T1决定了每个Cluster包含点的数目,这直接影响了Cluster的“重心”和“半径”,而T2则决定了Cluster的数目,T2太大会导致只有一个Cluster,而太小则会出现过多的Cluster。通过实验,T1和T2取值会严重影响到算法的效果

有人利用AIC、BIC或者交叉验证去做不是很懂,网上也有人给出了这个做法,仅供参考:

  1. 对数据进行采样。
  2. 计算所有文档之间的平均距离(使用要在Canopy中用的距离公式)。
  3. T1 = 平均距离 * 2;T2 = 平均距离。

参考:

http://blog.csdn.net/theonlytank2011/article/details/9493115   Mahout 中canopy源码分析,挺详细的

http://www.shahuwang.com/2012/08/14/canopy%E8%81%9A%E7%B1%BB%E7%AE%97%E6%B3%95.html

0 0
原创粉丝点击