Question

I have trained a classifier for my instances, and now want to export it to an Android application, where the Weka library will be unavailable.

It is not suitable to simply add the Weka library in the Android application, because of it's size (6.5 Mb).

Is there any other way to use my classifier to evaluate and label other unlabeled instances? Are there any smaller, independent library specifically design for this?

Of course I could, eventually, write my own library to interpret the output model of Weka, but it would seem logical to me, that such a solution already exists. (although it escapes me, somehow)

Was it helpful?

Solution 2

After paying more attention the output model of weka, I noticed that by using the option that generates the tree in a Java class form, I can use it separatly from the weka library.

You can remove the generated WekaWrapper and keep only the internal class, which is a basic implementation of the tree:

The class looks something like this:

public class WekaWrapper
  extends Classifier {

  /**
   * Returns only the toString() method.
   *
   * @return a string describing the classifier
   */
  public String globalInfo() {
    return toString();
  }

  /**
   * Returns the capabilities of this classifier.
   *
   * @return the capabilities
   */
  public Capabilities getCapabilities() {
    weka.core.Capabilities result = new weka.core.Capabilities(this);

    result.enable(weka.core.Capabilities.Capability.NOMINAL_ATTRIBUTES);
    result.enable(weka.core.Capabilities.Capability.NOMINAL_CLASS);
    result.enable(weka.core.Capabilities.Capability.MISSING_CLASS_VALUES);

    result.setMinimumNumberInstances(0);

    return result;
  }

  /**
   * only checks the data against its capabilities.
   *
   * @param i the training data
   */
  public void buildClassifier(Instances i) throws Exception {
    // can classifier handle the data?
    getCapabilities().testWithFail(i);
  }

  /**
   * Classifies the given instance.
   *
   * @param i the instance to classify
   * @return the classification result
   */
  public double classifyInstance(Instance i) throws Exception {
    Object[] s = new Object[i.numAttributes()];

    for (int j = 0; j < s.length; j++) {
      if (!i.isMissing(j)) {
        if (i.attribute(j).isNominal())
          s[j] = new String(i.stringValue(j));
        else if (i.attribute(j).isNumeric())
          s[j] = new Double(i.value(j));
      }
    }

    // set class value to missing
    s[i.classIndex()] = null;

    return WekaClassifier.classify(s);
  }

  /**
   * Returns the revision string.
   * 
   * @return        the revision
   */
  public String getRevision() {
    return RevisionUtils.extract("1.0");
  }

  /**
   * Returns only the classnames and what classifier it is based on.
   *
   * @return a short description
   */
  public String toString() {
    return "Auto-generated classifier wrapper, based on weka.classifiers.trees.Id3 (generated with Weka 3.6.9).\n" + this.getClass().getName() + "/WekaClassifier";
  }

  /**
   * Runs the classfier from commandline.
   *
   * @param args the commandline arguments
   */
  public static void main(String args[]) {
    runClassifier(new WekaWrapper(), args);
  }
}

class WekaClassifier {
  private static void checkMissing(Object[] i, int index) {
    if (i[index] == null)
      throw new IllegalArgumentException("Null values are not allowed!");
  }

  public static double classify(Object[] i) {
    return node0(i);
  }

  protected static double node0(Object[] i) {
    return 0.0; // unacc
  }
}

So, yeah, in fact you can do it really easy. Things to remember:

  • to classify an instance, call the classify(Object[]) method;
  • the return value will be a float value;
  • the return values are explained in comments, right next to the return command;
  • the parameters have no validation, so be careful in which order you are inputing them (this part was done by the weka dependent part);
  • the order is the one defined in the arff file.

OTHER TIPS

There are no independent libraries that would do what you want. You could remove all the parts of Weka you don't need and package that into a library.

In your particular case, the easiest thing to do might be to take the decision tree that Weka learns and put it directly into the code in a series of if...else statements. You could even write a script that takes the (graphical) output of the decision tree and writes that code for you.

If you want to run RandomForests, you can use a little script I wrote that turns the output of WEKA's -printTrees option of the RandomForest classifier into Java source code.

http://pielot.org/2015/06/exporting-randomforest-models-to-java-source-code/

The code you need to include into your Android app will consist of three classes only: the class with the generated model + two classes to make the classification work.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top