Weka算法Classifier-trees-REPTree源码分析(二)

(接上篇)

一、剪枝过程

上篇分析完了tree节点的构建过程,在REPTree.buildClassifier之后如果设置了剪枝选项,则还有一个剪枝和backfit过程。

    if (!m_NoPruning) {
      m_Tree.insertHoldOutSet(prune);
      m_Tree.reducedErrorPrune();
      m_Tree.backfitHoldOutSet();
    }

其中insertHoldOutSet就是把剪枝用到的数据集传进去,不具体的区跟代码了。

重点卡一下reducedErrorPrune和backfitHoldOutSet过程。

二、Tree.reducedErrorPrune

    protected double reducedErrorPrune() throws Exception {
<span style="white-space:pre">	</span>//这个函数会返回该树及其子树的一个错误情况,如果是枚举类型返回的是分错的instance数量,数值类型返回的是与正确值的偏差的平方和
      // 如果是叶子节点就不做任何操作
      if (m_Attribute == -1) {
	return m_HoldOutError;//简单的说一下这个error怎么计算来的,使用<span style="font-size:18px;">insertHoldOutSet传入数据时会根据原先训练时的分布,来预测出传入数据的class,然后根据这个结果和真正的class值进行比对,就知道是否分的正确了</span>
      }

      //计算一下所有的子树的偏差
      double errorTree = 0;
      for (int i = 0; i < m_Successors.length; i++) {
	errorTree += m_Successors[i].reducedErrorPrune();
      }

      if (errorTree >= m_HoldOutError) {
	m_Attribute = -1;//如果子树偏差大于本身的偏差,那子树就没啥存在的意义了,直接去掉。
	m_Successors = null;
	return m_HoldOutError;
      } else {
	return errorTree;
      }
    }

可以看出,这个剪枝过程和J48相比还是简单不少的。

三、Tree.backfitHoldOutSet

protected void backfitHoldOutSet() throws Exception {

      // Insert instance into hold-out class distribution
      if (m_Info.classAttribute().isNominal()) {

	// Nominal case
	if (m_ClassProbs == null) {
	  m_ClassProbs = new double[m_Info.numClasses()];
	}
	System.arraycopy(m_Distribution, 0, m_ClassProbs, 0, m_Info.numClasses());
        for (int i = 0; i < m_HoldOutDist.length; i++) {
          m_ClassProbs[i] += m_HoldOutDist[i];
        }
        if (Utils.sum(m_ClassProbs) > 0) {
          Utils.normalize(m_ClassProbs);
        } else {
          m_ClassProbs = null;
        }
      } else {

	// Numeric case
        double sumOfWeightsTrainAndHoldout = m_Distribution[1] + m_HoldOutDist[0];
        if (sumOfWeightsTrainAndHoldout <= 0) {
          return;
        }
	if (m_ClassProbs == null) {
	  m_ClassProbs = new double[1];
	} else {
          m_ClassProbs[0] *= m_Distribution[1];
        }
	m_ClassProbs[0] += m_HoldOutDist[1];
	m_ClassProbs[0] /= sumOfWeightsTrainAndHoldout;
      }	

      // The process is recursive
      if (m_Attribute != -1) {
        for (int i = 0; i < m_Successors.length; i++) {
          m_Successors[i].backfitHoldOutSet();
        }
      }
    }

可以看出,就是一个根据新传入的数据集对原数据的分布进行重新计算,并且再对子树进行递归的调用backfit的过程,不再详细对代码进行注释了。

四、REPTree和J48的比较

同样都是分类树,REPTree和J48有很多不同点,下面简单的说一说这些差异。

1、对连续值排序的处理

J48在处理连续值的时候,每一个subset都要进行排序,而REPTree是先在主流程中对所有属性进行排序,并生成index传给Tree节点来进行处理的。

因此J48所耗时间比较长,而REPTree则占用较大内存(数据数量*数据属性列数量,因此也可以看到REPTree的代码中不断的有显式置空去尝试释放内存的操作),这是一个典型的时间和空间的tradeoff。

2、递归退出条件

J48的分裂停止条件有5个,

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

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

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

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

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

REPTree的停止条件有4个

(1)训练集数量小于2*minNum

(2)如果枚举类型,且在一个类中

(3)如果数值类型,方差小于一个给定值

(4)达到最大深度

可以看出,主要的不同在于REPTree使用方差来判断连续值是否结束分裂。

3、节点选择方式

J48使用信息增益率,REPTree使用信息增益

4、剪枝与backfit

J48的剪枝较为复杂,分成了collapse()和prune()两个操作,而REPTree的剪枝从逻辑上讲只是J48的collapse操作,并没有子树上提等较为激进的剪枝策略。

J48没有backfit,REPTree有backfit,这是因为J48就自己独立的classifyInstance过程并不依赖样本集的分布,而J48的classifyInstance是调用基类过程,需要自己存储一个分布,进而使用backfit来防止过拟合。

时间: 2024-10-22 13:56:52

Weka算法Classifier-trees-REPTree源码分析(二)的相关文章

netty 源码分析二

以服务端启动,接收客户端连接整个过程为例分析, 简略分为 五个过程: 1.NioServerSocketChannel 管道生成, 2.NioServerSocketChannel 管道完成初始化, 3.NioServerSocketChannel注册至Selector选择器, 4.NioServerSocketChannel管道绑定到指定端口,启动服务 5.NioServerSocketChannel接受客户端的连接,进行相应IO操作 Ps:netty内部过程远比这复杂,简略记录下方便以后回忆

[Android]Volley源码分析(二)Cache

Cache作为Volley最为核心的一部分,Volley花了重彩来实现它.本章我们顺着Volley的源码思路往下,来看下Volley对Cache的处理逻辑. 我们回想一下昨天的简单代码,我们的入口是从构造一个Request队列开始的,而我们并不直接调用new来构造,而是将控制权反转给Volley这个静态工厂来构造. com.android.volley.toolbox.Volley: public static RequestQueue newRequestQueue(Context conte

哇!板球 源码分析二

游戏主页面布局 创建屏下Score标签 pLabel = CCLabelTTF::create("Score", "Arial", TITLE_FONT_SIZE); //分数标签 //设置标签字体的颜色 pLabel->setColor (ccc3(0, 0, 0)); //设置文本标签的位置 pLabel->setPosition ( ccp ( SCORE_X, //X坐标 SCORE_Y //Y坐标 ) ); //将文本标签添加到布景中 this

baksmali和smali源码分析(二)

这一节,主要介绍一下 baksmali代码的框架. 我们经常在反编译android apk包的时候使用apktool这个工具,其实本身这个工具里面对于dex文件解析和重新生成就是使用的baksmali 和smali这两个jar包其中 baksmali是将 dex文件转换成便于阅读的smali文件的,具体使用命令如下:java -jar baksmali.jar classes.dex -o myout其中myout是输出的文件夹 而smali是将smali文件重新生成回 dex文件的具体使用的命

【梦幻连连连】源码分析(二)

转载请注明出处:http://blog.csdn.net/oyangyufu/article/details/24736711 GameLayer场景界面效果: 源码分析: GameLayer场景初始化,主要是初始化加载界面及背景音乐 bool GameLayer::init() { float dt=0.0f; if ( !CCLayerColor::initWithColor(ccc4(255, 255, 255, 255))) { return false; } this->initLoa

[Android]Fragment源码分析(二) 状态

我们上一讲,抛出来一个问题,就是当Activity的onCreateView的时候,是如何构造Fragment中的View参数.要回答这个问题我们先要了解Fragment的状态,这是Fragment管理中非常重要的一环.我们先来看一下FragmentActivity提供的一些核心回调: @Override protected void onCreate(Bundle savedInstanceState) { mFragments.attachActivity(this, mContainer,

JAVA Collection 源码分析(二)之SubList

昨天我们分析了ArrayList的源码,我们可以看到,在其中还有一个类,名为SubList,其继承了AbstractList. // AbstractList类型的引用,所有继承了AbstractList都可以传进来 private final AbstractList<E> parent; // 这个是其实就是parent的偏移量,从parent中的第几个元素开始的 private final int parentOffset; private final int offset; int s

Tomcat源码分析二:先看看Tomcat的整体架构

Tomcat源码分析二:先看看Tomcat的整体架构 Tomcat架构图 我们先来看一张比较经典的Tomcat架构图: 从这张图中,我们可以看出Tomcat中含有Server.Service.Connector.Container等组件,接下来我们一起去大致的看看这些组件的作用和他们之间的相互联系.在这之前,我们先补充一个知识点,也就是Tomcat它实现的功能点是什么呢?通过查找一些资料,这里参考下极客时间<深入拆解Tomcat_Jetty>中的总结,即Tomcat 要实现 2 个核心功能:

folly::AtomicHashmap源码分析(二)

本文为原创,转载请注明:http://www.cnblogs.com/gistao/ 背景 上一篇只是细致的把源码分析了一遍,而源码背后的设计思想并没有写,设计思想往往是最重要的,没有它,基本无法做整体性的优化或正确的使用, 但是根据结果反推原因是困难的,也极容易不到位,这里‘磕磕绊绊’写下自己的理解,另外对源码里的‘问题’也写出来. 简单 调试一个多线程程序是比较头疼的,而使用atomic来编写一个正确的多线程数据结构更是困难的,出了问题一般都不是随机问题,且等着复现看log吧, 所以简单这个

JDK中String类的源码分析(二)

1.startsWith(String prefix, int toffset)方法 包括startsWith(*),endsWith(*)方法,都是调用上述一个方法 1 public boolean startsWith(String prefix, int toffset) { 2 char ta[] = value; 3 int to = toffset; 4 char pa[] = prefix.value; 5 int po = 0; 6 int pc = prefix.value.l