AdaBoostM1.java
Upload User: rhdiban
Upload Date: 2013-08-09
Package Size: 15085k
Code Size: 22k
Category:

Windows Develop

Development Platform:

Java

  1. /*
  2.  *    This program is free software; you can redistribute it and/or modify
  3.  *    it under the terms of the GNU General Public License as published by
  4.  *    the Free Software Foundation; either version 2 of the License, or
  5.  *    (at your option) any later version.
  6.  *
  7.  *    This program is distributed in the hope that it will be useful,
  8.  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  9.  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  10.  *    GNU General Public License for more details.
  11.  *
  12.  *    You should have received a copy of the GNU General Public License
  13.  *    along with this program; if not, write to the Free Software
  14.  *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15.  */
  16. /*
  17.  *    AdaBoostM1.java
  18.  *    Copyright (C) 1999 Eibe Frank,Len Trigg
  19.  *
  20.  */
  21. package weka.classifiers.meta;
  22. import weka.classifiers.Classifier;
  23. import weka.classifiers.DistributionClassifier;
  24. import weka.classifiers.Evaluation;
  25. import weka.classifiers.Sourcable;
  26. import weka.classifiers.rules.ZeroR;
  27. import java.io.*;
  28. import java.util.*;
  29. import weka.core.*;
  30. /**
  31.  * Class for boosting a classifier using Freund & Schapire's Adaboost 
  32.  * M1 method. For more information, see<p>
  33.  *
  34.  * Yoav Freund and Robert E. Schapire
  35.  * (1996). <i>Experiments with a new boosting algorithm</i>.  Proc
  36.  * International Conference on Machine Learning, pages 148-156, Morgan
  37.  * Kaufmann, San Francisco.<p>
  38.  *
  39.  * Valid options are:<p>
  40.  *
  41.  * -D <br>
  42.  * Turn on debugging output.<p>
  43.  *
  44.  * -W classname <br>
  45.  * Specify the full class name of a classifier as the basis for 
  46.  * boosting (required).<p>
  47.  *
  48.  * -I num <br>
  49.  * Set the number of boost iterations (default 10). <p>
  50.  *
  51.  * -P num <br>
  52.  * Set the percentage of weight mass used to build classifiers
  53.  * (default 100). <p>
  54.  *
  55.  * -Q <br>
  56.  * Use resampling instead of reweighting.<p>
  57.  *
  58.  * -S seed <br>
  59.  * Random number seed for resampling (default 1). <p>
  60.  *
  61.  * Options after -- are passed to the designated classifier.<p>
  62.  *
  63.  * @author Eibe Frank (eibe@cs.waikato.ac.nz)
  64.  * @author Len Trigg (trigg@cs.waikato.ac.nz)
  65.  * @version $Revision: 1.15 $ 
  66.  */
  67. public class AdaBoostM1 extends DistributionClassifier 
  68.   implements OptionHandler, WeightedInstancesHandler, Sourcable {
  69.   /** Max num iterations tried to find classifier with non-zero error. */ 
  70.   private static int MAX_NUM_RESAMPLING_ITERATIONS = 10;
  71.   /** The model base classifier to use */
  72.   protected Classifier m_Classifier = new weka.classifiers.rules.ZeroR();
  73.   
  74.   /** Array for storing the generated base classifiers. */
  75.   protected Classifier [] m_Classifiers;
  76.   
  77.   /** Array for storing the weights for the votes. */
  78.   protected double [] m_Betas;
  79.   /** The maximum number of boost iterations */
  80.   protected int m_MaxIterations = 10;
  81.   /** The number of successfully generated base classifiers. */
  82.   protected int m_NumIterations;
  83.   /** Weight Threshold. The percentage of weight mass used in training */
  84.   protected int m_WeightThreshold = 100;
  85.   /** Debugging mode, gives extra output if true */
  86.   protected boolean m_Debug;
  87.   /** Use boosting with reweighting? */
  88.   protected boolean m_UseResampling;
  89.   /** Seed for boosting with resampling. */
  90.   protected int m_Seed = 1;
  91.   /** The number of classes */
  92.   protected int m_NumClasses;
  93.   /**
  94.    * Select only instances with weights that contribute to 
  95.    * the specified quantile of the weight distribution
  96.    *
  97.    * @param data the input instances
  98.    * @param quantile the specified quantile eg 0.9 to select 
  99.    * 90% of the weight mass
  100.    * @return the selected instances
  101.    */
  102.   protected Instances selectWeightQuantile(Instances data, double quantile) { 
  103.     int numInstances = data.numInstances();
  104.     Instances trainData = new Instances(data, numInstances);
  105.     double [] weights = new double [numInstances];
  106.     double sumOfWeights = 0;
  107.     for(int i = 0; i < numInstances; i++) {
  108.       weights[i] = data.instance(i).weight();
  109.       sumOfWeights += weights[i];
  110.     }
  111.     double weightMassToSelect = sumOfWeights * quantile;
  112.     int [] sortedIndices = Utils.sort(weights);
  113.     // Select the instances
  114.     sumOfWeights = 0;
  115.     for(int i = numInstances - 1; i >= 0; i--) {
  116.       Instance instance = (Instance)data.instance(sortedIndices[i]).copy();
  117.       trainData.add(instance);
  118.       sumOfWeights += weights[sortedIndices[i]];
  119.       if ((sumOfWeights > weightMassToSelect) && 
  120.   (i > 0) && 
  121.   (weights[sortedIndices[i]] != weights[sortedIndices[i - 1]])) {
  122. break;
  123.       }
  124.     }
  125.     if (m_Debug) {
  126.       System.err.println("Selected " + trainData.numInstances()
  127.  + " out of " + numInstances);
  128.     }
  129.     return trainData;
  130.   }
  131.   /**
  132.    * Returns an enumeration describing the available options.
  133.    *
  134.    * @return an enumeration of all the available options.
  135.    */
  136.   public Enumeration listOptions() {
  137.     Vector newVector = new Vector(6);
  138.     newVector.addElement(new Option(
  139.       "tTurn on debugging output.",
  140.       "D", 0, "-D"));
  141.     newVector.addElement(new Option(
  142.       "tMaximum number of boost iterations.n"
  143.       +"t(default 10)",
  144.       "I", 1, "-I <num>"));
  145.     newVector.addElement(new Option(
  146.       "tPercentage of weight mass to base training on.n"
  147.       +"t(default 100, reduce to around 90 speed up)",
  148.       "P", 1, "-P <num>"));
  149.     newVector.addElement(new Option(
  150.       "tFull name of classifier to boost.n"
  151.       +"teg: weka.classifiers.bayes.NaiveBayes",
  152.       "W", 1, "-W <class name>"));
  153.     newVector.addElement(new Option(
  154.       "tUse resampling for boosting.",
  155.       "Q", 0, "-Q"));
  156.     newVector.addElement(new Option(
  157.       "tSeed for resampling. (Default 1)",
  158.       "S", 1, "-S <num>"));
  159.     
  160.     if ((m_Classifier != null) &&
  161. (m_Classifier instanceof OptionHandler)) {
  162.       newVector.addElement(new Option(
  163.      "",
  164.      "", 0, "nOptions specific to classifier "
  165.      + m_Classifier.getClass().getName() + ":"));
  166.       Enumeration enum = ((OptionHandler)m_Classifier).listOptions();
  167.       while (enum.hasMoreElements()) {
  168. newVector.addElement(enum.nextElement());
  169.       }
  170.     }
  171.     return newVector.elements();
  172.   }
  173.   /**
  174.    * Parses a given list of options. Valid options are:<p>
  175.    *
  176.    * -D <br>
  177.    * Turn on debugging output.<p>
  178.    *
  179.    * -W classname <br>
  180.    * Specify the full class name of a classifier as the basis for 
  181.    * boosting (required).<p>
  182.    *
  183.    * -I num <br>
  184.    * Set the number of boost iterations (default 10). <p>
  185.    *
  186.    * -P num <br>
  187.    * Set the percentage of weight mass used to build classifiers
  188.    * (default 100). <p>
  189.    *
  190.    * -Q <br>
  191.    * Use resampling instead of reweighting.<p>
  192.    *
  193.    * -S seed <br>
  194.    * Random number seed for resampling (default 1).<p>
  195.    *
  196.    * Options after -- are passed to the designated classifier.<p>
  197.    *
  198.    * @param options the list of options as an array of strings
  199.    * @exception Exception if an option is not supported
  200.    */
  201.   public void setOptions(String[] options) throws Exception {
  202.     
  203.     setDebug(Utils.getFlag('D', options));
  204.     
  205.     String boostIterations = Utils.getOption('I', options);
  206.     if (boostIterations.length() != 0) {
  207.       setMaxIterations(Integer.parseInt(boostIterations));
  208.     } else {
  209.       setMaxIterations(10);
  210.     }
  211.     String thresholdString = Utils.getOption('P', options);
  212.     if (thresholdString.length() != 0) {
  213.       setWeightThreshold(Integer.parseInt(thresholdString));
  214.     } else {
  215.       setWeightThreshold(100);
  216.     }
  217.       
  218.     setUseResampling(Utils.getFlag('Q', options));
  219.     if (m_UseResampling && (thresholdString.length() != 0)) {
  220.       throw new Exception("Weight pruning with resampling"+
  221.   "not allowed.");
  222.     }
  223.     String seedString = Utils.getOption('S', options);
  224.     if (seedString.length() != 0) {
  225.       setSeed(Integer.parseInt(seedString));
  226.     } else {
  227.       setSeed(1);
  228.     }
  229.     String classifierName = Utils.getOption('W', options);
  230.     if (classifierName.length() == 0) {
  231.       throw new Exception("A classifier must be specified with"
  232.   + " the -W option.");
  233.     }
  234.     setClassifier(Classifier.forName(classifierName,
  235.      Utils.partitionOptions(options)));
  236.   }
  237.   /**
  238.    * Gets the current settings of the Classifier.
  239.    *
  240.    * @return an array of strings suitable for passing to setOptions
  241.    */
  242.   public String [] getOptions() {
  243.     String [] classifierOptions = new String [0];
  244.     if ((m_Classifier != null) && 
  245. (m_Classifier instanceof OptionHandler)) {
  246.       classifierOptions = ((OptionHandler)m_Classifier).getOptions();
  247.     }
  248.     String [] options = new String [classifierOptions.length + 10];
  249.     int current = 0;
  250.     if (getDebug()) {
  251.       options[current++] = "-D";
  252.     }
  253.     if (getUseResampling()) {
  254.       options[current++] = "-Q";
  255.     } else {
  256.       options[current++] = "-P"; 
  257.       options[current++] = "" + getWeightThreshold();
  258.     }
  259.     options[current++] = "-I"; options[current++] = "" + getMaxIterations();
  260.     options[current++] = "-S"; options[current++] = "" + getSeed();
  261.     if (getClassifier() != null) {
  262.       options[current++] = "-W";
  263.       options[current++] = getClassifier().getClass().getName();
  264.     }
  265.     options[current++] = "--";
  266.     System.arraycopy(classifierOptions, 0, options, current, 
  267.      classifierOptions.length);
  268.     current += classifierOptions.length;
  269.     while (current < options.length) {
  270.       options[current++] = "";
  271.     }
  272.     return options;
  273.   }
  274.   /**
  275.    * Set the classifier for boosting. 
  276.    *
  277.    * @param newClassifier the Classifier to use.
  278.    */
  279.   public void setClassifier(Classifier newClassifier) {
  280.     m_Classifier = newClassifier;
  281.   }
  282.   /**
  283.    * Get the classifier used as the classifier
  284.    *
  285.    * @return the classifier used as the classifier
  286.    */
  287.   public Classifier getClassifier() {
  288.     return m_Classifier;
  289.   }
  290.   /**
  291.    * Set the maximum number of boost iterations
  292.    */
  293.   public void setMaxIterations(int maxIterations) {
  294.     m_MaxIterations = maxIterations;
  295.   }
  296.   /**
  297.    * Get the maximum number of boost iterations
  298.    *
  299.    * @return the maximum number of boost iterations
  300.    */
  301.   public int getMaxIterations() {
  302.     return m_MaxIterations;
  303.   }
  304.   /**
  305.    * Set weight threshold
  306.    *
  307.    * @param thresholding the percentage of weight mass used for training
  308.    */
  309.   public void setWeightThreshold(int threshold) {
  310.     m_WeightThreshold = threshold;
  311.   }
  312.   /**
  313.    * Get the degree of weight thresholding
  314.    *
  315.    * @return the percentage of weight mass used for training
  316.    */
  317.   public int getWeightThreshold() {
  318.     return m_WeightThreshold;
  319.   }
  320.   /**
  321.    * Set seed for resampling.
  322.    *
  323.    * @param seed the seed for resampling
  324.    */
  325.   public void setSeed(int seed) {
  326.     m_Seed = seed;
  327.   }
  328.   /**
  329.    * Get seed for resampling.
  330.    *
  331.    * @return the seed for resampling
  332.    */
  333.   public int getSeed() {
  334.     return m_Seed;
  335.   }
  336.   /**
  337.    * Set debugging mode
  338.    *
  339.    * @param debug true if debug output should be printed
  340.    */
  341.   public void setDebug(boolean debug) {
  342.     m_Debug = debug;
  343.   }
  344.   /**
  345.    * Get whether debugging is turned on
  346.    *
  347.    * @return true if debugging output is on
  348.    */
  349.   public boolean getDebug() {
  350.     return m_Debug;
  351.   }
  352.   /**
  353.    * Set resampling mode
  354.    *
  355.    * @param resampling true if resampling should be done
  356.    */
  357.   public void setUseResampling(boolean r) {
  358.     m_UseResampling = r;
  359.   }
  360.   /**
  361.    * Get whether resampling is turned on
  362.    *
  363.    * @return true if resampling output is on
  364.    */
  365.   public boolean getUseResampling() {
  366.     return m_UseResampling;
  367.   }
  368.   /**
  369.    * Boosting method.
  370.    *
  371.    * @param data the training data to be used for generating the
  372.    * boosted classifier.
  373.    * @exception Exception if the classifier could not be built successfully
  374.    */
  375.   public void buildClassifier(Instances data) throws Exception {
  376.     if (data.checkForStringAttributes()) {
  377.       throw new UnsupportedAttributeTypeException("Cannot handle string attributes!");
  378.     }
  379.     data = new Instances(data);
  380.     data.deleteWithMissingClass();
  381.     if (data.numInstances() == 0) {
  382.       throw new Exception("No train instances without class missing!");
  383.     }
  384.     if (data.classAttribute().isNumeric()) {
  385.       throw new UnsupportedClassTypeException("AdaBoostM1 can't handle a numeric class!");
  386.     }
  387.     if (m_Classifier == null) {
  388.       throw new Exception("A base classifier has not been specified!");
  389.     }
  390.     m_NumClasses = data.numClasses();
  391.     m_Classifiers = Classifier.makeCopies(m_Classifier, getMaxIterations());
  392.     if ((!m_UseResampling) && 
  393. (m_Classifier instanceof WeightedInstancesHandler)) {
  394.       buildClassifierWithWeights(data);
  395.     } else {
  396.       buildClassifierUsingResampling(data);
  397.     }
  398.   }
  399.   /**
  400.    * Boosting method. Boosts using resampling
  401.    *
  402.    * @param data the training data to be used for generating the
  403.    * boosted classifier.
  404.    * @exception Exception if the classifier could not be built successfully
  405.    */
  406.   protected void buildClassifierUsingResampling(Instances data) 
  407.     throws Exception {
  408.     Instances trainData, sample, training;
  409.     double epsilon, reweight, beta = 0, sumProbs;
  410.     double oldSumOfWeights, newSumOfWeights;
  411.     Evaluation evaluation;
  412.     int numInstances = data.numInstances();
  413.     Random randomInstance = new Random(m_Seed);
  414.     double[] probabilities;
  415.     int resamplingIterations = 0;
  416.     int k, l;
  417.     // Initialize data
  418.     m_Betas = new double [m_Classifiers.length];
  419.     m_NumIterations = 0;
  420.     // Create a copy of the data so that when the weights are diddled
  421.     // with it doesn't mess up the weights for anyone else
  422.     training = new Instances(data, 0, numInstances);
  423.     sumProbs = training.sumOfWeights();
  424.     for (int i = 0; i < training.numInstances(); i++) {
  425.       training.instance(i).setWeight(training.instance(i).
  426.       weight() / sumProbs);
  427.     }
  428.     
  429.     // Do boostrap iterations
  430.     for (m_NumIterations = 0; m_NumIterations < m_Classifiers.length; 
  431.  m_NumIterations++) {
  432.       if (m_Debug) {
  433. System.err.println("Training classifier " + (m_NumIterations + 1));
  434.       }
  435.       // Select instances to train the classifier on
  436.       if (m_WeightThreshold < 100) {
  437. trainData = selectWeightQuantile(training, 
  438.  (double)m_WeightThreshold / 100);
  439.       } else {
  440. trainData = new Instances(training);
  441.       }
  442.       
  443.       // Resample
  444.       resamplingIterations = 0;
  445.       double[] weights = new double[trainData.numInstances()];
  446.       for (int i = 0; i < weights.length; i++) {
  447. weights[i] = trainData.instance(i).weight();
  448.       }
  449.       do {
  450. sample = trainData.resampleWithWeights(randomInstance, weights);
  451. // Build and evaluate classifier
  452. m_Classifiers[m_NumIterations].buildClassifier(sample);
  453. evaluation = new Evaluation(data);
  454. evaluation.evaluateModel(m_Classifiers[m_NumIterations], 
  455.  training);
  456. epsilon = evaluation.errorRate();
  457. resamplingIterations++;
  458.       } while (Utils.eq(epsilon, 0) && 
  459.       (resamplingIterations < MAX_NUM_RESAMPLING_ITERATIONS));
  460.       
  461.       // Stop if error too big or 0
  462.       if (Utils.grOrEq(epsilon, 0.5) || Utils.eq(epsilon, 0)) {
  463. if (m_NumIterations == 0) {
  464.   m_NumIterations = 1; // If we're the first we have to to use it
  465. }
  466. break;
  467.       }
  468.       
  469.       // Determine the weight to assign to this model
  470.       m_Betas[m_NumIterations] = beta = Math.log((1 - epsilon) / epsilon);
  471.       reweight = (1 - epsilon) / epsilon;
  472.       if (m_Debug) {
  473. System.err.println("terror rate = " + epsilon
  474.    +"  beta = " + m_Betas[m_NumIterations]);
  475.       }
  476.  
  477.       // Update instance weights
  478.       oldSumOfWeights = training.sumOfWeights();
  479.       Enumeration enum = training.enumerateInstances();
  480.       while (enum.hasMoreElements()) {
  481. Instance instance = (Instance) enum.nextElement();
  482. if (!Utils.eq(m_Classifiers[m_NumIterations].classifyInstance(instance), 
  483.      instance.classValue()))
  484.   instance.setWeight(instance.weight() * reweight);
  485.       }
  486.       // Renormalize weights
  487.       newSumOfWeights = training.sumOfWeights();
  488.       enum = training.enumerateInstances();
  489.       while (enum.hasMoreElements()) {
  490. Instance instance = (Instance) enum.nextElement();
  491. instance.setWeight(instance.weight() * oldSumOfWeights 
  492.    / newSumOfWeights);
  493.       }
  494.     }
  495.   }
  496.   /**
  497.    * Boosting method. Boosts any classifier that can handle weighted
  498.    * instances.
  499.    *
  500.    * @param data the training data to be used for generating the
  501.    * boosted classifier.
  502.    * @exception Exception if the classifier could not be built successfully
  503.    */
  504.   protected void buildClassifierWithWeights(Instances data) 
  505.     throws Exception {
  506.     Instances trainData, training;
  507.     double epsilon, reweight, beta = 0;
  508.     double oldSumOfWeights, newSumOfWeights;
  509.     Evaluation evaluation;
  510.     int numInstances = data.numInstances();
  511.     // Initialize data
  512.     m_Betas = new double [m_Classifiers.length];
  513.     m_NumIterations = 0;
  514.     // Create a copy of the data so that when the weights are diddled
  515.     // with it doesn't mess up the weights for anyone else
  516.     training = new Instances(data, 0, numInstances);
  517.     
  518.     // Do boostrap iterations
  519.     for (m_NumIterations = 0; m_NumIterations < m_Classifiers.length; 
  520.  m_NumIterations++) {
  521.       if (m_Debug) {
  522. System.err.println("Training classifier " + (m_NumIterations + 1));
  523.       }
  524.       // Select instances to train the classifier on
  525.       if (m_WeightThreshold < 100) {
  526. trainData = selectWeightQuantile(training, 
  527.  (double)m_WeightThreshold / 100);
  528.       } else {
  529. trainData = new Instances(training, 0, numInstances);
  530.       }
  531.       // Build the classifier
  532.       m_Classifiers[m_NumIterations].buildClassifier(trainData);
  533.       // Evaluate the classifier
  534.       evaluation = new Evaluation(data);
  535.       evaluation.evaluateModel(m_Classifiers[m_NumIterations], training);
  536.       epsilon = evaluation.errorRate();
  537.       // Stop if error too small or error too big and ignore this model
  538.       if (Utils.grOrEq(epsilon, 0.5) || Utils.eq(epsilon, 0)) {
  539. if (m_NumIterations == 0) {
  540.   m_NumIterations = 1; // If we're the first we have to to use it
  541. }
  542. break;
  543.       }
  544.       // Determine the weight to assign to this model
  545.       m_Betas[m_NumIterations] = beta = Math.log((1 - epsilon) / epsilon);
  546.       reweight = (1 - epsilon) / epsilon;
  547.       if (m_Debug) {
  548. System.err.println("terror rate = " + epsilon
  549.    +"  beta = " + m_Betas[m_NumIterations]);
  550.       }
  551.  
  552.       // Update instance weights
  553.       oldSumOfWeights = training.sumOfWeights();
  554.       Enumeration enum = training.enumerateInstances();
  555.       while (enum.hasMoreElements()) {
  556. Instance instance = (Instance) enum.nextElement();
  557. if (!Utils.eq(m_Classifiers[m_NumIterations]
  558.       .classifyInstance(instance), 
  559.       instance.classValue()))
  560.   instance.setWeight(instance.weight() * reweight);
  561.       }
  562.       // Renormalize weights
  563.       newSumOfWeights = training.sumOfWeights();
  564.       enum = training.enumerateInstances();
  565.       while (enum.hasMoreElements()) {
  566. Instance instance = (Instance) enum.nextElement();
  567. instance.setWeight(instance.weight() * oldSumOfWeights
  568.    / newSumOfWeights);
  569.       }
  570.     }
  571.   }
  572.   
  573.   /**
  574.    * Calculates the class membership probabilities for the given test instance.
  575.    *
  576.    * @param instance the instance to be classified
  577.    * @return predicted class probability distribution
  578.    * @exception Exception if instance could not be classified
  579.    * successfully
  580.    */
  581.   public double [] distributionForInstance(Instance instance) 
  582.     throws Exception {
  583.       
  584.     if (m_NumIterations == 0) {
  585.       throw new Exception("No model built");
  586.     }
  587.     double [] sums = new double [instance.numClasses()]; 
  588.     
  589.     if (m_NumIterations == 1) {
  590.       if (m_Classifiers[0] instanceof DistributionClassifier) {
  591. return ((DistributionClassifier)m_Classifiers[0]).
  592. distributionForInstance(instance);
  593.       } else {
  594. sums[(int)m_Classifiers[0].classifyInstance(instance)] ++;
  595.       }
  596.     } else {
  597.       for (int i = 0; i < m_NumIterations; i++) {
  598. sums[(int)m_Classifiers[i].classifyInstance(instance)] += 
  599. m_Betas[i];
  600.       }
  601.     }
  602.     Utils.normalize(sums);
  603.     return sums;
  604.   }
  605.   /**
  606.    * Returns the boosted model as Java source code.
  607.    *
  608.    * @return the tree as Java source code
  609.    * @exception Exception if something goes wrong
  610.    */
  611.   public String toSource(String className) throws Exception {
  612.     if (m_NumIterations == 0) {
  613.       throw new Exception("No model built yet");
  614.     }
  615.     if (!(m_Classifiers[0] instanceof Sourcable)) {
  616.       throw new Exception("Base learner " + m_Classifier.getClass().getName()
  617.   + " is not Sourcable");
  618.     }
  619.     StringBuffer text = new StringBuffer("class ");
  620.     text.append(className).append(" {nn");
  621.     text.append("  public static double classify(Object [] i) {n");
  622.     if (m_NumIterations == 1) {
  623.       text.append("    return " + className + "_0.classify(i);n");
  624.     } else {
  625.       text.append("    double [] sums = new double [" + m_NumClasses + "];n");
  626.       for (int i = 0; i < m_NumIterations; i++) {
  627. text.append("    sums[(int) " + className + '_' + i 
  628.     + ".classify(i)] += " + m_Betas[i] + ";n");
  629.       }
  630.       text.append("    double maxV = sums[0];n" +
  631.   "    int maxI = 0;n"+
  632.   "    for (int j = 1; j < " + m_NumClasses + "; j++) {n"+
  633.   "      if (sums[j] > maxV) { maxV = sums[j]; maxI = j; }n"+
  634.   "    }n    return (double) maxI;n");
  635.     }
  636.     text.append("  }n}n");
  637.     for (int i = 0; i < m_Classifiers.length; i++) {
  638. text.append(((Sourcable)m_Classifiers[i])
  639.     .toSource(className + '_' + i));
  640.     }
  641.     return text.toString();
  642.   }
  643.   /**
  644.    * Returns description of the boosted classifier.
  645.    *
  646.    * @return description of the boosted classifier as a string
  647.    */
  648.   public String toString() {
  649.     
  650.     StringBuffer text = new StringBuffer();
  651.     
  652.     if (m_NumIterations == 0) {
  653.       text.append("AdaBoostM1: No model built yet.n");
  654.     } else if (m_NumIterations == 1) {
  655.       text.append("AdaBoostM1: No boosting possible, one classifier used!n");
  656.       text.append(m_Classifiers[0].toString() + "n");
  657.     } else {
  658.       text.append("AdaBoostM1: Base classifiers and their weights: nn");
  659.       for (int i = 0; i < m_NumIterations ; i++) {
  660. text.append(m_Classifiers[i].toString() + "nn");
  661. text.append("Weight: " + Utils.roundDouble(m_Betas[i], 2) + "nn");
  662.       }
  663.       text.append("Number of performed Iterations: " 
  664.   + m_NumIterations + "n");
  665.     }
  666.     
  667.     return text.toString();
  668.   }
  669.   /**
  670.    * Main method for testing this class.
  671.    *
  672.    * @param argv the options
  673.    */
  674.   public static void main(String [] argv) {
  675.     try {
  676.       System.out.println(Evaluation.evaluateModel(new AdaBoostM1(), argv));
  677.     } catch (Exception e) {
  678.       System.err.println(e.getMessage());
  679.     }
  680.   }
  681. }
  682.