KStar.java
Upload User: rhdiban
Upload Date: 2013-08-09
Package Size: 15085k
Code Size: 17k
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.  *    KS.java
  18.  *    Copyright (c) 1995-97 by Len Trigg (trigg@cs.waikato.ac.nz).
  19.  *    Java port to Weka by Abdelaziz Mahoui (am14@cs.waikato.ac.nz).
  20.  *
  21.  */
  22. package weka.classifiers.lazy.kstar;
  23. import weka.classifiers.lazy.IB1;
  24. import java.io.*;
  25. import java.util.*;
  26. import weka.core.*;
  27. import weka.classifiers.*;
  28. //import java.text.NumberFormat;
  29. /**
  30.  * K* is an instance-based classifier, that is the class of a test
  31.  * instance is based upon the class of those training instances
  32.  * similar to it, as determined by some similarity function.  The
  33.  * underlying assumption of instance-based classifiers such as K*,
  34.  * IB1, PEBLS, etc, is that similar instances will have similar
  35.  * classes.
  36.  *
  37.  * For more information on K*, see <p>
  38.  * 
  39.  * John, G. Cleary and Leonard, E. Trigg (1995) "K*: An Instance-
  40.  * based Learner Using an Entropic Distance Measure",
  41.  * <i>Proceedings of the 12th International Conference on Machine
  42.  * learning</i>, pp. 108-114.<p>
  43.  *
  44.  * @author Len Trigg (len@intelligenesis.net)
  45.  * @author Abdelaziz Mahoui (am14@cs.waikato.ac.nz)
  46.  * @version $Revision 1.0 $
  47.  */
  48. public class KStar extends DistributionClassifier
  49.   implements KStarConstants, OptionHandler, UpdateableClassifier, WeightedInstancesHandler {
  50.   /** The training instances used for classification. */
  51.   protected Instances m_Train; 
  52.   /** The number of instances in the dataset */
  53.   protected int m_NumInstances;
  54.   /** The number of class values */
  55.   protected int m_NumClasses;
  56.   /** The number of attributes */
  57.   protected int m_NumAttributes;
  58.   /** The class attribute type */
  59.   protected int m_ClassType;
  60.   /** Table of random class value colomns */
  61.   protected int [][] m_RandClassCols;
  62.   /** Flag turning on and off the computation of random class colomns */
  63.   protected int m_ComputeRandomCols = ON;
  64.   /** Flag turning on and off the initialisation of config variables */
  65.   protected int m_InitFlag = ON;
  66.   /**
  67.    * A custom data structure for caching distinct attribute values
  68.    * and their scale factor or stop parameter.
  69.    */
  70.   protected KStarCache [] m_Cache;
  71.   /** missing value treatment */
  72.   protected int m_MissingMode = M_AVERAGE;
  73.   /** 0 = use specified blend, 1 = entropic blend setting */
  74.   protected int m_BlendMethod = B_SPHERE;
  75.   /** default sphere of influence blend setting */
  76.   protected int m_GlobalBlend = 20;
  77.   /** Define possible missing value handling methods */
  78.   public static final Tag [] TAGS_MISSING = {
  79.     new Tag(M_DELETE, "Ignore the instance with the missing value"),
  80.     new Tag(M_MAXDIFF, "Treat missing values as maximally different"),
  81.     new Tag(M_NORMAL, "Normilize over the attributes"),
  82.     new Tag(M_AVERAGE, "Average column entropy curves")
  83.       };
  84.   /**
  85.    * Generates the classifier.
  86.    *
  87.    * @param instances set of instances serving as training data 
  88.    * @exception Exception if the classifier has not been generated successfully
  89.    */
  90.   public void buildClassifier(Instances instances) throws Exception {
  91.     String debug = "(KStar.buildClassifier) ";
  92.     if (instances.classIndex() < 0)
  93.       throw new Exception ("No class attribute assigned to instances");
  94.     if (instances.checkForStringAttributes())
  95.       throw new UnsupportedAttributeTypeException("Cannot handle string attributes!");
  96.     m_Train = new Instances(instances, 0, instances.numInstances());
  97.     // Throw away training instances with missing class
  98.     m_Train.deleteWithMissingClass();
  99.     // initializes class attributes ** java-speaking! :-) **
  100.     init_m_Attributes();
  101.   }
  102.   
  103.   /**
  104.    * Adds the supplied instance to the training set
  105.    *
  106.    * @param instance the instance to add
  107.    * @exception Exception if instance could not be incorporated successfully
  108.    */
  109.   public void updateClassifier(Instance instance) throws Exception {
  110.     String debug = "(KStar.updateClassifier) ";
  111.     if (m_Train.equalHeaders(instance.dataset()) == false)
  112.       throw new Exception("Incompatible instance types");
  113.     if ( instance.classIsMissing() )
  114.       return;
  115.     m_Train.add(instance);
  116.     // update relevant attributes ...
  117.     update_m_Attributes();
  118.   }
  119.   /**
  120.    * Calculates the class membership probabilities for the given test instance.
  121.    *
  122.    * @param instance the instance to be classified
  123.    * @return predicted class probability distribution
  124.    * @exception Exception if an error occurred during the prediction
  125.    */
  126.   public double [] distributionForInstance(Instance instance) throws Exception {
  127.     String debug = "(KStar.distributionForInstance) ";
  128.     double transProb = 0.0, temp = 0.0;
  129.     double [] classProbability = new double[m_NumClasses];
  130.     double [] predictedValue = new double[1];
  131.     // initialization ...
  132.     for (int i=0; i<classProbability.length; i++) {
  133.       classProbability[i] = 0.0;
  134.     }
  135.     predictedValue[0] = 0.0;
  136.     if (m_InitFlag == ON) {
  137. // need to compute them only once and will be used for all instances.
  138. // We are doing this because the evaluation module controls the calls. 
  139.       if (m_BlendMethod == B_ENTROPY) {
  140. generateRandomClassColomns();
  141.       }
  142.       m_Cache = new KStarCache[m_NumAttributes];
  143.       for (int i=0; i<m_NumAttributes;i++) {
  144. m_Cache[i] = new KStarCache();
  145.       }
  146.       m_InitFlag = OFF;
  147.       //      System.out.println("Computing...");
  148.     }
  149.     // init done.
  150.     Instance trainInstance;
  151.     Enumeration enum = m_Train.enumerateInstances();
  152.     while ( enum.hasMoreElements() ) {
  153.       trainInstance = (Instance)enum.nextElement();
  154.       transProb = instanceTransformationProbability(instance, trainInstance);      
  155.       switch ( m_ClassType )
  156. {
  157. case Attribute.NOMINAL:
  158.   classProbability[(int)trainInstance.classValue()] += transProb;
  159.   break;
  160. case Attribute.NUMERIC:
  161.   predictedValue[0] += transProb * trainInstance.classValue();
  162.   temp += transProb;
  163.   break;
  164. }
  165.     }
  166.     if (m_ClassType == Attribute.NOMINAL) {
  167.       double sum = Utils.sum(classProbability);
  168.       if (sum <= 0.0)
  169. for (int i=0; i<classProbability.length; i++)
  170.   classProbability[i] = 1/m_NumClasses;
  171.       else Utils.normalize(classProbability, sum);
  172.       return classProbability;
  173.     }
  174.     else {
  175.       predictedValue[0] = (temp != 0) ? predictedValue[0] / temp : 0.0;
  176.       return predictedValue;
  177.     }
  178.   }
  179.   /**
  180.    * Calculate the probability of the first instance transforming into the 
  181.    * second instance:
  182.    * the probability is the product of the transformation probabilities of 
  183.    * the attributes normilized over the number of instances used.
  184.    * 
  185.    * @param first the test instance
  186.    * @param second the train instance
  187.    * @return transformation probability value
  188.    */
  189.   private double instanceTransformationProbability(Instance first, 
  190.    Instance second) {
  191.     String debug = "(KStar.instanceTransformationProbability) ";
  192.     double transProb = 1.0;
  193.     int numMissAttr = 0;
  194.     for (int i = 0; i < m_NumAttributes; i++) {
  195.       if (i == m_Train.classIndex()) {
  196. continue; // ignore class attribute
  197.       }
  198.       if (first.isMissing(i)) { // test instance attribute value is missing
  199. numMissAttr++;
  200. continue;
  201.       }
  202.       transProb *= attrTransProb(first, second, i);
  203.       // normilize for missing values
  204.       if (numMissAttr != m_NumAttributes) {
  205. transProb = Math.pow(transProb, (double)m_NumAttributes / 
  206.      (m_NumAttributes - numMissAttr));
  207.       }
  208.       else { // weird case!
  209. transProb = 0.0;
  210.       }
  211.     }
  212.     // normilize for the train dataset
  213.      return transProb / m_NumInstances;
  214.   }
  215.   /**
  216.    * Calculates the transformation probability of the indexed test attribute 
  217.    * to the indexed train attribute.
  218.    *
  219.    * @param first the test instance.
  220.    * @param second the train instance.
  221.    * @param col the index of the attribute in the instance.
  222.    * @return the value of the transformation probability.
  223.    */
  224.   private double attrTransProb(Instance first, Instance second, int col) {
  225.     String debug = "(KStar.attrTransProb)";
  226.     double transProb = 0.0;
  227.     KStarNominalAttribute ksNominalAttr;
  228.     KStarNumericAttribute ksNumericAttr;
  229.     switch ( m_Train.attribute(col).type() )
  230.       {
  231.       case Attribute.NOMINAL:
  232. ksNominalAttr = new KStarNominalAttribute(first, second, col, m_Train, 
  233.   m_RandClassCols, 
  234.   m_Cache[col]);
  235. ksNominalAttr.setOptions(m_MissingMode, m_BlendMethod, m_GlobalBlend);
  236. transProb = ksNominalAttr.transProb();
  237. ksNominalAttr = null;
  238. break;
  239.       case Attribute.NUMERIC:
  240. ksNumericAttr = new KStarNumericAttribute(first, second, col, 
  241.   m_Train, m_RandClassCols, 
  242.   m_Cache[col]);
  243. ksNumericAttr.setOptions(m_MissingMode, m_BlendMethod, m_GlobalBlend);
  244. transProb = ksNumericAttr.transProb();
  245. ksNumericAttr = null;
  246. break;
  247.       }
  248.     return transProb;
  249.   }
  250.   /**
  251.    * Gets the method to use for handling missing values. Will be one of
  252.    * M_NORMAL, M_AVERAGE, M_MAXDIFF or M_DELETE.
  253.    *
  254.    * @return the method used for handling missing values.
  255.    */
  256.   public SelectedTag getMissingMode() {
  257.     return new SelectedTag(m_MissingMode, TAGS_MISSING);
  258.   }
  259.   
  260.   /**
  261.    * Sets the method to use for handling missing values. Values other than
  262.    * M_NORMAL, M_AVERAGE, M_MAXDIFF and M_DELETE will be ignored.
  263.    *
  264.    * @param newMode the method to use for handling missing values.
  265.    */
  266.   public void setMissingMode(SelectedTag newMode) {
  267.     
  268.     if (newMode.getTags() == TAGS_MISSING) {
  269.       m_MissingMode = newMode.getSelectedTag().getID();
  270.     }
  271.   }
  272.   /**
  273.    * Returns an enumeration describing the available options.
  274.    *
  275.    * @return an enumeration of all the available options.
  276.    */
  277.   public Enumeration listOptions() {
  278.     Vector optVector = new Vector( 3 );
  279.     optVector.addElement(new Option(
  280.       "tManual blend setting (default 20%)n",
  281.       "B", 1, "-B <num>"));
  282.     optVector.addElement(new Option(
  283.       "tEnable entropic auto-blend setting (symbolic class only)n",
  284.       "E", 0, "-E"));
  285.     optVector.addElement(new Option(
  286.       "tSpecify the missing value treatment mode (default a)n"
  287.       +"tValid options are: a(verage), d(elete), m(axdiff), n(ormal)n",
  288.       "M", 1,"-M <char>"));
  289.     return optVector.elements();
  290.   }
  291.   /**
  292.    * Set the global blend parameter
  293.    * @param b the value for global blending
  294.    */
  295.   public void setGlobalBlend(int b) {
  296.      m_GlobalBlend = b;
  297.       if ( m_GlobalBlend > 100 ) {
  298. m_GlobalBlend = 100;
  299.       }
  300.       if ( m_GlobalBlend < 0 ) {
  301. m_GlobalBlend = 0;
  302.       }
  303.   }
  304.   /**
  305.    * Get the value of the global blend parameter
  306.    * @return the value of the global blend parameter
  307.    */
  308.   public int getGlobalBlend() {
  309.     return m_GlobalBlend;
  310.   }
  311.   /**
  312.    * Set whether entropic blending is to be used.
  313.    * @param e true if entropic blending is to be used
  314.    */
  315.   public void setEntropicAutoBlend(boolean e) {
  316.     if (e) {
  317.       m_BlendMethod = B_ENTROPY;
  318.     } else {
  319.       m_BlendMethod = B_SPHERE;
  320.     }
  321.   }
  322.   /**
  323.    * Get whether entropic blending being used
  324.    * @return true if entropic blending is used
  325.    */
  326.   public boolean getEntropicAutoBlend() {
  327.     if (m_BlendMethod == B_ENTROPY) {
  328.       return true;
  329.     }
  330.     return false;
  331.   }
  332.   /**
  333.    * Parses a given list of options. Valid options are:
  334.    * ...
  335.    *
  336.    * @param options the list of options as an array of strings
  337.    * @exception Exception if an option is not supported
  338.    */
  339.   public void setOptions(String[] options) throws Exception {
  340.     String debug = "(KStar.setOptions)";
  341.     String blendStr = Utils.getOption('B', options);
  342.     if (blendStr.length() != 0) {
  343.       setGlobalBlend(Integer.parseInt(blendStr));
  344.     }
  345.     setEntropicAutoBlend(Utils.getFlag('E', options));
  346.     
  347.     String missingModeStr = Utils.getOption('M', options);
  348.     if (missingModeStr.length() != 0) {
  349.       switch ( missingModeStr.charAt(0) ) {
  350.       case 'a':
  351. setMissingMode(new SelectedTag(M_AVERAGE, TAGS_MISSING));
  352. break;
  353.       case 'd':
  354. setMissingMode(new SelectedTag(M_DELETE, TAGS_MISSING));
  355. break;
  356.       case 'm':
  357. setMissingMode(new SelectedTag(M_MAXDIFF, TAGS_MISSING));
  358. break;
  359.       case 'n':
  360. setMissingMode(new SelectedTag(M_NORMAL, TAGS_MISSING));
  361. break;
  362.       default:
  363. setMissingMode(new SelectedTag(M_AVERAGE, TAGS_MISSING));
  364.       }
  365.     }
  366.     Utils.checkForRemainingOptions(options);
  367.   }
  368.   /**
  369.    * Gets the current settings of K*.
  370.    *
  371.    * @return an array of strings suitable for passing to setOptions()
  372.    */
  373.   public String [] getOptions() {
  374.     // -B <num> -E -M <char>
  375.     String [] options = new String [ 5 ];
  376.     int itr = 0;
  377.     options[itr++] = "-B";
  378.     options[itr++] = "" + m_GlobalBlend;
  379.     if (getEntropicAutoBlend()) {
  380.       options[itr++] = "-E";
  381.     }
  382.     options[itr++] = "-M";
  383.     if (m_MissingMode == M_AVERAGE) {
  384.       options[itr++] = "" + "a";
  385.     }
  386.     else if (m_MissingMode == M_DELETE) {
  387.       options[itr++] = "" + "d";
  388.     }
  389.     else if (m_MissingMode == M_MAXDIFF) {
  390.       options[itr++] = "" + "m";
  391.     }
  392.     else if (m_MissingMode == M_NORMAL) {
  393.       options[itr++] = "" + "n";
  394.     }
  395.     while (itr < options.length) {
  396.       options[itr++] = "";
  397.     }
  398.     return options;
  399.   }
  400.   /**
  401.    * Returns a description of this classifier.
  402.    *
  403.    * @return a description of this classifier as a string.
  404.    */
  405.   public String toString() {
  406.     StringBuffer st = new StringBuffer();
  407.     st.append("KStar Beta Verion (0.1b).n"
  408.       +"Copyright (c) 1995-97 by Len Trigg (trigg@cs.waikato.ac.nz).n"
  409.       +"Java port to Weka by Abdelaziz Mahoui "
  410.       +"(am14@cs.waikato.ac.nz).nnKStar options : ");
  411.     String [] ops = getOptions();
  412.     for (int i=0;i<ops.length;i++) {
  413.       st.append(ops[i]+' ');
  414.     }
  415.     return st.toString();
  416.   }
  417.   
  418.   /**
  419.    * Main method for testing this class.
  420.    *
  421.    * @param argv should contain command line options (see setOptions)
  422.    */
  423.   public static void main(String [] argv) {
  424.     try {
  425.       System.out.println(Evaluation.evaluateModel(new KStar(), argv));
  426.     } catch (Exception e) {
  427.       // System.err.println(e.getMessage());
  428.       e.printStackTrace();
  429.     }
  430.   }
  431.   /**
  432.    * Initializes the m_Attributes of the class.
  433.    */
  434.   private void init_m_Attributes() {
  435.     try {
  436.       m_NumInstances = m_Train.numInstances();
  437.       m_NumClasses = m_Train.numClasses();
  438.       m_NumAttributes = m_Train.numAttributes();
  439.       m_ClassType = m_Train.classAttribute().type();
  440.     } catch(Exception e) {
  441.       e.printStackTrace();
  442.     }
  443.   }
  444.   /**
  445.    * Updates the m_attributes of the class.
  446.    */
  447.   private void update_m_Attributes() {
  448.     m_NumInstances = m_Train.numInstances();
  449.     m_InitFlag = ON;
  450.   }
  451.   /**
  452.    * Note: for Nominal Class Only!
  453.    * Generates a set of random versions of the class colomn.
  454.    */
  455.   private void generateRandomClassColomns() {
  456.     String debug = "(KStar.generateRandomClassColomns)";
  457.     Random generator = new Random(42);
  458.     //    Random generator = new Random();
  459.     m_RandClassCols = new int [NUM_RAND_COLS+1][];
  460.     int [] classvals = classValues();
  461.     for (int i=0; i < NUM_RAND_COLS; i++) {
  462.       // generate a randomized version of the class colomn
  463.       m_RandClassCols[i] = randomize(classvals, generator);
  464.     }
  465.     // original colomn is preserved in colomn NUM_RAND_COLS
  466.     m_RandClassCols[NUM_RAND_COLS] = classvals;
  467.   }
  468.   /**
  469.    * Note: for Nominal Class Only!
  470.    * Returns an array of the class values
  471.    *
  472.    * @return an array of class values
  473.    */
  474.   private int [] classValues() {
  475.     String debug = "(KStar.classValues)";
  476.     int [] classval = new int[m_NumInstances];
  477.     for (int i=0; i < m_NumInstances; i++) {
  478.       try {
  479. classval[i] = (int)m_Train.instance(i).classValue();
  480.       } catch (Exception ex) {
  481. ex.printStackTrace();
  482.       }
  483.     }
  484.     return classval;
  485.   }
  486.   /**
  487.    * Returns a copy of the array with its elements randomly redistributed.
  488.    *
  489.    * @param array the array to randomize.
  490.    * @return a copy of the array with its elements randomly redistributed.
  491.    */
  492.   private int [] randomize(int [] array, Random generator) {
  493.     String debug = "(KStar.randomize)";
  494.     int index;
  495.     int temp;
  496.     int [] newArray = new int[array.length];
  497.     System.arraycopy(array, 0, newArray, 0, array.length);
  498.     for (int j = newArray.length - 1; j > 0; j--) {
  499.       index = (int) ( generator.nextDouble() * (double)j );
  500.       temp = newArray[j];
  501.       newArray[j] = newArray[index];
  502.       newArray[index] = temp;
  503.     }
  504.     return newArray;
  505.   }
  506. } // class end