2012-03-14 34 views
5

Sto cercando di creare un classificatore di testo in JAVA con Weka. Ho letto alcuni tutorial e sto cercando di costruire il mio classificatore.Classificazione di base del testo con Weka in Java

ho le seguenti categorie:

computer,sport,unknown 

ei dati che già formati

cs belongs to computer 
java -> computer 
soccer -> sport 
snowboard -> sport 

Così, per esempio, se un utente vuole classificare la parola java, deve restituire il computer categoria (senza dubbio, java esiste solo in quella categoria!).

Compilare, ma genera un output strano.

L'output è:

 ====== RESULT ====== CLASSIFIED AS: [0.5769230769230769, 0.2884615384615385, 0.1346153846153846] 
     ====== RESULT ====== CLASSIFIED AS: [0.42857142857142855, 0.42857142857142855, 0.14285714285714285] 

Ma il primo testo di classificare è Java e occures solo nel computer di categoria, pertanto, dovrebbe essere

 [1.0 0.0 0.0] 

e per l'altro essa non dovrebbe essere trovato a tutti, quindi dovrebbe essere classificato come ignoto

 [0.0 0.0 1.0]. 

Ecco il codice:

import java.io.FileNotFoundException; 
    import java.io.Serializable; 
    import java.util.Arrays; 

    import weka.classifiers.Classifier; 
    import weka.classifiers.bayes.NaiveBayesMultinomialUpdateable; 
    import weka.core.Attribute; 
    import weka.core.FastVector; 
    import weka.core.Instance; 
    import weka.core.Instances; 
    import weka.filters.Filter; 
    import weka.filters.unsupervised.attribute.StringToWordVector; 

    public class TextClassifier implements Serializable { 

     private static final long serialVersionUID = -1397598966481635120L; 
     public static void main(String[] args) { 
      try { 
       TextClassifier cl = new TextClassifier(new NaiveBayesMultinomialUpdateable()); 
       cl.addCategory("computer"); 
       cl.addCategory("sport"); 
       cl.addCategory("unknown"); 
       cl.setupAfterCategorysAdded(); 

       // 
       cl.addData("cs", "computer"); 
       cl.addData("java", "computer"); 
       cl.addData("soccer", "sport"); 
       cl.addData("snowboard", "sport"); 

       double[] result = cl.classifyMessage("java"); 
       System.out.println("====== RESULT ====== \tCLASSIFIED AS:\t" + Arrays.toString(result)); 

       result = cl.classifyMessage("asdasdasd"); 
       System.out.println("====== RESULT ======\tCLASSIFIED AS:\t" + Arrays.toString(result)); 
      } catch (Exception e) { 
       e.printStackTrace(); 
      } 
     } 
     private Instances trainingData; 
     private StringToWordVector filter; 
     private Classifier classifier; 
     private boolean upToDate; 
     private FastVector classValues; 
     private FastVector attributes; 
     private boolean setup; 

     private Instances filteredData; 

     public TextClassifier(Classifier classifier) throws FileNotFoundException { 
      this(classifier, 10); 
     } 

     public TextClassifier(Classifier classifier, int startSize) throws FileNotFoundException { 
      this.filter = new StringToWordVector(); 
      this.classifier = classifier; 
      // Create vector of attributes. 
      this.attributes = new FastVector(2); 
      // Add attribute for holding texts. 
      this.attributes.addElement(new Attribute("text", (FastVector) null)); 
      // Add class attribute. 
      this.classValues = new FastVector(startSize); 
      this.setup = false; 

     } 

     public void addCategory(String category) { 
      category = category.toLowerCase(); 
      // if required, double the capacity. 
      int capacity = classValues.capacity(); 
      if (classValues.size() > (capacity - 5)) { 
       classValues.setCapacity(capacity * 2); 
      } 
      classValues.addElement(category); 
     } 

     public void addData(String message, String classValue) throws IllegalStateException { 
      if (!setup) { 
       throw new IllegalStateException("Must use setup first"); 
      } 
      message = message.toLowerCase(); 
      classValue = classValue.toLowerCase(); 
      // Make message into instance. 
      Instance instance = makeInstance(message, trainingData); 
      // Set class value for instance. 
      instance.setClassValue(classValue); 
      // Add instance to training data. 
      trainingData.add(instance); 
      upToDate = false; 
     } 

     /** 
     * Check whether classifier and filter are up to date. Build i necessary. 
     * @throws Exception 
     */ 
     private void buildIfNeeded() throws Exception { 
      if (!upToDate) { 
       // Initialize filter and tell it about the input format. 
       filter.setInputFormat(trainingData); 
       // Generate word counts from the training data. 
       filteredData = Filter.useFilter(trainingData, filter); 
       // Rebuild classifier. 
       classifier.buildClassifier(filteredData); 
       upToDate = true; 
      } 
     } 

     public double[] classifyMessage(String message) throws Exception { 
      message = message.toLowerCase(); 
      if (!setup) { 
       throw new Exception("Must use setup first"); 
      } 
      // Check whether classifier has been built. 
      if (trainingData.numInstances() == 0) { 
       throw new Exception("No classifier available."); 
      } 
      buildIfNeeded(); 
      Instances testset = trainingData.stringFreeStructure(); 
      Instance testInstance = makeInstance(message, testset); 

      // Filter instance. 
      filter.input(testInstance); 
      Instance filteredInstance = filter.output(); 
      return classifier.distributionForInstance(filteredInstance); 

     } 

     private Instance makeInstance(String text, Instances data) { 
      // Create instance of length two. 
      Instance instance = new Instance(2); 
      // Set value for message attribute 
      Attribute messageAtt = data.attribute("text"); 
      instance.setValue(messageAtt, messageAtt.addStringValue(text)); 
      // Give instance access to attribute information from the dataset. 
      instance.setDataset(data); 
      return instance; 
     } 

     public void setupAfterCategorysAdded() { 
      attributes.addElement(new Attribute("class", classValues)); 
      // Create dataset with initial capacity of 100, and set index of class. 
      trainingData = new Instances("MessageClassificationProblem", attributes, 100); 
      trainingData.setClassIndex(trainingData.numAttributes() - 1); 
      setup = true; 
     } 

    } 

Btw, ha trovato una buona pagina:

http://www.hakank.org/weka/TextClassifierApplet3.html

risposta

4

Il classificatore Bayes ti dà una (ponderata) probabilità che una parola appartiene ad una categoria. Questo non sarà quasi mai esattamente uguale a 0 o 1. Puoi impostare un hard-cutoff (es. 0.5) e decidere l'appartenenza a una classe basata su questo, o ispezionare le probabilità calcolate e decidere in base a quella (cioè la mappa più alta a 1, la più basso a 0).

+0

Sì, lo so. Ma in questo esempio, quando provo a classificare: result = cl.classifyMessage ("asdasdasd"); il risultato dovrebbe essere classificato come sconosciuto, ma non lo è:/ Ma, posso vedere che probabilmente non funzionerà. Perché non ho alcun documento per quella categoria ... Esiste una soluzione intelligente per aggiungere una categoria "uknown" o simile? E anche per la parola java, ho pensato che sarebbe stata ponderata più verso la categoria computer, perché non è nemmeno menzionata nelle altre categorie. – joxxe

+2

Dovresti aggiungere manualmente le cose a una categoria "sconosciuta" se la probabilità per l'appartenenza a ciascuna delle tue classi è troppo bassa. –

+0

Ma le probabilità insieme sono sempre 1.0. Quindi se provo a classificare qualche parola che non esiste in nessun documento, le probabilità insieme (per tutte le categorie) sono ancora 1.0 – joxxe

0

Se si tenta di ottenere classe definitiva, invece di distribuzioni, provate a cambiare

return classifier.distributionForInstance(filteredInstance);

a

return classifier.classifyInstance(filteredInstance);

+0

questa risposta è difficile da capire - quando pubblichi il codice, dovresti provare a spiegarlo – ronalchn

+4

Beh, se joxxe prova questo nel suo codice, capirà cosa intendo. Ad ogni modo, il metodo distributionForInstance() restituisce probabilità per ogni classe target. E il metodo classifyInstance() seleziona la classe con la probabilità più alta e restituisce l'indice di classe. Quindi suppongo che joxxe abbia bisogno del secondo metodo. –

1

ho pensato solo offrire fino che si potrebbe fare la maggior parte del genere la classificazione del testo funziona senza codifica semplicemente scaricando e utilizzando LightSIDE da http://lightsidelabs.com. Questo pacchetto Java open source include WEKA ed è disponibile per distribuzioni sia su Windows che su Mac; può gestire gran parte dei set di dati WEKA con grande flessibilità, consentendo di scorrere vari modelli, impostazioni e parametri e fornire un valido supporto per le istantanee e salvare i dati, i modelli e i risultati di classificazione in qualsiasi momento fino a quando non hai costruito un modello di cui sei soddisfatto. Questo prodotto si è dimostrato nella competizione ASAP su Kaggle.com lo scorso anno e sta ottenendo molta trazione. Naturalmente ci sono sempre dei motivi per cui le persone vogliono o hanno bisogno di "rollare le proprie" ma forse anche come controllo, conoscere e usare LightSIDE se si sta programmando le soluzioni WEKA potrebbe essere molto utile.