I am facing some issues when trying to run my application outside the Netbeans IDE. I have a GUI which is calling calling a few of my own classes and methods, which in turn, is calling some Weka classes. The Weka class is giving an output,which is getting displayed on the GUI. Now when I do this on the IDE, the output gets displayed correctly. However, when I try to create a jar of my application and try to run the jar from outside the IDE, the output from the Weka class is not displayed. My classes, which I am using to do some parsing and processing of the data are working fine, though. Initially I thought it was a problem of external jar not getting included properly, but I checked the *PROJECT_HOME*/dist/lib folder and the weka.jar file seemed to be there. Can someone please help me out on this?
Thanks in advance!
One thing I can tell you I have developed one application outside IDE. You just need to get WEKA package folder in your application directory and import it as package in your java class application and directly call the WEKA classifiers or any other work you wish.
for example I am using PART Classifier following code works and speaks for itself.
import java.io.*;
import java.util.Random;
import weka.core.Instances;
import weka.classifiers.Classifier;
import weka.classifiers.rules.PART;
import weka.classifiers.Evaluation;
public class Prediction
{
public double Create(String Rf1,String Rf2,String Rf3) throws Exception
{
input p = new input();
double res=p.classify(Rf1,Rf2,Rf3);
return res;
}
}
class input
{
public double classify(String file1,String file2,String file3) throws Exception
{
// ------------> 1. Reading from an ARFF file
FileReader fr = new FileReader(file1);
BufferedReader br = new BufferedReader(fr);
Instances data = new Instances(br);
br.close();
// setting class attribute
data.setClassIndex(data.numAttributes() - 1);
// -------------> 2. Building a Classifier
String[] options = new String[1];
options[0] = "M 2 -C 0.25 -Q 1"; // confidenceFactor = 0.25, minNumObject = 2
PART tree = new PART(); // new instance of tree
tree.setOptions(options); // set the options
tree.buildClassifier(data); // build classifier
// -------------> 3. Cross-validation
Evaluation eval = new Evaluation(data);
eval.crossValidateModel(tree, data, 10, new Random(1));
// check --------------> 4. Train
Instances train = new Instances(data);
Instances test = new Instances(train);
// train classifier
Classifier cls = new PART();
cls.buildClassifier(train);
// evaluate classifier and print some statistics
Evaluation eval1 = new Evaluation(train);
eval1.evaluateModel(cls, test);
//System.out.println(eval1.toSummaryString("\nResults\n======\n", false));
// ----------------> 5. Statistics
String[] options1 = new String[2];
options1[0] = "-t";
options1[1] = file1;
// ----------------> 6. Classifying instances
// load unlabeled data
FileReader fr2 = new FileReader(file2);
BufferedReader br2 = new BufferedReader(fr2);
Instances unlabeled = new Instances(br2);
// set class attribute
unlabeled.setClassIndex(unlabeled.numAttributes() - 1);
// create copy
Instances labeled = new Instances(unlabeled);
double clsLabel[];
clsLabel = new double[100];
int count = 0;
// label instances
for (int i = 0; i < unlabeled.numInstances(); i++)
{
clsLabel[i] = tree.classifyInstance(unlabeled.instance(i));
labeled.instance(i).setClassValue(clsLabel[i]);
count++;
}
// save labeled data
FileWriter fw = new FileWriter(file3);
BufferedWriter bw = new BufferedWriter(fw);
bw.write(labeled.toString());
bw.newLine();
bw.flush();
bw.close();
fw.close();
for(int i=0;i<count;i++)
System.out.println(clsLabel[i]);
return clsLabel[0];
} // main
} // class
Related
I am using Weka to do a text classification. I have created a NaiveBayes model using the Weka GUI, and I have saved that model and then was trying to use this model to classify instances of a training set. This is my code :
Classifier clsClassifier = (Classifier) weka.core.SerializationHelper.read("Source/test/80percentModel.model");
StringToWordVector filter = new StringToWordVector();
BufferedReader reader = new BufferedReader(
new FileReader("Source/test/clt.train.arff"));
Instances trainingData = new Instances(reader);
reader.close();
trainingData.setClassIndex(trainingData.numAttributes() - 1);
filter.setInputFormat(trainingData);
BufferedReader reader2 = new BufferedReader(
new FileReader("Source/test/clt.test.arff"));
Instances testingData = new Instances(reader2);
reader2.close();
testingData.setClassIndex(testingData.numAttributes() - 1);
testingData = Filter.useFilter(testingData, filter);
System.out.println(testingData.numInstances());
for (int j = 0; j < testingData.numInstances(); j++) {
double res = clsClassifier.classifyInstance(testingData.get(j));
System.out.println(testingData.classAttribute().value((int)res));
}
I am getting the following error :
java.lang.IllegalArgumentException: Src and Dest differ in # of attributes: 1 != 1781
at weka.core.RelationalLocator.copyRelationalValues(RelationalLocator.java:87)
at weka.filters.Filter.copyValues(Filter.java:405)
at weka.filters.Filter.push(Filter.java:326)
at weka.filters.unsupervised.attribute.StringToWordVector.input(StringToWordVector.java:655)
at weka.classifiers.meta.FilteredClassifier.filterInstance(FilteredClassifier.java:672)
at weka.classifiers.meta.FilteredClassifier.distributionForInstance(FilteredClassifier.java:699)
at weka.classifiers.AbstractClassifier.classifyInstance(AbstractClassifier.java:173)
at test.WekaClassification.main(WekaClassification.java:66)
I dont quite get what I am doing wrong here. Why is there a mismatch in the number of attributes ? and is this the correct way to apply a trained model in a testData set ?
There are some possibilities, but your Error Shows that the number of attributes are not equal in your trained and dataset. They must be completely in a same format, type and value. The test file also should have the values for the label attribute. Check to see if in Weka GUI you get the same Error or not? StringToWordVector may not Filter in an affordable manner. check its output by watching the contents.
I'm using the model builder addon for OpenNLP to create a better NER model.
According to this post, I have used the code posted by markg :
public class ModelBuilderAddonUse {
private static List<String> getSentencesFromSomewhere() throws Exception
{
List<String> list = new ArrayList<String>();
BufferedReader reader = new BufferedReader(new FileReader("D:\\Work\\workspaces\\default\\UpdateModel\\documentrequirements.docx"));
String line;
while ((line = reader.readLine()) != null)
{
list.add(line);
}
reader.close();
return list;
}
public static void main(String[] args) throws Exception {
/**
* establish a file to put sentences in
*/
File sentences = new File("D:\\Work\\workspaces\\default\\UpdateModel\\sentences.text");
/**
* establish a file to put your NER hits in (the ones you want to keep based
* on prob)
*/
File knownEntities = new File("D:\\Work\\workspaces\\default\\UpdateModel\\knownentities.txt");
/**
* establish a BLACKLIST file to put your bad NER hits in (also can be based
* on prob)
*/
File blacklistedentities = new File("D:\\Work\\workspaces\\default\\UpdateModel\\blentities.txt");
/**
* establish a file to write your annotated sentences to
*/
File annotatedSentences = new File("D:\\Work\\workspaces\\default\\UpdateModel\\annotatedSentences.txt");
/**
* establish a file to write your model to
*/
File theModel = new File("D:\\Work\\workspaces\\default\\UpdateModel\\nl-ner-person.bin");
//------------create a bunch of file writers to write your results and sentences to a file
FileWriter sentenceWriter = new FileWriter(sentences, true);
FileWriter blacklistWriter = new FileWriter(blacklistedentities, true);
FileWriter knownEntityWriter = new FileWriter(knownEntities, true);
//set some thresholds to decide where to write hits, you don't have to use these at all...
double keeperThresh = .95;
double blacklistThresh = .7;
/**
* Load your model as normal
*/
TokenNameFinderModel personModel = new TokenNameFinderModel(new File("D:\\Work\\workspaces\\default\\UpdateModel\\nl-ner-person.bin"));
NameFinderME personFinder = new NameFinderME(personModel);
/**
* do your normal NER on the sentences you have
*/
for (String s : getSentencesFromSomewhere()) {
sentenceWriter.write(s.trim() + "\n");
sentenceWriter.flush();
String[] tokens = s.split(" ");//better to use a tokenizer really
Span[] find = personFinder.find(tokens);
double[] probs = personFinder.probs();
String[] names = Span.spansToStrings(find, tokens);
for (int i = 0; i < names.length; i++) {
//YOU PROBABLY HAVE BETTER HEURISTICS THAN THIS TO MAKE SURE YOU GET GOOD HITS OUT OF THE DEFAULT MODEL
if (probs[i] > keeperThresh) {
knownEntityWriter.write(names[i].trim() + "\n");
}
if (probs[i] < blacklistThresh) {
blacklistWriter.write(names[i].trim() + "\n");
}
}
personFinder.clearAdaptiveData();
blacklistWriter.flush();
knownEntityWriter.flush();
}
//flush and close all the writers
knownEntityWriter.flush();
knownEntityWriter.close();
sentenceWriter.flush();
sentenceWriter.close();
blacklistWriter.flush();
blacklistWriter.close();
/**
* THIS IS WHERE THE ADDON IS GOING TO USE THE FILES (AS IS) TO CREATE A NEW MODEL. YOU SHOULD NOT HAVE TO RUN THE FIRST PART AGAIN AFTER THIS RUNS, JUST NOW PLAY WITH THE
* KNOWN ENTITIES AND BLACKLIST FILES AND RUN THE METHOD BELOW AGAIN UNTIL YOU GET SOME DECENT RESULTS (A DECENT MODEL OUT OF IT).
*/
DefaultModelBuilderUtil.generateModel(sentences, knownEntities, blacklistedentities, theModel, annotatedSentences, "person", 3);
}
}
It also runs, but my output quits at :
annotated sentences: 1862
knowns: 58
Building Model using 1862 annotations
reading training data...
But in the example in the post it should go futher like this :
Indexing events using cutoff of 5
Computing event counts... done. 561755 events
Indexing... done.
Sorting and merging events... done. Reduced 561755 events to 127362.
Done indexing.
Incorporating indexed data for training...
done.
Number of Event Tokens: 127362
Number of Outcomes: 3
Number of Predicates: 106490
...done.
Can anyone help me to fix this problem, so I can generate a model?
I have searched realy a lot but cant find any good documutation about it.
Would really appreciat it, thanks.
Correct the path to your training data file like this:
File sentences = new File("D:/Work/workspaces/default/UpdateModel/sentences.text");
instead of
File sentences = new File("D:\\Work\\workspaces\\default\\UpdateModel\\sentences.text");
Update
This is how is used, by adding the files to the project folder. Try it like this -
File sentences = new File("src/training/resources/CreateModel/sentences.txt");
Check my respository for reference on Github
This should help.
I am using weka java API to classify couple of my instances, the file that I feed my weka file with is as follow:
0.3,0.1,1
0.0,0.04,0
0.0,0.03,1
And all of the above instances have unique id assigned to them for example the first row has id of 1098...
I wrote the following code which use weka java API to classify the result and return those instances that are classified incorrectly:
public static void SVM(ArrayList<String[]> testData) throws FileNotFoundException, IOException,
Exception {
BufferedReader breader = null;
breader = new BufferedReader(new FileReader("weka/train.txt"));
Instances train = new Instances(breader);
train.setClassIndex(train.numAttributes() - 1);
Instances unlabeled = new Instances(new BufferedReader(new FileReader(
"weka/test.txt")));
breader.close();
// set class attribute
unlabeled.setClassIndex(unlabeled.numAttributes() - 1);
// create copy
Instances labeled = new Instances(unlabeled);
LibSVM svm = new LibSVM();
svm.buildClassifier(train);
Evaluation eval = new Evaluation(train);
BufferedWriter writer = new BufferedWriter(new FileWriter(
"weka/labeledSVM.txt"));
for (int i = 0; i < unlabeled.numInstances(); i++) {
double clsLabel = svm.classifyInstance(unlabeled.instance(i));
if(unlabeled.instance(i).value(5)!=clsLabel){
writer.write("the unique id is: "+testData.get(i)[0] + " real label of the text is : "+ unlabeled.instance(i).toString() + ", According to Algorithm reult label is: " + clsLabel);
writer.newLine();
}
writer.flush();
writer.close();
}
But a big problem is that the mapping between the unique id and the instance labeled by algorithm is incorrect, so I am wondering if there is any way that I can include the unique id of each text inside the instances that I have but tell the weka classifier to ignore it ?
for example something like this:
1980,0.3,0.1,1
1981,0.0,0.04,0
1982,0.0,0.03,0
or any other suggestion is appreciated
The only way I found to do this was to create my own subclass of Instance.
Use "AddID" filter which will assign a uniqueID to every instance, then use FilteredClassifier i.e. weka.classifiers.meta.FilteredClassifier.
I wrote a WEKA java code to train 4 classifiers. I saved the classifiers models and want to use them to predict new unseen instances (think about it as someone who wants to test whether a tweet is positive or negative).
I used StringToWordsVector filter on the training data. And to avoid the "Src and Dest differ in # of attributes" error I used the following code to train the filter using the trained data before applying the filter on the new instance to try and predict whether a new instance is positive or negative. And I just can't get it right.
Classifier cls = (Classifier) weka.core.SerializationHelper.read("models/myModel.model"); //reading one of the trained classifiers
BufferedReader datafile = readDataFile("Tweets/tone1.ARFF"); //read training data
Instances data = new Instances(datafile);
data.setClassIndex(data.numAttributes() - 1);
Filter filter = new StringToWordVector(50);//keep 50 words
filter.setInputFormat(data);
Instances filteredData = Filter.useFilter(data, filter);
// rebuild classifier
cls.buildClassifier(filteredData);
String testInstance= "Text that I want to use as an unseen instance and predict whether it's positive or negative";
System.out.println(">create test instance");
FastVector attributes = new FastVector(2);
attributes.addElement(new Attribute("text", (FastVector) null));
// Add class attribute.
FastVector classValues = new FastVector(2);
classValues.addElement("Negative");
classValues.addElement("Positive");
attributes.addElement(new Attribute("Tone", classValues));
// Create dataset with initial capacity of 100, and set index of class.
Instances tests = new Instances("test istance", attributes, 100);
tests.setClassIndex(tests.numAttributes() - 1);
Instance test = new Instance(2);
// Set value for message attribute
Attribute messageAtt = tests.attribute("text");
test.setValue(messageAtt, messageAtt.addStringValue(testInstance));
test.setDataset(tests);
Filter filter2 = new StringToWordVector(50);
filter2.setInputFormat(tests);
Instances filteredTests = Filter.useFilter(tests, filter2);
System.out.println(">train Test filter using training data");
Standardize sfilter = new Standardize(); //Match the number of attributes between src and dest.
sfilter.setInputFormat(filteredData); // initializing the filter with training set
filteredTests = Filter.useFilter(filteredData, sfilter); // create new test set
ArffSaver saver = new ArffSaver(); //save test data to ARFF file
saver.setInstances(filteredTests);
File unseenFile = new File ("Tweets/unseen.ARFF");
saver.setFile(unseenFile);
saver.writeBatch();
When I try to Standardize the Input data using the filtered training data I get a new ARFF file (unseen.ARFF) but with 2000 (same number of training data) instances where most of the values are negative. I don't understand why or how to remove those instances.
System.out.println(">Evaluation"); //without the following 2 lines I get ArrayIndexOutOfBoundException.
filteredData.setClassIndex(filteredData.numAttributes() - 1);
filteredTests.setClassIndex(filteredTests.numAttributes() - 1);
Evaluation eval = new Evaluation(filteredData);
eval.evaluateModel(cls, filteredTests);
System.out.println(eval.toSummaryString("\nResults\n======\n", false));
Printing the evaluation results I want to see for example a percentage of how positive or negative this instance is but instead I get the following. I also want to see 1 instance instead of 2000. Any help on how to do this will be great.
> Results
======
Correlation coefficient 0.0285
Mean absolute error 0.8765
Root mean squared error 1.2185
Relative absolute error 409.4123 %
Root relative squared error 121.8754 %
Total Number of Instances 2000
Thanks
use eval.predictions(). It is an java.util.ArrayList<Prediction>. Then you can use Prediction.weight() method to get how much positive or negative your test variable is....
cls.distributionForInstance(newInst) returns the probability distribution for an instance. Check the docs
I have reached a good solution and here I share my code with you. This trains a classifier using WEKA Java code then use it to predict new unseen instances. Some parts - like paths - are hardcoded but you can easily modify the method to take parameters.
/**
* This method performs classification of unseen instance.
* It starts by training a model using a selection of classifiers then classifiy new unlabled instances.
*/
public static void predict() throws Exception {
//start by providing the paths for your training and testing ARFF files make sure both files have the same structure and the exact classes in the header
//initialise classifier
Classifier classifier = null;
System.out.println("read training arff");
Instances train = new Instances(new BufferedReader(new FileReader("Train.arff")));
train.setClassIndex(0);//in my case the class was the first attribute thus zero otherwise it's the number of attributes -1
System.out.println("read testing arff");
Instances unlabeled = new Instances(new BufferedReader(new FileReader("Test.arff")));
unlabeled.setClassIndex(0);
// training using a collection of classifiers (NaiveBayes, SMO (AKA SVM), KNN and Decision trees.)
String[] algorithms = {"nb","smo","knn","j48"};
for(int w=0; w<algorithms.length;w++){
if(algorithms[w].equals("nb"))
classifier = new NaiveBayes();
if(algorithms[w].equals("smo"))
classifier = new SMO();
if(algorithms[w].equals("knn"))
classifier = new IBk();
if(algorithms[w].equals("j48"))
classifier = new J48();
System.out.println("==========================================================================");
System.out.println("training using " + algorithms[w] + " classifier");
Evaluation eval = new Evaluation(train);
//perform 10 fold cross validation
eval.crossValidateModel(classifier, train, 10, new Random(1));
String output = eval.toSummaryString();
System.out.println(output);
String classDetails = eval.toClassDetailsString();
System.out.println(classDetails);
classifier.buildClassifier(train);
}
Instances labeled = new Instances(unlabeled);
// label instances (use the trained classifier to classify new unseen instances)
for (int i = 0; i < unlabeled.numInstances(); i++) {
double clsLabel = classifier.classifyInstance(unlabeled.instance(i));
labeled.instance(i).setClassValue(clsLabel);
System.out.println(clsLabel + " -> " + unlabeled.classAttribute().value((int) clsLabel));
}
//save the model for future use
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("myModel.dat"));
out.writeObject(classifier);
out.close();
System.out.println("===== Saved model =====");
}
I trained an IBK classifier with some training data that I created manually as following:
ArrayList<Attribute> atts = new ArrayList<Attribute>();
ArrayList<String> classVal = new ArrayList<String>();
classVal.add("C1");
classVal.add("C2");
atts.add(new Attribute("a"));
atts.add(new Attribute("b"));
atts.add(new Attribute("c"));
atts.add(new Attribute("d"));
atts.add(new Attribute("##class##", classVal));
Instances dataRaw = new Instances("TestInstances", atts, 0);
dataRaw.setClassIndex(dataRaw.numAttributes() - 1);
double[] instanceValue1 = new double[]{3,0,1,0,0};
dataRaw.add(new DenseInstance(1.0, instanceValue1));
double[] instanceValue2 = new double[]{2,1,1,0,0};
dataRaw.add(new DenseInstance(1.0, instanceValue2));
double[] instanceValue3 = new double[]{2,0,2,0,0};
dataRaw.add(new DenseInstance(1.0, instanceValue3));
double[] instanceValue4 = new double[]{1,3,0,0,1};
dataRaw.add(new DenseInstance(1.0, instanceValue4));
double[] instanceValue5 = new double[]{0,3,1,0,1};
dataRaw.add(new DenseInstance(1.0, instanceValue5));
double[] instanceValue6 = new double[]{0,2,1,1,1};
dataRaw.add(new DenseInstance(1.0, instanceValue6));
Then I build up the classifier:
IBk ibk = new IBk();
try {
ibk.buildClassifier(dataRaw);
} catch (Exception e) {
e.printStackTrace();
}
I want to create a new instance with unlabeled class and classify this instance, I tried the following with no luck.
IBk ibk = new IBk();
try {
ibk.buildClassifier(dataRaw);
double[] values = new double[]{3,1,0,0,-1};
DenseInstance newInst = new DenseInstance(1.0,values);
double classif = ibk.classifyInstance(newInst);
System.out.println(classif);
} catch (Exception e) {
e.printStackTrace();
}
I just get the following errors
weka.core.UnassignedDatasetException: DenseInstance doesn't have access to a dataset!
at weka.core.AbstractInstance.classAttribute(AbstractInstance.java:98)
at weka.classifiers.AbstractClassifier.classifyInstance(AbstractClassifier.java:74)
at TextCategorizationTest.instancesWithDoubleValues(TextCategorizationTest.java:136)
at TextCategorizationTest.main(TextCategorizationTest.java:33)
Looks like I am doing something wrong while creating a new instance. How can I create an unlabeled instance exactly ?
Thanks in Advance
You will see this error, when you classify a new instance which is not associated with a dataset. You have to associate every new instance you create to an Instances object using setDataset.
//Make a place holder Instances
//If you already have access to one, you can skip this step
Instances dataset = new Instances("testdata", attr, 1);
dataset.setClassIndex(classIdx);
DenseInstance newInst = new DenseInstance(1.0,values);
//To associate your instance with Instances object, in this case dataset
newInst.setDataset(dataset);
After this you can classify newly created instance.
double classif = ibk.classifyInstance(newInst);
http://www.cs.tufts.edu/~ablumer/weka/doc/weka.core.Instance.html
Detailed Implementation Link
The problem is with this line:
double classif = ibk.classifyInstance(newInst);
When you try to classify newInst, Weka throws an exception because newInst has no Instances object (i.e., dataset) associated with it - thus it does not know anything about its class attribute.
You should first create a new Instances object similar to the dataRaw, add your unlabeled instance to it, set class index, and only then try classifying it, e.g.:
Instances dataUnlabeled = new Instances("TestInstances", atts, 0);
dataUnlabeled.add(newInst);
dataUnlabeled.setClassIndex(dataUnlabeled.numAttributes() - 1);
double classif = ibk.classifyInstance(dataUnlabeled.firstInstance());
See pages 203 - 204 of the WEKA documentation. That helped me a lot! (The Weka Manual is a pdf file that is located in your weka installation folder. Just open the doucmentation.html and it will point you to the pdf manual.)
Copy-pasting some snippets of the code listings of Chapter 17 (Using the WEKA API / Creating datasets in memory) should help you solve the task.