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