Weka算法Classifier-meta-Bagging源码分析

Bagging部分比较简单,算法和代码放到一起写了。

一、Bagging算法

严格来看Bagging并不能算是一种分类算法,Bagging和Boosting一样,是一种组合基本分类器的方法,也就是使用多个基分类器来获取更为强大的分类器,其核心思想是有放回的抽样。

Bagging算法的训练流程:

1、从样本集中有放回的抽样M个样本。

2、用这M个样本训练基分类器C。

3、重复这个过程X次,得到若干个基分类器。

Bagging算法的预测流程:

1、对于新传入实例A,用这X个新分类器得到一个分类结果的列表。

2、若待分类属性是数值型(回归),求这个列表的算数平均值作为结果返回。

3、若待分类属性是枚举类型(分类),按这个列表对分类结果进行投票,返回票数最高的。

二、Weka代码实现

(1)基分类器

Weka中的默认基分类器使用的是REPTree,也就是Fast decision tree learner,至于这个具体是个什么,后面我再写文章进行分析。

 public Bagging() {

    m_Classifier = new weka.classifiers.trees.REPTree();
  }

(2)构建过程BuildClassifier

整个BuildClassifier都是围绕标m_CalcOutOfBag来展开的,这个m_CalcOutOfBag标识的意思是:是否计算OutofBag的错误比例。

假如我们对训练集M进行抽样,抽样的数量和M的数量是一样的,那么肯定会有一些样本并没有被抽到(为什么?因为是有放回的抽样),这个标识就是用来评测这些没抽到的样本的准确率,如果没有这个标,那么这个准确率到后面就不会被计算了。

if (m_CalcOutOfBag && (m_BagSizePercent != 100)) {
      throw new IllegalArgumentException("Bag size needs to be 100% if "
          + "out-of-bag error is to be calculated!");
    }

    int bagSize = data.numInstances() * m_BagSizePercent / 100;
    Random random = new Random(m_Seed);

    boolean[][] inBag = null;
    if (m_CalcOutOfBag)
      inBag = new boolean[m_Classifiers.length][];

    for (int j = 0; j < m_Classifiers.length; j++) {
      Instances bagData = null;

      // create the in-bag dataset
      if (m_CalcOutOfBag) {
        inBag[j] = new boolean[data.numInstances()];
        // bagData = resampleWithWeights(data, random, inBag[j]);
        bagData = data.resampleWithWeights(random, inBag[j]);
      } else {
        bagData = data.resampleWithWeights(random);
        if (bagSize < data.numInstances()) {
          bagData.randomize(random);
          Instances newBagData = new Instances(bagData, 0, bagSize);
          bagData = newBagData;
        }
      }

这一部分是抽样,首先如果有m_CalcOutOfBag标,则必须要求抽样比例是100%。

其次算出要抽样的大小。

inBag数组是用来记录Instances中哪些样本被抽到了哪些没被抽到。

data.resampleWithWeight就是进行有放回的抽样。

      if (m_Classifier instanceof Randomizable) {
        ((Randomizable) m_Classifiers[j]).setSeed(random.nextInt());
      }

      // build the classifier
      m_Classifiers[j].buildClassifier(bagData);

接着是构建分类树的过程,调用具体classifier的buildClassifier方法。

最后是计算OutOfBag的过程,代码我已写注释。

if (getCalcOutOfBag()) { //如果有这个标就计算
      double outOfBagCount = 0.0; //错误的权重和
      double errorSum = 0.0;//错误的偏差值的和
      boolean numeric = data.classAttribute().isNumeric();//是否是连续数值
      for (int i = 0; i < data.numInstances(); i++) {
        double vote;//代表投票结果
        double[] votes;//代表投票
        if (numeric)
          votes = new double[1];//如果是数值,则取平均数,计算平均数的过程一个数组单元就够了
        else
          votes = new double[data.numClasses()];//否则则要进行投票

        // determine predictions for instance
        int voteCount = 0;
        for (int j = 0; j < m_Classifiers.length; j++) {
          if (inBag[j][i])
            continue;//如果已经被采样,就忽略,因为要计算的是OutOfBag

          voteCount++;//记录有多少样本被计算
          if (numeric) {
            votes[0] = m_Classifiers[j].classifyInstance(data.instance(i));//数值型则直接把预测结果累加
          } else {
            double[] newProbs = m_Classifiers[j].distributionForInstance(data
                .instance(i));
            for (int k = 0; k < newProbs.length; k++) {
              votes[k] += newProbs[k]; //枚举型则要把所有枚举概率进行累加
            }
          }
        }

        // "vote"
        if (numeric) {
          vote = votes[0];
          if (voteCount > 0) {
            vote /= voteCount; // 数值型取均值
          }
        } else {
          if (Utils.eq(Utils.sum(votes), 0)) {
          } else {
            Utils.normalize(votes);//归一化
          }
          vote = Utils.maxIndex(votes); // 选出最大的index
        }
        outOfBagCount += data.instance(i).weight();//累加权重
        if (numeric) {
          errorSum += StrictMath.abs(vote - data.instance(i).classValue())
              * data.instance(i).weight();//累加错误偏差
        } else {
          if (vote != data.instance(i).classValue())
            errorSum += data.instance(i).weight();//如果是枚举就对出错进行计数
        }
      }

      m_OutOfBagError = errorSum / outOfBagCount;//最后取个平均值
    } else {
      m_OutOfBagError = 0;//如果没有那个标就不计算了
    }

三、根据权重进行无放回抽样的过程

也就是 data.resampleWithWeights(random, inBag[j]);这个方法,感觉看了一下还挺有意思的,就放上来剖析一下。

重载形式有3个,前两个都会调用第三个:

  public Instances resampleWithWeights(Random random, double[] weights) {

    return resampleWithWeights(random, weights, null);
  }
  public Instances resampleWithWeights(Random random, boolean[] sampled) {

    double[] weights = new double[numInstances()];
    for (int i = 0; i < weights.length; i++) {
      weights[i] = instance(i).weight();
    }
    return resampleWithWeights(random, weights, sampled);
  }
public Instances resampleWithWeights(Random random, double[] weights,
    boolean[] sampled) {

    if (weights.length != numInstances()) {
      throw new IllegalArgumentException("weights.length != numInstances.");
    }

    Instances newData = new Instances(this, numInstances());
    if (numInstances() == 0) {
      return newData;
    }

    // Walker's method, see pp. 232 of "Stochastic Simulation" by B.D. Ripley
    double[] P = new double[weights.length];
    System.arraycopy(weights, 0, P, 0, weights.length);
    Utils.normalize(P);
    double[] Q = new double[weights.length];
    int[] A = new int[weights.length];
    int[] W = new int[weights.length];
    int M = weights.length;
    int NN = -1;
    int NP = M;
    for (int I = 0; I < M; I++) {
      if (P[I] < 0) {
        throw new IllegalArgumentException("Weights have to be positive.");
      }
      Q[I] = M * P[I];
      if (Q[I] < 1.0) {
        W[++NN] = I;
      } else {
        W[--NP] = I;
      }
    }
    if (NN > -1 && NP < M) {
      for (int S = 0; S < M - 1; S++) {
        int I = W[S];
        int J = W[NP];
        A[I] = J;
        Q[J] += Q[I] - 1.0;
        if (Q[J] < 1.0) {
          NP++;
        }
        if (NP >= M) {
          break;
        }
      }
      // A[W[M]] = W[M];
    }

    for (int I = 0; I < M; I++) {
      Q[I] += I;
    }

    for (int i = 0; i < numInstances(); i++) {
      int ALRV;
      double U = M * random.nextDouble();
      int I = (int) U;
      if (U < Q[I]) {
        ALRV = I;
      } else {
        ALRV = A[I];
      }
      newData.add(instance(ALRV));
      if (sampled != null) {
        sampled[ALRV] = true;
      }
      newData.instance(newData.numInstances() - 1).setWeight(1);
    }

    return newData;
  }

这个所谓的

Walker's method, see pp. 232 of "Stochastic Simulation" by B.D. Ripley

我找了半天也不知道是个啥算法,代码也没啥注释,大体一看没看懂,等下次有机会再把这个函数的算法补上吧。

时间: 2024-08-24 14:25:15

Weka算法Classifier-meta-Bagging源码分析的相关文章

K-近邻算法的Python实现 : 源码分析

网上介绍K-近邻算法的例子很多,其Python实现版本基本都是来自于机器学习的入门书籍<机器学习实战>,虽然K-近邻算法本身很简单,但很多初学者对其Python版本的源代码理解不够,所以本文将对其源代码进行分析. 什么是K-近邻算法? 简单的说,K-近邻算法采用不同特征值之间的距离方法进行分类.所以它是一个分类算法. 优点:无数据输入假定,对异常值不敏感 缺点:复杂度高 好了,直接先上代码,等会在分析:(这份代码来自<机器学习实战>) def classify0(inx, data

OpenStack_Swift源码分析——Ring的rebalance算法源代码详细分析

今天有同学去百度,带回一道面试题,和大家分享一下: 打印: n=1 1 n=2 3 3 2 4 1 1 4 5 5 n=3 7 7 7 7 6 8 3 3 2 6 8 4 1 1 6 8 4 5 5 5 8 9 9 9 9 提供一段参考程序: <pre name="code" class="cpp">// ConsoleApplication1.cpp: 主项目文件. #include "stdafx.h" #include &quo

Openck_Swift源码分析——增加、删除设备时算法具体的实现过程

1 初始添加设备后.上传Object的具体流程 前几篇博客中,我们讲到环的基本原理即具体的实现过程,加入我们在初始创建Ring是执行如下几条命令: ?swift-ring-builder object.builder create 5 3 1 ?swift-ring-builder object.builder add z1-127.0.0.1:6010/sdb1 100 ?swift-ring-builder object.builder add z2-127.0.0.1:6020/sdb2 

OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波

http://blog.csdn.net/chenyusiyuan/article/details/8710462 OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波 2013-03-23 17:44 16963人阅读 评论(28) 收藏 举报 分类: 机器视觉(34) 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] KAZE系列笔记: OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波 OpenCV学习笔记(28)KA

WEKA学习——CSVLoader 实例训练 和 源码分析

简介: Weka支持多种数据导入方式,CSVLoader是能从csv文件加载数据集,也可以保存为arff格式文件.官方介绍文件:Converting CSV to ARFF ( http://weka.wikispaces.com/Converting+CSV+to+ARFF) CSVLoader加载文件,关键是对文件字段属性名称和属性的类型需要自己定义,这样才能得到满足自己需要的数据集. CSVLoader通过options设置,可以设置每一列的属性为Nominal,String,Date类型

OpenStack_Swift源码分析——Ring基本原理及一致性Hash算法

1.Ring的基本概念 Ring是swfit中最重要的组件,用于记录存储对象与物理位置之间的映射关系,当用户需要对Account.Container.Object操作时,就需要查询对应的Ring文件(Account.Container.Object都有自己对应的Ring),Ring 使用Region(最近几个版本中新加入的).Zone.Device.Partition和Replica来维护这些信息,对于每一个对象,根据你在部署swift设置的Replica数量,集群中会存有Replica个对象.

SURF算法与源码分析、下

上一篇文章 SURF算法与源码分析.上 中主要分析的是SURF特征点定位的算法原理与相关OpenCV中的源码分析,这篇文章接着上篇文章对已经定位到的SURF特征点进行特征描述.这一步至关重要,这是SURF特征点匹配的基础.总体来说算法思路和SIFT相似,只是每一步都做了不同程度的近似与简化,提高了效率. 1. SURF特征点方向分配 为了保证特征矢量具有旋转不变性,与SIFT特征一样,需要对每个特征点分配一个主方向.为些,我们需要以特征点为中心,以$6s$($s = 1.2 *L /9$为特征点

Mahout源码分析:并行化FP-Growth算法

FP-Growth是一种常被用来进行关联分析,挖掘频繁项的算法.与Aprior算法相比,FP-Growth算法采用前缀树的形式来表征数据,减少了扫描事务数据库的次数,通过递归地生成条件FP-tree来挖掘频繁项.参考资料[1]详细分析了这一过程.事实上,面对大数据量时,FP-Growth算法生成的FP-tree非常大,无法放入内存,挖掘到的频繁项也可能有指数多个.本文将分析如何并行化FP-Growth算法以及Mahout中并行化FP-Growth算法的源码. 1. 并行化FP-Growth 并行

Mahout源码分析-K-means聚类算法

一 算法描述 1.随机选取k个对象作为初始簇中心: 2.计算每个对象到簇中心的距离,将每个对象聚类到离该对象最近的聚簇中去: 3.计算每个聚簇中的簇均值,并将簇均值作为新的簇中心: 4.计算准则函数: 5.重复(2).(3)和(4),直到准则函数不再发生变化. 二 源码分析 Mahout源码分析-K-means聚类算法

【E2LSH源码分析】LSH算法框架分析

位置敏感哈希(Locality Sensitive Hashing,LSH)是近似最近邻搜索算法中最流行的一种,它有坚实的理论依据并且在高维数据空间中表现优异.由于网络上相关知识的介绍比较单一,现就LSH的相关算法和技术做一介绍总结,希望能给感兴趣的朋友提供便利,也希望有兴趣的同道中人多交流.多指正. 1.LSH原理 最近邻问题(nearest neighbor problem)可以定义如下:给定n个对象的集合并建立一个数据结构,当给定任意的要查询对象时,该数据结构返回针对查询对象的最相似的数据