首页 > 代码库 > Weka算法Clusterers-Xmeans源码分析(二)

Weka算法Clusterers-Xmeans源码分析(二)

上一篇文章不知道为什么前面部分乱码了,我会在这篇文章后面写个总结,总结之前开头的部分。


首先处理两个问题,第一个是splitCenter用于对已有中心进行分裂,第二个是newCentersAfterSplit,根据分裂后的BIC计算出新的聚类中心,这个分裂机制可以算是XMeans区别于KMeans的最大不同点。


一、splitCenter

 protected Instances splitCenter(Random random,
			        Instance center,
			        double variance,
			        Instances model) throws Exception {
    m_NumSplits++;
    AlgVector r = null;
    Instances children = new Instances(model, 2);

    if (m_DebugVectorsFile.exists() && m_DebugVectorsFile.isFile()) {
      Instance nextVector = getNextDebugVectorsInstance(model);
      PFD(D_RANDOMVECTOR, "Random Vector from File " + nextVector);
      r = new AlgVector(nextVector);
    }
    else {
      //这个model是表头,r是生成一个随机向量,每一维都是0到1之间
      r = new AlgVector(model, random);
    }
    r.changeLength(Math.pow(variance, 0.5));//改变向量的长度为sqrt(variance)这个variance就是聚类点到聚类中心的平均偏差
    PFD(D_RANDOMVECTOR, "random vector *variance "+ r);
    
    // 首先生成两个聚类中心的向量
    AlgVector c = new AlgVector(center);
    AlgVector c2 = (AlgVector) c.clone();
    c = c.add(r);//c+r
    Instance newCenter = c.getAsInstance(model, random);
    children.add(newCenter);
    PFD(D_FOLLOWSPLIT, "first child "+ newCenter);
    
    // c2-r
    c2 = c2.substract(r);
    newCenter = c2.getAsInstance(model, random);
    children.add(newCenter);
    PFD(D_FOLLOWSPLIT, "second child "+ newCenter);

    return children;
  }

执行过后的结果如图所示:



二、newCentersAfterSplit

protected Instances newCentersAfterSplit(double[] pbic, 
					 double[] cbic,
					 double cutoffFactor,
					 Instances splitCenters) {

    // 
    boolean splitPerCutoff = false;
    boolean takeSomeAway = false;
    boolean[] splitWon = initBoolArray(m_ClusterCenters.numInstances());//这个数组存放每个聚类中心是否分裂的决定
    int numToSplit = 0;
    Instances newCenters = null;
    
    for (int i = 0; i < cbic.length; i++) {
      if (cbic[i] > pbic[i]) {
	// 如果child的BIC比较大,就分裂,为什么是BIC越大越好而不是越小越好?Weka的BIC公式貌似没取负。
	splitWon[i] = true; numToSplit++;
	PFD(D_FOLLOWSPLIT, "Center " + i + " decide for children");
      }
      else {
	// 默认是false,不用重新赋值。
	PFD(D_FOLLOWSPLIT, "Center " + i + " decide for parent");
      }
    }

    if ((numToSplit == 0) && (cutoffFactor > 0)) {
      splitPerCutoff = true;
      
      // 如果没有节点需要分裂,则使用cutoffFactor来决定要分裂的数量,这么做的原因是为了防止陷入局部最优点。
      numToSplit = (int) 
        ((double) m_ClusterCenters.numInstances() * m_CutOffFactor); 
    }

    // 把pbic和cbic进行相减,并排序,以便找出差最大的,优先分裂。
    double[] diff = new double [m_NumClusters];
    for (int j = 0; j < diff.length; j++) {
      diff[j] = pbic[j] - cbic[j];
    }    
    int[] sortOrder = Utils.sort(diff);
    
    //检查一下最多的可分裂数量
    int possibleToSplit = m_MaxNumClusters - m_NumClusters; 

    if (possibleToSplit > numToSplit) {
      // 如果可分裂数量多于numToSplit,就按照numToSplit去分裂
      possibleToSplit = numToSplit;
    }
    else
      takeSomeAway = true;

    // 如果有splitPerCuteoff标,说明使用了cutoffFactor来决定分裂多少,这时候splitWon里面肯定都是false,需要设置一定数量的为true
    if (splitPerCutoff) {
      for (int j = 0; (j < possibleToSplit) && (cbic[sortOrder[j]] > 0.0);
	   j++) {
	splitWon[sortOrder[j]] = true;
      }
      m_NumSplitsStillDone += possibleToSplit;
    } 
    else {
      // take some splits away if max number of clusters would be exceeded
      if (takeSomeAway) {
	int count = 0;
	int j = 0;//如果有这个标,说明能分裂的数量小于了splitWon中的数量,需要将一定数量得true设置为false
	for (;j < splitWon.length && count < possibleToSplit; j++){
	  if (splitWon[sortOrder[j]] == true) count++;
	}
	
	while (j < splitWon.length) {
	  splitWon[sortOrder[j]] = false;
	  j++;
	}
      }
    }
   
    // 进行分裂操作,即若splitWon==true就分裂,否则保持原样
    if (possibleToSplit > 0) 
      newCenters = newCentersAfterSplit(splitWon, splitCenters);
    else
      newCenters = m_ClusterCenters;
    return newCenters;
  }



三、总结

首先来回顾一下整个算法流程:

1、随机选取聚类中心

2、对于每个实例,分配到离其最近的聚类中心

3、重新计算新的聚类中心

4、尝试对新的聚类中心进行分裂

5、回到2,若连续两个循环结果相同,则结束

可以看出,和传统的Kmeans相比,Xmeans最重要的改进在于可以自动决定聚类中心的数量,并进行“智能”的分裂。


最后总结一下第一篇文章开头(虽然现在已经乱码了)提出的问题:

1、如何计算各用例之间的“距离”

答:默认使用欧式距离,但可以定制传入距离函数,来计算任意两个用例的距离。

2、所谓的“迭代退出条件”是什么。

迭代有两层,分别为外层迭代和内层迭代,每一次外层迭代产生不同的聚类中心,每一次内层迭代将用例分配到各聚类中心。

外层迭代退出条件有三个:(1)达到最大迭代次数,(2)两次外层迭代产生聚类中心数量相等,即聚类中心没有分裂,(3)达到最大的聚类个数

内层迭代退出条件有二个:(1)两次内层迭代所有用例分配到的聚类中心一样(2)达到最大迭代次数

3、如何确定聚类中心

答:所有属性的算数平均值为聚类中心。

4、在实现过程中有没有一些用来提高效率的trick

使用了KDTree来寻找某个用例离得最近的中心。




Weka算法Clusterers-Xmeans源码分析(二)