ClassifierSubsetEval.java
Upload User: rhdiban
Upload Date: 2013-08-09
Package Size: 15085k
Code Size: 19k
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.  *    ClassifierSubsetEval.java
  18.  *    Copyright (C) 2000 Mark Hall
  19.  *
  20.  */
  21. package weka.attributeSelection;
  22. import java.io.*;
  23. import java.util.*;
  24. import weka.core.*;
  25. import weka.classifiers.*;
  26. import weka.classifiers.rules.ZeroR;
  27. import weka.classifiers.Evaluation;
  28. import weka.filters.Filter;
  29. import weka.filters.unsupervised.attribute.Remove;
  30. /**
  31.  * Classifier subset evaluator. Uses a classifier to estimate the "merit"
  32.  * of a set of attributes.
  33.  *
  34.  * Valid options are:<p>
  35.  *
  36.  * -B <classifier> <br>
  37.  * Class name of the classifier to use for accuracy estimation.
  38.  * Place any classifier options last on the command line following a
  39.  * "--". Eg  -B weka.classifiers.bayes.NaiveBayes ... -- -K <p>
  40.  *
  41.  * -T <br>
  42.  * Use the training data for accuracy estimation rather than a hold out/
  43.  * test set. <p>
  44.  *
  45.  * -H <filename> <br>
  46.  * The file containing hold out/test instances to use for accuracy estimation
  47.  * <p>
  48.  *
  49.  * @author Mark Hall (mhall@cs.waikato.ac.nz)
  50.  * @version $Revision: 1.8 $
  51.  */
  52. public class ClassifierSubsetEval 
  53.   extends HoldOutSubsetEvaluator
  54.   implements OptionHandler, ErrorBasedMeritEvaluator {
  55.   /** training instances */
  56.   private Instances m_trainingInstances;
  57.   /** class index */
  58.   private int m_classIndex;
  59.   /** number of attributes in the training data */
  60.   private int m_numAttribs;
  61.   
  62.   /** number of training instances */
  63.   private int m_numInstances;
  64.   /** holds the classifier to use for error estimates */
  65.   private Classifier m_Classifier = new ZeroR();
  66.   /** holds the evaluation object to use for evaluating the classifier */
  67.   private Evaluation m_Evaluation;
  68.   /** the file that containts hold out/test instances */
  69.   private File m_holdOutFile = new File("Click to set hold out or "
  70. +"test instances");
  71.   /** the instances to test on */
  72.   private Instances m_holdOutInstances = null;
  73.   /** evaluate on training data rather than seperate hold out/test set */
  74.   private boolean m_useTraining = false;
  75.   /**
  76.    * Returns a string describing this attribute evaluator
  77.    * @return a description of the evaluator suitable for
  78.    * displaying in the explorer/experimenter gui
  79.    */
  80.   public String globalInfo() {
  81.     return "Evaluates attribute subsets on training data or a seperate "
  82.       +"hold out testing set";
  83.   }
  84.   /**
  85.    * Returns an enumeration describing the available options. <p>
  86.    *
  87.    * -B <classifier> <br>
  88.    * Class name of the classifier to use for accuracy estimation.
  89.    * Place any classifier options last on the command line following a
  90.    * "--". Eg  -B weka.classifiers.bayes.NaiveBayes ... -- -K <p>
  91.    *
  92.    * -T <br>
  93.    * Use the training data for accuracy estimation rather than a hold out/
  94.    * test set. <p>
  95.    *
  96.    * -H <filename> <br>
  97.    * The file containing hold out/test instances to use for accuracy estimation
  98.    * <p>
  99.    *
  100.    * @return an enumeration of all the available options.
  101.    **/
  102.   public Enumeration listOptions () {
  103.     Vector newVector = new Vector(3);
  104.     newVector.addElement(new Option("tclass name of the classifier to use for" 
  105.     + "ntaccuracy estimation. Place any" 
  106.     + "ntclassifier options LAST on the" 
  107.     + "ntcommand line following a "--"." 
  108.     + "nteg. -C weka.classifiers.bayes.NaiveBayes ... " 
  109.     + "-- -K", "B", 1, "-B <classifier>"));
  110.     
  111.     newVector.addElement(new Option("tUse the training data to estimate"
  112.     +" accuracy."
  113.     ,"T",0,"-T"));
  114.     
  115.     newVector.addElement(new Option("tName of the hold out/test set to "
  116.     +"ntestimate accuracy on."
  117.     ,"H", 1,"-H <filename>"));
  118.     if ((m_Classifier != null) && 
  119. (m_Classifier instanceof OptionHandler)) {
  120.       newVector.addElement(new Option("", "", 0, "nOptions specific to " 
  121.       + "scheme " 
  122.       + m_Classifier.getClass().getName() 
  123.       + ":"));
  124.       Enumeration enum = ((OptionHandler)m_Classifier).listOptions();
  125.       while (enum.hasMoreElements()) {
  126.         newVector.addElement(enum.nextElement());
  127.       }
  128.     }
  129.     return  newVector.elements();
  130.   }
  131.   /**
  132.    * Parses a given list of options.
  133.    *
  134.    * Valid options are:<p>
  135.    *
  136.    * -C <classifier> <br>
  137.    * Class name of classifier to use for accuracy estimation.
  138.    * Place any classifier options last on the command line following a
  139.    * "--". Eg  -B weka.classifiers.bayes.NaiveBayes ... -- -K <p>
  140.    *
  141.    * -T <br>
  142.    * Use training data instead of a hold out/test set for accuracy estimation.
  143.    * <p>
  144.    *
  145.    * -H <filname> <br>
  146.    * Name of the hold out/test set to estimate classifier accuracy on.
  147.    * <p>
  148.    *
  149.    * @param options the list of options as an array of strings
  150.    * @exception Exception if an option is not supported
  151.    *
  152.    **/
  153.   public void setOptions (String[] options)
  154.     throws Exception
  155.   {
  156.     String optionString;
  157.     resetOptions();
  158.     optionString = Utils.getOption('B', options);
  159.     
  160.     if (optionString.length() == 0) {
  161.       throw new Exception("A classifier must be specified with -B option");
  162.     }
  163.     setClassifier(Classifier.forName(optionString,
  164.      Utils.partitionOptions(options)));
  165.     optionString = Utils.getOption('H',options);
  166.     if (optionString.length() != 0) {
  167.       setHoldOutFile(new File(optionString));
  168.     }
  169.     setUseTraining(Utils.getFlag('T',options));
  170.   }
  171.     /**
  172.    * Returns the tip text for this property
  173.    * @return tip text for this property suitable for
  174.    * displaying in the explorer/experimenter gui
  175.    */
  176.   public String classifierTipText() {
  177.     return "Classifier to use for estimating the accuracy of subsets";
  178.   }
  179.   /**
  180.    * Set the classifier to use for accuracy estimation
  181.    *
  182.    * @param newClassifier the Classifier to use.
  183.    */
  184.   public void setClassifier (Classifier newClassifier) {
  185.     m_Classifier = newClassifier;
  186.   }
  187.   /**
  188.    * Get the classifier used as the base learner.
  189.    *
  190.    * @return the classifier used as the classifier
  191.    */
  192.   public Classifier getClassifier () {
  193.     return  m_Classifier;
  194.   }
  195.   /**
  196.    * Returns the tip text for this property
  197.    * @return tip text for this property suitable for
  198.    * displaying in the explorer/experimenter gui
  199.    */
  200.   public String holdOutFileTipText() {
  201.     return "File containing hold out/test instances.";
  202.   }
  203.   /**
  204.    * Gets the file that holds hold out/test instances.
  205.    * @return File that contains hold out instances
  206.    */
  207.   public File getHoldOutFile() {
  208.     return m_holdOutFile;
  209.   }
  210.   /**
  211.    * Set the file that contains hold out/test instances
  212.    * @param h the hold out file
  213.    */
  214.   public void setHoldOutFile(File h) {
  215.     m_holdOutFile = h;
  216.   }
  217.   /**
  218.    * Returns the tip text for this property
  219.    * @return tip text for this property suitable for
  220.    * displaying in the explorer/experimenter gui
  221.    */
  222.   public String useTrainingTipText() {
  223.     return "Use training data instead of hold out/test instances.";
  224.   }
  225.   /**
  226.    * Get if training data is to be used instead of hold out/test data
  227.    * @return true if training data is to be used instead of hold out data
  228.    */
  229.   public boolean getUseTraining() {
  230.     return m_useTraining;
  231.   }
  232.   /**
  233.    * Set if training data is to be used instead of hold out/test data
  234.    * @return true if training data is to be used instead of hold out data
  235.    */
  236.   public void setUseTraining(boolean t) {
  237.     m_useTraining = t;
  238.   }
  239.   /**
  240.    * Gets the current settings of ClassifierSubsetEval
  241.    *
  242.    * @return an array of strings suitable for passing to setOptions()
  243.    */
  244.   public String[] getOptions () {
  245.     String[] classifierOptions = new String[0];
  246.     if ((m_Classifier != null) && 
  247. (m_Classifier instanceof OptionHandler)) {
  248.       classifierOptions = ((OptionHandler)m_Classifier).getOptions();
  249.     }
  250.     String[] options = new String[6 + classifierOptions.length];
  251.     int current = 0;
  252.     if (getClassifier() != null) {
  253.       options[current++] = "-B";
  254.       options[current++] = getClassifier().getClass().getName();
  255.     }
  256.     if (getUseTraining()) {
  257.       options[current++] = "-T";
  258.     }
  259.     options[current++] = "-H"; options[current++] = getHoldOutFile().getPath();
  260.     options[current++] = "--";
  261.     System.arraycopy(classifierOptions, 0, options, current, 
  262.      classifierOptions.length);
  263.     current += classifierOptions.length;
  264.         while (current < options.length) {
  265.       options[current++] = "";
  266.     }
  267.     return  options;
  268.   }
  269.   /**
  270.    * Generates a attribute evaluator. Has to initialize all fields of the 
  271.    * evaluator that are not being set via options.
  272.    *
  273.    * @param data set of instances serving as training data 
  274.    * @exception Exception if the evaluator has not been 
  275.    * generated successfully
  276.    */
  277.   public void buildEvaluator (Instances data)
  278.     throws Exception
  279.   {
  280.     if (data.checkForStringAttributes()) {
  281.       throw  new UnsupportedAttributeTypeException("Can't handle string attributes!");
  282.     }
  283.     m_trainingInstances = data;
  284.     m_classIndex = m_trainingInstances.classIndex();
  285.     m_numAttribs = m_trainingInstances.numAttributes();
  286.     m_numInstances = m_trainingInstances.numInstances();
  287.     // load the testing data
  288.     if (!m_useTraining && 
  289. (!getHoldOutFile().getPath().startsWith("Click to set"))) {
  290.       java.io.Reader r = new java.io.BufferedReader(
  291.  new java.io.FileReader(getHoldOutFile().getPath()));
  292. m_holdOutInstances = new Instances(r);
  293. m_holdOutInstances.setClassIndex(m_trainingInstances.classIndex());
  294. if (m_trainingInstances.equalHeaders(m_holdOutInstances) == false) {
  295.   throw new Exception("Hold out/test set is not compatable with "
  296.       +"training data.");
  297. }
  298.     }
  299.   }
  300.   /**
  301.    * Evaluates a subset of attributes
  302.    *
  303.    * @param subset a bitset representing the attribute subset to be 
  304.    * evaluated 
  305.    * @exception Exception if the subset could not be evaluated
  306.    */
  307.   public double evaluateSubset (BitSet subset)
  308.     throws Exception
  309.   {
  310.     int i,j;
  311.     double errorRate = 0;
  312.     int numAttributes = 0;
  313.     Instances trainCopy=null;
  314.     Instances testCopy=null;
  315.     Remove delTransform = new Remove();
  316.     delTransform.setInvertSelection(true);
  317.     // copy the training instances
  318.     trainCopy = new Instances(m_trainingInstances);
  319.     
  320.     if (!m_useTraining) {
  321.       if (m_holdOutInstances == null) {
  322. throw new Exception("Must specify a set of hold out/test instances "
  323.     +"with -H");
  324.       } 
  325.       // copy the test instances
  326.       testCopy = new Instances(m_holdOutInstances);
  327.     }
  328.     
  329.     // count attributes set in the BitSet
  330.     for (i = 0; i < m_numAttribs; i++) {
  331.       if (subset.get(i)) {
  332.         numAttributes++;
  333.       }
  334.     }
  335.     
  336.     // set up an array of attribute indexes for the filter (+1 for the class)
  337.     int[] featArray = new int[numAttributes + 1];
  338.     
  339.     for (i = 0, j = 0; i < m_numAttribs; i++) {
  340.       if (subset.get(i)) {
  341.         featArray[j++] = i;
  342.       }
  343.     }
  344.     
  345.     featArray[j] = m_classIndex;
  346.     delTransform.setAttributeIndicesArray(featArray);
  347.     delTransform.setInputFormat(trainCopy);
  348.     trainCopy = Filter.useFilter(trainCopy, delTransform);
  349.     if (!m_useTraining) {
  350.       testCopy = Filter.useFilter(testCopy, delTransform);
  351.     }
  352.     // build the classifier
  353.     m_Classifier.buildClassifier(trainCopy);
  354.     m_Evaluation = new Evaluation(trainCopy);
  355.     if (!m_useTraining) {
  356.       m_Evaluation.evaluateModel(m_Classifier, testCopy);
  357.     } else {
  358.       m_Evaluation.evaluateModel(m_Classifier, trainCopy);
  359.     }
  360.     if (m_trainingInstances.classAttribute().isNominal()) {
  361.       errorRate = m_Evaluation.errorRate();
  362.     } else {
  363.       errorRate = m_Evaluation.meanAbsoluteError();
  364.     }
  365.     // return the negative of the error rate as search methods  need to
  366.     // maximize something
  367.     return -errorRate;
  368.   }
  369.   /**
  370.    * Evaluates a subset of attributes with respect to a set of instances.
  371.    * Calling this function overides any test/hold out instancs set from
  372.    * setHoldOutFile.
  373.    * @param subset a bitset representing the attribute subset to be
  374.    * evaluated
  375.    * @param holdOut a set of instances (possibly seperate and distinct
  376.    * from those use to build/train the evaluator) with which to
  377.    * evaluate the merit of the subset
  378.    * @return the "merit" of the subset on the holdOut data
  379.    * @exception Exception if the subset cannot be evaluated
  380.    */
  381.   public double evaluateSubset(BitSet subset, Instances holdOut) 
  382.     throws Exception {
  383.     int i,j;
  384.     double errorRate;
  385.     int numAttributes = 0;
  386.     Instances trainCopy=null;
  387.     Instances testCopy=null;
  388.     if (m_trainingInstances.equalHeaders(holdOut) == false) {
  389.       throw new Exception("evaluateSubset : Incompatable instance types.");
  390.     }
  391.     Remove delTransform = new Remove();
  392.     delTransform.setInvertSelection(true);
  393.     // copy the training instances
  394.     trainCopy = new Instances(m_trainingInstances);
  395.     
  396.     testCopy = new Instances(holdOut);
  397.     // count attributes set in the BitSet
  398.     for (i = 0; i < m_numAttribs; i++) {
  399.       if (subset.get(i)) {
  400.         numAttributes++;
  401.       }
  402.     }
  403.     
  404.     // set up an array of attribute indexes for the filter (+1 for the class)
  405.     int[] featArray = new int[numAttributes + 1];
  406.     
  407.     for (i = 0, j = 0; i < m_numAttribs; i++) {
  408.       if (subset.get(i)) {
  409.         featArray[j++] = i;
  410.       }
  411.     }
  412.     
  413.     featArray[j] = m_classIndex;
  414.     delTransform.setAttributeIndicesArray(featArray);
  415.     delTransform.setInputFormat(trainCopy);
  416.     trainCopy = Filter.useFilter(trainCopy, delTransform);
  417.     testCopy = Filter.useFilter(testCopy, delTransform);
  418.     // build the classifier
  419.     m_Classifier.buildClassifier(trainCopy);
  420.     m_Evaluation = new Evaluation(trainCopy);
  421.     m_Evaluation.evaluateModel(m_Classifier, testCopy);
  422.     if (m_trainingInstances.classAttribute().isNominal()) {
  423.       errorRate = m_Evaluation.errorRate();
  424.     } else {
  425.       errorRate = m_Evaluation.meanAbsoluteError();
  426.     }
  427.     // return the negative of the error as search methods need to
  428.     // maximize something
  429.    return -errorRate;
  430.   }
  431.   /**
  432.    * Evaluates a subset of attributes with respect to a single instance.
  433.    * Calling this function overides any hold out/test instances set
  434.    * through setHoldOutFile.
  435.    * @param subset a bitset representing the attribute subset to be
  436.    * evaluated
  437.    * @param holdOut a single instance (possibly not one of those used to
  438.    * build/train the evaluator) with which to evaluate the merit of the subset
  439.    * @param retrain true if the classifier should be retrained with respect
  440.    * to the new subset before testing on the holdOut instance.
  441.    * @return the "merit" of the subset on the holdOut instance
  442.    * @exception Exception if the subset cannot be evaluated
  443.    */
  444.   public double evaluateSubset(BitSet subset, Instance holdOut,
  445.        boolean retrain) 
  446.     throws Exception {
  447.     int i,j;
  448.     double error;
  449.     int numAttributes = 0;
  450.     Instances trainCopy=null;
  451.     Instance testCopy=null;
  452.     if (m_trainingInstances.equalHeaders(holdOut.dataset()) == false) {
  453.       throw new Exception("evaluateSubset : Incompatable instance types.");
  454.     }
  455.     Remove delTransform = new Remove();
  456.     delTransform.setInvertSelection(true);
  457.     // copy the training instances
  458.     trainCopy = new Instances(m_trainingInstances);
  459.     
  460.     testCopy = new Instance(holdOut);
  461.     // count attributes set in the BitSet
  462.     for (i = 0; i < m_numAttribs; i++) {
  463.       if (subset.get(i)) {
  464.         numAttributes++;
  465.       }
  466.     }
  467.     
  468.     // set up an array of attribute indexes for the filter (+1 for the class)
  469.     int[] featArray = new int[numAttributes + 1];
  470.     
  471.     for (i = 0, j = 0; i < m_numAttribs; i++) {
  472.       if (subset.get(i)) {
  473.         featArray[j++] = i;
  474.       }
  475.     }
  476.     featArray[j] = m_classIndex;
  477.     delTransform.setAttributeIndicesArray(featArray);
  478.     delTransform.setInputFormat(trainCopy);
  479.     if (retrain) {
  480.       trainCopy = Filter.useFilter(trainCopy, delTransform);
  481.       // build the classifier
  482.       m_Classifier.buildClassifier(trainCopy);
  483.     }
  484.     delTransform.input(testCopy);
  485.     testCopy = delTransform.output();
  486.     double pred;
  487.     if (m_Classifier instanceof DistributionClassifier) {
  488.       double [] distrib;
  489.       distrib = ((DistributionClassifier)m_Classifier).
  490. distributionForInstance(testCopy);
  491.       if (m_trainingInstances.classAttribute().isNominal()) {
  492. pred = distrib[(int)testCopy.classValue()];
  493.       } else {
  494. pred = distrib[0];
  495.       }
  496.     } else {
  497.       pred = m_Classifier.classifyInstance(testCopy);
  498.       if (m_trainingInstances.classAttribute().isNominal()) {
  499. pred = (pred == testCopy.classValue()) ? 1.0 : 0.0;
  500.       }
  501.       
  502.     }
  503.     if (m_trainingInstances.classAttribute().isNominal()) {
  504.       error = 1.0 - pred;
  505.     } else {
  506.       error = testCopy.classValue() - pred;
  507.     }
  508.     // return the negative of the error as search methods need to
  509.     // maximize something
  510.     return -error;
  511.   }
  512.   /**
  513.    * Returns a string describing classifierSubsetEval
  514.    *
  515.    * @return the description as a string
  516.    */
  517.   public String toString() {
  518.     StringBuffer text = new StringBuffer();
  519.     
  520.     if (m_trainingInstances == null) {
  521.       text.append("tClassifier subset evaluator has not been built yetn");
  522.     }
  523.     else {
  524.       text.append("tClassifier Subset Evaluatorn");
  525.       text.append("tLearning scheme: " 
  526.   + getClassifier().getClass().getName() + "n");
  527.       text.append("tScheme options: ");
  528.       String[] classifierOptions = new String[0];
  529.       if (m_Classifier instanceof OptionHandler) {
  530.         classifierOptions = ((OptionHandler)m_Classifier).getOptions();
  531.         for (int i = 0; i < classifierOptions.length; i++) {
  532.           text.append(classifierOptions[i] + " ");
  533.         }
  534.       }
  535.       text.append("n");
  536.       text.append("tHold out/test set: ");
  537.       if (!m_useTraining) {
  538. if (getHoldOutFile().getPath().startsWith("Click to set")) {
  539.   text.append("nonen");
  540. } else {
  541.   text.append(getHoldOutFile().getPath()+'n');
  542. }
  543.       } else {
  544. text.append("Training datan");
  545.       }
  546.       if (m_trainingInstances.attribute(m_classIndex).isNumeric()) {
  547. text.append("tAccuracy estimation: MAEn");
  548.       } else {
  549. text.append("tAccuracy estimation: classification errorn");
  550.       }
  551.     }
  552.     return text.toString();
  553.   }
  554.   
  555.   /**
  556.    * reset to defaults
  557.    */
  558.   protected void resetOptions () {
  559.     m_trainingInstances = null;
  560.     m_Evaluation = null;
  561.     m_Classifier = new ZeroR();
  562.     m_holdOutFile = new File("Click to set hold out or test instances");
  563.     m_holdOutInstances = null;
  564.     m_useTraining = false;
  565.   }
  566.   
  567.   /**
  568.    * Main method for testing this class.
  569.    *
  570.    * @param args the options
  571.    */
  572.   public static void main (String[] args) {
  573.     try {
  574.       System.out.println(AttributeSelection.
  575.  SelectAttributes(new ClassifierSubsetEval(), args));
  576.     }
  577.     catch (Exception e) {
  578.       e.printStackTrace();
  579.       System.out.println(e.getMessage());
  580.     }
  581.   }
  582. }