/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * TLDSimple.java * Copyright (C) 2005 University of Waikato, Hamilton, New Zealand * */ package weka.classifiers.mi; import weka.classifiers.RandomizableClassifier; import weka.core.Capabilities; import weka.core.Instance; import weka.core.Instances; import weka.core.MultiInstanceCapabilitiesHandler; import weka.core.Optimization; import weka.core.Option; import weka.core.OptionHandler; import weka.core.RevisionUtils; import weka.core.TechnicalInformation; import weka.core.TechnicalInformationHandler; import weka.core.Utils; import weka.core.Capabilities.Capability; import weka.core.TechnicalInformation.Field; import weka.core.TechnicalInformation.Type; import java.util.Enumeration; import java.util.Random; import java.util.Vector; /** * A simpler version of TLD, mu random but sigma^2 fixed and estimated via data.
*
* For more information see:
*
* Xin Xu (2003). Statistical learning in multiple instance problem. Hamilton, NZ. *

* * BibTeX: *

 * @mastersthesis{Xu2003,
 *    address = {Hamilton, NZ},
 *    author = {Xin Xu},
 *    note = {0657.594},
 *    school = {University of Waikato},
 *    title = {Statistical learning in multiple instance problem},
 *    year = {2003}
 * }
 * 
*

* * Valid options are:

* *

 -C
 *  Set whether or not use empirical
 *  log-odds cut-off instead of 0
* *
 -R <numOfRuns>
 *  Set the number of multiple runs 
 *  needed for searching the MLE.
* *
 -S <num>
 *  Random number seed.
 *  (default 1)
* *
 -D
 *  If set, classifier is run in debug mode and
 *  may output additional info to the console
* * * @author Eibe Frank (eibe@cs.waikato.ac.nz) * @author Xin Xu (xx5@cs.waikato.ac.nz) * @version $Revision: 5481 $ */ public class TLDSimple extends RandomizableClassifier implements OptionHandler, MultiInstanceCapabilitiesHandler, TechnicalInformationHandler { /** for serialization */ static final long serialVersionUID = 9040995947243286591L; /** The mean for each attribute of each positive exemplar */ protected double[][] m_MeanP = null; /** The mean for each attribute of each negative exemplar */ protected double[][] m_MeanN = null; /** The effective sum of weights of each positive exemplar in each dimension*/ protected double[][] m_SumP = null; /** The effective sum of weights of each negative exemplar in each dimension*/ protected double[][] m_SumN = null; /** Estimated sigma^2 in positive bags*/ protected double[] m_SgmSqP; /** Estimated sigma^2 in negative bags*/ protected double[] m_SgmSqN; /** The parameters to be estimated for each positive exemplar*/ protected double[] m_ParamsP = null; /** The parameters to be estimated for each negative exemplar*/ protected double[] m_ParamsN = null; /** The dimension of each exemplar, i.e. (numAttributes-2) */ protected int m_Dimension = 0; /** The class label of each exemplar */ protected double[] m_Class = null; /** The number of class labels in the data */ protected int m_NumClasses = 2; /** The very small number representing zero */ static public double ZERO = 1.0e-12; protected int m_Run = 1; protected double m_Cutoff; protected boolean m_UseEmpiricalCutOff = false; private double[] m_LkRatio; private Instances m_Attribute = null; /** * Returns a string describing this filter * * @return a description of the filter suitable for * displaying in the explorer/experimenter gui */ public String globalInfo() { return "A simpler version of TLD, mu random but sigma^2 fixed and estimated " + "via data.\n\n" + "For more information see:\n\n" + getTechnicalInformation().toString(); } /** * Returns an instance of a TechnicalInformation object, containing * detailed information about the technical background of this class, * e.g., paper reference or book this class is based on. * * @return the technical information about this class */ public TechnicalInformation getTechnicalInformation() { TechnicalInformation result; result = new TechnicalInformation(Type.MASTERSTHESIS); result.setValue(Field.AUTHOR, "Xin Xu"); result.setValue(Field.YEAR, "2003"); result.setValue(Field.TITLE, "Statistical learning in multiple instance problem"); result.setValue(Field.SCHOOL, "University of Waikato"); result.setValue(Field.ADDRESS, "Hamilton, NZ"); result.setValue(Field.NOTE, "0657.594"); return result; } /** * Returns default capabilities of the classifier. * * @return the capabilities of this classifier */ public Capabilities getCapabilities() { Capabilities result = super.getCapabilities(); result.disableAll(); // attributes result.enable(Capability.NOMINAL_ATTRIBUTES); result.enable(Capability.RELATIONAL_ATTRIBUTES); result.enable(Capability.MISSING_VALUES); // class result.enable(Capability.BINARY_CLASS); result.enable(Capability.MISSING_CLASS_VALUES); // other result.enable(Capability.ONLY_MULTIINSTANCE); return result; } /** * Returns the capabilities of this multi-instance classifier for the * relational data. * * @return the capabilities of this object * @see Capabilities */ public Capabilities getMultiInstanceCapabilities() { Capabilities result = super.getCapabilities(); result.disableAll(); // attributes result.enable(Capability.NOMINAL_ATTRIBUTES); result.enable(Capability.NUMERIC_ATTRIBUTES); result.enable(Capability.DATE_ATTRIBUTES); result.enable(Capability.MISSING_VALUES); // class result.disableAllClasses(); result.enable(Capability.NO_CLASS); return result; } /** * * @param exs the training exemplars * @throws Exception if the model cannot be built properly */ public void buildClassifier(Instances exs)throws Exception{ // can classifier handle the data? getCapabilities().testWithFail(exs); // remove instances with missing class exs = new Instances(exs); exs.deleteWithMissingClass(); int numegs = exs.numInstances(); m_Dimension = exs.attribute(1).relation().numAttributes(); m_Attribute = exs.attribute(1).relation().stringFreeStructure(); Instances pos = new Instances(exs, 0), neg = new Instances(exs, 0); // Divide into two groups for(int u=0; u1) && (varP[v][w]>ZERO)){ m_SgmSqP[w] += varP[v][w]*(m_SumP[v][w]-1.0)/m_SumP[v][w]; //m_SgmSqP[w] += varP[v][w]*(m_SumP[v][w]-1.0); effNumExP[w]++; // Not count exemplars with 1 instance pInvN[w] += 1.0/m_SumP[v][w]; //pInvN[w] += m_SumP[v][w]; } else numOneInsExsP[w]++; } } } for(int v=0; v < nnum; v++){ //Instance nx = neg.instance(v); Instances nxi = neg.instance(v).relationalValue(1); for (int k=0; k1) && (varN[v][w]>ZERO)){ m_SgmSqN[w] += varN[v][w]*(m_SumN[v][w]-1.0)/m_SumN[v][w]; //m_SgmSqN[w] += varN[v][w]*(m_SumN[v][w]-1.0); effNumExN[w]++; // Not count exemplars with 1 instance nInvN[w] += 1.0/m_SumN[v][w]; //nInvN[w] += m_SumN[v][w]; } else numOneInsExsN[w]++; } } } // Expected \sigma^2 /* if m_SgmSqP[u] or m_SgmSqN[u] is 0, assign 0 to sigma^2. * Otherwise, may cause k m_SgmSqP / m_SgmSqN to be NaN. * Modified by Lin Dong (Sep. 2005) */ for (int u=0; u < m_Dimension; u++){ // For exemplars with only one instance, use avg(\sigma^2) of other exemplars if (m_SgmSqP[u]!=0) m_SgmSqP[u] /= (effNumExP[u]-pInvN[u]); else m_SgmSqP[u] = 0; if (m_SgmSqN[u]!=0) m_SgmSqN[u] /= (effNumExN[u]-nInvN[u]); else m_SgmSqN[u] = 0; //m_SgmSqP[u] /= (pInvN[u]-effNumExP[u]); //m_SgmSqN[u] /= (nInvN[u]-effNumExN[u]); effNumExP[u] += numOneInsExsP[u]; effNumExN[u] += numOneInsExsN[u]; pMM[u] /= effNumExP[u]; nMM[u] /= effNumExN[u]; pVM[u] = pVM[u]/(effNumExP[u]-1.0) - pMM[u]*pMM[u]*effNumExP[u]/(effNumExP[u]-1.0); nVM[u] = nVM[u]/(effNumExN[u]-1.0) - nMM[u]*nMM[u]*effNumExN[u]/(effNumExN[u]-1.0); } //Bounds and parameter values for each run double[][] bounds = new double[2][2]; double[] pThisParam = new double[2], nThisParam = new double[2]; // Initial values for parameters double w, m; Random whichEx = new Random(m_Seed); // Optimize for one dimension for (int x=0; x < m_Dimension; x++){ // System.out.println("\n\n!!!!!!!!!!!!!!!!!!!!!!???Dimension #"+x); // Positive examplars: first run pThisParam[0] = pVM[x]; // w if( pThisParam[0] <= ZERO) pThisParam[0] = 1.0; pThisParam[1] = pMM[x]; // m // Negative examplars: first run nThisParam[0] = nVM[x]; // w if(nThisParam[0] <= ZERO) nThisParam[0] = 1.0; nThisParam[1] = nMM[x]; // m // Bound constraints bounds[0][0] = ZERO; // w > 0 bounds[0][1] = Double.NaN; bounds[1][0] = Double.NaN; bounds[1][1] = Double.NaN; double pminVal=Double.MAX_VALUE, nminVal=Double.MAX_VALUE; TLDSimple_Optm pOp=null, nOp=null; boolean isRunValid = true; double[] sumP=new double[pnum], meanP=new double[pnum]; double[] sumN=new double[nnum], meanN=new double[nnum]; // One dimension for(int p=0; p=neg[nOrder[n]]); n++, fstAccu++); if(n>=nNum){ // totally seperate m_Cutoff = (neg[nOrder[nNum-1]]+pos[pOrder[0]])/2.0; //m_Cutoff = neg[nOrder[nNum-1]]; return; } count=n; while((p=neg[nOrder[n]]){ // Neg has less log-odds fstAccu += 1.0; split=neg[nOrder[n]]; n++; } else{ sndAccu -= 1.0; split=pos[pOrder[p]]; p++; } count++; /* double entropy=0.0, cover=(double)count; if(fstAccu>0.0) entropy -= fstAccu*Math.log(fstAccu/cover); if(sndAccu>0.0) entropy -= sndAccu*Math.log(sndAccu/(total-cover)); if(entropy < minEntropy){ minEntropy = entropy; //find the next smallest //double next = neg[nOrder[n]]; //if(pos[pOrder[p]] maxAccu) || ((fstAccu+sndAccu == maxAccu) && (Math.abs(split)")); Enumeration enu = super.listOptions(); while (enu.hasMoreElements()) { result.addElement(enu.nextElement()); } return result.elements(); } /** * Parses a given list of options.

* * Valid options are:

* *

 -C
   *  Set whether or not use empirical
   *  log-odds cut-off instead of 0
* *
 -R <numOfRuns>
   *  Set the number of multiple runs 
   *  needed for searching the MLE.
* *
 -S <num>
   *  Random number seed.
   *  (default 1)
* *
 -D
   *  If set, classifier is run in debug mode and
   *  may output additional info to the console
* * * @param options the list of options as an array of strings * @throws Exception if an option is not supported */ public void setOptions(String[] options) throws Exception{ setDebug(Utils.getFlag('D', options)); setUsingCutOff(Utils.getFlag('C', options)); String runString = Utils.getOption('R', options); if (runString.length() != 0) setNumRuns(Integer.parseInt(runString)); else setNumRuns(1); super.setOptions(options); } /** * Gets the current settings of the Classifier. * * @return an array of strings suitable for passing to setOptions */ public String[] getOptions() { Vector result; String[] options; int i; result = new Vector(); options = super.getOptions(); for (i = 0; i < options.length; i++) result.add(options[i]); if (getDebug()) result.add("-D"); if (getUsingCutOff()) result.add("-C"); result.add("-R"); result.add("" + getNumRuns()); return (String[]) result.toArray(new String[result.size()]); } /** * Returns the tip text for this property * * @return tip text for this property suitable for * displaying in the explorer/experimenter gui */ public String numRunsTipText() { return "The number of runs to perform."; } /** * Sets the number of runs to perform. * * @param numRuns the number of runs to perform */ public void setNumRuns(int numRuns) { m_Run = numRuns; } /** * Returns the number of runs to perform. * * @return the number of runs to perform */ public int getNumRuns() { return m_Run; } /** * Returns the tip text for this property * * @return tip text for this property suitable for * displaying in the explorer/experimenter gui */ public String usingCutOffTipText() { return "Whether to use an empirical cutoff."; } /** * Sets whether to use an empirical cutoff. * * @param cutOff whether to use an empirical cutoff */ public void setUsingCutOff (boolean cutOff) { m_UseEmpiricalCutOff =cutOff; } /** * Returns whether an empirical cutoff is used * * @return true if an empirical cutoff is used */ public boolean getUsingCutOff() { return m_UseEmpiricalCutOff ; } /** * Gets a string describing the classifier. * * @return a string describing the classifer built. */ public String toString(){ StringBuffer text = new StringBuffer("\n\nTLDSimple:\n"); double sgm, w, m; for (int x=0, y=0; x