Weka算法Classifier-tree-J48源码分析(四)总结


一、ClassifyInstance

首先先说一下构造好的分类树是如何对一个新的Instance进行区分。

直观上,会对树进行一个检索,从根节点根据属性的不同,最终走到叶子节点,得到具体的分类。

但Weka在实现上,是遍历了这个Instance属于不同的class的可能性,并从中选出了一个最大的,代码如下:

  public double classifyInstance(Instance instance) 
    throws Exception {

    double maxProb = -1;
    double currentProb;
    int maxIndex = 0;
    int j;

    for (j = 0; j < instance.numClasses(); j++) {
      currentProb = getProbs(j, instance, 1);
      if (Utils.gr(currentProb,maxProb)) {
	maxIndex = j;
	maxProb = currentProb;
      }
    }

    return (double)maxIndex;
  }
而getProbs函数关键代码如下:

    if (m_isLeaf) {
      return weight * localModel().classProb(classIndex, instance, -1);
    } else {
      int treeIndex = localModel().whichSubset(instance);
      if (treeIndex == -1) {
	double[] weights = localModel().weights(instance);
	for (int i = 0; i < m_sons.length; i++) {
	  if (!son(i).m_isEmpty) {
	    prob += son(i).getProbs(classIndex, instance, 
				    weights[i] * weight);
	  }
如果是一个叶子节点,则直接从本节点的分布就能确定当前instance属于相关class的可能性,否则就要取出treeIndex进行判断,这里的treeIndex==-1代表这个Instance属于不同的branch,换句话说,本节点的Model不能对此Instance给出一个确定的划分。

在分类树中这种情况理论上是不会出现的,weka在这里其实是用treeIndex==-1来处理了相关属性缺失的情况。

whichSubset代码如下:

  public final int whichSubset(Instance instance) 
       throws Exception {
    
    if (instance.isMissing(m_attIndex))
      return -1;
    else{
      if (instance.attribute(m_attIndex).isNominal())
	return (int)instance.value(m_attIndex);
      else
	if (Utils.smOrEq(instance.value(m_attIndex),m_splitPoint))
	  return 0;
	else
	  return 1;
    }
  }
如果属性缺失,则直接返回-1,如果是离散值,则根据属性进行选择,如果是连续值,则通过训练时得到的分裂点进行判断。



二、总结

接下来可以回答第二篇博客开头时提到的4个问题了。

1、如何控制分类树的精度。

答:使用minNoObj参数来控制分类树的精度,节点停止分裂的条件有5个:

(1)所有的instances已经属于同一个分类(selectModel里)

(2)instances数量小于2*minNoObj(selectModel里)

(3)一个分裂产生的信息增益石0(selectModel里)

(4)对离散值进行分裂节点的计算时,超过一个的Bag里的instance数量小于minNoObj(spliter里)

(5)对连续值进行分裂计算时,有效instances数量小于2*minNoObj(spliter里)

2、如何处理缺失的值(MissingValue)

答:对缺失值得处理分两种,其一是训练阶段的缺失值处理,方法是直接忽略掉这条记录。在分类阶段对缺失值得处理是,忽略该属性,使其在不同的branch上进行计算,最后将概率结果进行合并。

3、如何对连续值进行离散化。

答:Spliter首先对连续值进行排序,之后从前到后遍历每一个值当做分裂点,对分裂结果计算信息增益率最大的作为最终分裂点构建不同的branch。

4、如何进行分类树的剪枝。

答:略,参见第二篇博客的collapse方法和prune方法。

关于J48差不多也就说到这里了,通过分析源码可以看到Weka的实现和原始的C4.5算法还是有相当多的不一样的地方。下一篇将对RandomForest算法进行分析。


你可能感兴趣的:(源码,算法,机器学习,weka,分类器)