Mahout源码MeanShiftCanopy聚类算法分析(3)

继上篇博客,分析MeanShiftCanopyMapperFollow:

首先要获得数据,MeanShiftCanopyMapperFollow的getMapData函数是改编自KMeansDriver中的一段代码,主要是读取序列文件的Value值,把这些Value值返回一个变量,其中涉及到强制转型,这里转型的代码改编一下就可以适应来读取不同的序列文件的value值了。

因为使用的是synthetic_control.data数据,所以这里读取后的数据应该是一个600size的ClusterWritable的集合,通过设置断点看到的数据如下:


可以看到第一个CLusterWritable的value值是和原始数据的第一行一样的,如下:

28.7812 34.4632 31.3381 31.2834 28.9207 33.7596 25.3969 27.7849 ;

MeanShiftCanopyMapperFollow的setup函数主要是把相关的参数赋值给一个MeanShiftCanopy变量,主要包含的参数有:判断是否退出循环的阈值大小、kernelProfile的类名(这个暂时不清楚是用来干什么的)、距离计算方法、是否在结束循环后对原始数据进行分类标志、在Mapper中计算中用到的t1和t2阈值,如下图:


MeanShiftCanopyMapper的map函数的主要操作是获得每一行的值,然后执行mergeCanopy方法,看mergeCanopy方法,如下:

public void mergeCanopy(MeanShiftCanopy aCanopy,
      Collection<MeanShiftCanopy> canopies) {
    MeanShiftCanopy closestCoveringCanopy = null;
    double closestNorm = Double.MAX_VALUE;
    for (MeanShiftCanopy canopy : canopies) {
      double norm = measure.distance(canopy.getCenter(), aCanopy.getCenter());
      double weight = kernelProfile.calculateDerivativeValue(norm, t1);
      if (weight > 0.0) {
        aCanopy.touch(canopy, weight);
      }
      if (norm < t2 && (closestCoveringCanopy == null || norm < closestNorm)) {
        closestNorm = norm;
        closestCoveringCanopy = canopy;
      }
    }
    if (closestCoveringCanopy == null) {
      canopies.add(aCanopy);
    } else {
      closestCoveringCanopy.merge(aCanopy, runClustering);
    }
  }
这里面的for循环主要是循环已经得到的canopies里面的每个值。比如针对输入的前三条记录,如下:

28.7812 34.4632 31.3381 31.2834 28.9207 33.7596 25.3969 27.7849 35.2479 27.1159 32.8717 29.2171 36.0253 32.337 34.5249 32.8717 34.1173 26.5235 27.6623 26.3693 25.7744 29.27 30.7326 29.5054 33.0292 25.04 28.9167 24.3437 26.1203 34.9424 25.0293 26.6311 35.6541 28.4353 29.1495 28.1584 26.1927 33.3182 30.9772 27.0443 35.5344 26.2353 28.9964 32.0036 31.0558 34.2553 28.0721 28.9402 35.4973 29.747 31.4333 24.5556 33.7431 25.0466 34.9318 34.9879 32.4721 33.3759 25.4652 25.8717
24.8923 25.741 27.5532 32.8217 27.8789 31.5926 31.4861 35.5469 27.9516 31.6595 27.5415 31.1887 27.4867 31.391 27.811 24.488 27.5918 35.6273 35.4102 31.4167 30.7447 24.1311 35.1422 30.4719 31.9874 33.6615 25.5511 30.4686 33.6472 25.0701 34.0765 32.5981 28.3038 26.1471 26.9414 31.5203 33.1089 24.1491 28.5157 25.7906 35.9519 26.5301 24.8578 25.9562 32.8357 28.5322 26.3458 30.6213 28.9861 29.4047 32.5577 31.0205 26.6418 28.4331 33.6564 26.4244 28.4661 34.2484 32.1005 26.691
31.3987 30.6316 26.3983 24.2905 27.8613 28.5491 24.9717 32.4358 25.2239 27.3068 31.8387 27.2587 28.2572 26.5819 24.0455 35.0625 31.5717 32.5614 31.0308 34.1202 26.9337 31.4781 35.0173 32.3851 24.3323 30.2001 31.2452 26.6814 31.5137 28.8778 27.3086 24.246 26.9631 25.2919 31.6114 24.7131 27.4809 24.2075 26.8059 35.1253 32.6293 31.0561 26.3583 28.0861 31.4391 27.3057 29.6082 35.9725 34.1444 27.1717 33.6318 26.5966 25.5387 32.5434 25.5772 29.9897 31.351 33.9002 29.5446 29.343
25.774 30.5262 35.4209 25.6033 27.97 25.2702 28.132 29.4268 31.4549 27.32 28.9564 28.9916 29.9578 30.2773 30.4447 24.3037 24.314 35.0966 25.3679 32.0968 33.3303 25.0102 35.3155 31.6264 29.2806 34.2021 26.5077 32.2279 25.5265 24.824 27.5587 28.3714 32.3667 26.9752 35.9346 35.1146 24.3749 27.6083 27.8433 29.8557 32.4185 26.8908 31.3209 29.3849 34.3336 24.7381 35.769 31.8725 34.2054 31.156 34.6292 28.7261 28.2979 31.5787 34.6156 32.5492 30.9827 24.8938 27.3659 25.3069
当第一条记录来的时候,因为canopies变量为空,所以没有执行for循环,直接执行canopies.add(aCanopy)这里,且aCanopy的s0为0,说明这个canopy里面没有一个值;

当第二条记录来的时候,canopies里面已经有一个值,可以循环,canopies(1)和第二条记录的distance即上面的norm变量为42.18<t1=47.6,接着

kernelProfile.calculateDerivativeValue(norm, t1)
进入这个方法,可以看到这个的方法的意思是当norm<t1时返回1,否则返回0,那么就应该返回1,则进行aCanopy.touch(canopy, weight);这一行,这个touch函数是把调用的变量aCanopy执行一遍observe方法,同时把canopy(这个变量这时就是canopie(1))执行一遍observe方法,这个observe方法在前面已经介绍过了,所以执行完这一步骤后,aCanopy的s0变为1(s1、s2相应变化),canopies(1)的s0变为1(s1、s2相应变化);然后是norm < t2 && (closestCoveringCanopy == null || norm < closestNorm),由于第一遍循环没有执行下面的,所以这里暂时不介绍。然后就又到了canopies.add(aCanopy);把aCanopy添加到canopies中,即为canopies(2);

当第三条记录来的时候,首先遍历canopies(1),这时的距离norm=38.78<t1=47.6,则进行touch方法,然后canopies(1)的s0变为2(s1、s2相应变化),aCanopy的s0变为1,(s1、s2相应变化),然后遍历canopies(2),这时的距离norm=35<t1=47.6,则进入touch方法,所以canopies(2)的s0变为2(s1、s2相应变化),aCanopy的s0变为2(s1、s2相应变化),然后把aCanopy加入到canopies中;

执行完上面三步后,canopies后变为如下的样子:

Mahout源码MeanShiftCanopy聚类算法分析(3)_第1张图片
等全部600条记录全部读取操作完成后,输出的canopies含有600size,但是每个canopy的s0不尽相同,这个就是根据距离来进行判断了。

最后是cleanup函数了,这个函数取得map的输出canopies,然后进行遍历,遍历的主体如下:

clusterer.shiftToMean(canopy);
	      ClusterWritable clusterWritable = new ClusterWritable();
	      clusterWritable.setValue(canopy);
	      map.put(new Text(String.valueOf(reducer)), clusterWritable);
clsusterer.shiftToMean(canopy)方法如下所示:

canopy.observe(canopy.getCenter(), canopy.getMass());
    canopy.computeConvergence(measure, convergenceDelta);
    canopy.computeParameters();
第一句observe把canopy的中心作为一个点加入到这个canopy中,比如canopies(1)的s0=99,执行完这一步后s1=100,(s1、s2相应变化);

第二句主要是计算是否前后两次中心点达到满足阈值的要求(前一次中心点为原来的值,而后一次中心点的值是通过s1/s0得到的);

第三句是把canopies(1)的中心值center进行赋值,即把后一次中心点替换前一次中心点值。

cleanup后面的就是简单的输出了,map.put对应于原来的context.write方法;还有就是:

if (reducer >= numReducers) {
	    	  reducer=0;
	      }
这里把reducer基本都设置为0了,所以输出到reducer中的key都是0,即reducer中输入是一个组,对应于前篇博客的第一次循环的图,如下:

Mahout源码MeanShiftCanopy聚类算法分析(3)_第2张图片

这样基本mapper的数据逻辑流就基本分析完成。


分享,快乐,成长


转载请注明出处:http://blog.csdn.net/fansy1990 


你可能感兴趣的:(Mahout,源码分析,shift,mean,canopy聚类算)