/*
* 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.
*/
/*
* Classifier.java
* Copyright (C) 2002 University of Waikato, Hamilton, New Zealand
*
*/
package weka.gui.beans;
import java.awt.BorderLayout;
import java.beans.EventSetDescriptor;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.filechooser.FileFilter;
import weka.classifiers.rules.ZeroR;
import weka.core.Instances;
import weka.core.OptionHandler;
import weka.core.Utils;
import weka.core.xml.KOML;
import weka.core.xml.XStream;
import weka.experiment.Task;
import weka.experiment.TaskStatusInfo;
import weka.gui.ExtensionFileFilter;
import weka.gui.Logger;
/**
* Bean that wraps around weka.classifiers
*
* @author Mark Hall
* @version $Revision: 6197 $
* @since 1.0
* @see JPanel
* @see BeanCommon
* @see Visible
* @see WekaWrapper
* @see Serializable
* @see UserRequestAcceptor
* @see TrainingSetListener
* @see TestSetListener
*/
public class Classifier
extends JPanel
implements BeanCommon, Visible,
WekaWrapper, EventConstraints,
Serializable, UserRequestAcceptor,
TrainingSetListener, TestSetListener,
InstanceListener, ConfigurationProducer {
/** for serialization */
private static final long serialVersionUID = 659603893917736008L;
protected BeanVisual m_visual =
new BeanVisual("Classifier",
BeanVisual.ICON_PATH+"DefaultClassifier.gif",
BeanVisual.ICON_PATH+"DefaultClassifier_animated.gif");
private static int IDLE = 0;
private static int BUILDING_MODEL = 1;
private static int CLASSIFYING = 2;
private int m_state = IDLE;
//private Thread m_buildThread = null;
/**
* Global info for the wrapped classifier (if it exists).
*/
protected String m_globalInfo;
/**
* Objects talking to us
*/
private Hashtable m_listenees = new Hashtable();
/**
* Objects listening for batch classifier events
*/
private Vector m_batchClassifierListeners = new Vector();
/**
* Objects listening for incremental classifier events
*/
private Vector m_incrementalClassifierListeners = new Vector();
/**
* Objects listening for graph events
*/
private Vector m_graphListeners = new Vector();
/**
* Objects listening for text events
*/
private Vector m_textListeners = new Vector();
/**
* Holds training instances for batch training. Not transient because
* header is retained for validating any instance events that this
* classifier might be asked to predict in the future.
*/
private Instances m_trainingSet;
private transient Instances m_testingSet;
private weka.classifiers.Classifier m_Classifier = new ZeroR();
/** Template used for creating copies when building in parallel */
private weka.classifiers.Classifier m_ClassifierTemplate = m_Classifier;
private IncrementalClassifierEvent m_ie =
new IncrementalClassifierEvent(this);
/** the extension for serialized models (binary Java serialization) */
public final static String FILE_EXTENSION = "model";
private transient JFileChooser m_fileChooser = null;
protected FileFilter m_binaryFilter =
new ExtensionFileFilter("."+FILE_EXTENSION, "Binary serialized model file (*"
+ FILE_EXTENSION + ")");
protected FileFilter m_KOMLFilter =
new ExtensionFileFilter(KOML.FILE_EXTENSION + FILE_EXTENSION,
"XML serialized model file (*"
+ KOML.FILE_EXTENSION + FILE_EXTENSION + ")");
protected FileFilter m_XStreamFilter =
new ExtensionFileFilter(XStream.FILE_EXTENSION + FILE_EXTENSION,
"XML serialized model file (*"
+ XStream.FILE_EXTENSION + FILE_EXTENSION + ")");
/**
* If the classifier is an incremental classifier, should we
* update it (ie train it on incoming instances). This makes it
* possible incrementally test on a separate stream of instances
* without updating the classifier, or mix batch training/testing
* with incremental training/testing
*/
private boolean m_updateIncrementalClassifier = true;
private transient Logger m_log = null;
/**
* Event to handle when processing incremental updates
*/
private InstanceEvent m_incrementalEvent;
/**
* Number of threads to use to train models with
*/
protected int m_executionSlots = 2;
// protected int m_queueSize = 5;
/**
* Pool of threads to train models on incoming data
*/
protected transient ThreadPoolExecutor m_executorPool;
/**
* Stores completed models and associated data sets.
*/
protected transient BatchClassifierEvent[][] m_outputQueues;
/**
* Stores which sets from which runs have been completed.
*/
protected transient boolean[][] m_completedSets;
/**
* Identifier for the current batch. A batch is a group
* of related runs/sets.
*/
protected transient Date m_currentBatchIdentifier;
/**
* Holds original icon label text
*/
protected String m_oldText = "";
/**
* true if we should reject any further training
* data sets, until all processing has been finished,
* once we've received the last fold of
* the last run.
*/
protected boolean m_reject = false;
/**
* True if we should block rather reject until
* all processing has been completed.
*/
protected boolean m_block = false;
/**
* Global info (if it exists) for the wrapped classifier
*
* @return the global info
*/
public String globalInfo() {
return m_globalInfo;
}
/**
* Creates a new Classifier
instance.
*/
public Classifier() {
setLayout(new BorderLayout());
add(m_visual, BorderLayout.CENTER);
setClassifierTemplate(m_ClassifierTemplate);
//setupFileChooser();
}
private void startExecutorPool() {
if (m_executorPool != null) {
m_executorPool.shutdownNow();
}
m_executorPool = new ThreadPoolExecutor(m_executionSlots, m_executionSlots,
120, TimeUnit.SECONDS, new LinkedBlockingQueue());
}
/**
* Set a custom (descriptive) name for this bean
*
* @param name the name to use
*/
public void setCustomName(String name) {
m_visual.setText(name);
}
/**
* Get the custom (descriptive) name for this bean (if one has been set)
*
* @return the custom name (or the default name)
*/
public String getCustomName() {
return m_visual.getText();
}
protected void setupFileChooser() {
if (m_fileChooser == null) {
m_fileChooser =
new JFileChooser(new File(System.getProperty("user.dir")));
}
m_fileChooser.addChoosableFileFilter(m_binaryFilter);
if (KOML.isPresent()) {
m_fileChooser.addChoosableFileFilter(m_KOMLFilter);
}
if (XStream.isPresent()) {
m_fileChooser.addChoosableFileFilter(m_XStreamFilter);
}
m_fileChooser.setFileFilter(m_binaryFilter);
}
/**
* Get the number of execution slots (threads) used
* to train models.
*
* @return the number of execution slots.
*/
public int getExecutionSlots() {
return m_executionSlots;
}
/**
* Set the number of execution slots (threads) to use to
* train models with.
*
* @param slots the number of execution slots to use.
*/
public void setExecutionSlots(int slots) {
m_executionSlots = slots;
}
/**
* Set whether to block on receiving the last fold
* of the last run rather than rejecting any further
* data until all processing is complete.
*
* @param block true if we should block on the
* last fold of the last run.
*/
public void setBlockOnLastFold(boolean block) {
m_block = block;
}
/**
* Gets whether we are blocking on the last fold of the
* last run rather than rejecting any further data until
* all processing has been completed.
*
* @return true if we are blocking on the last fold
* of the last run
*/
public boolean getBlockOnLastFold() {
return m_block;
}
/**
* Set the template classifier for this wrapper
*
* @param c a weka.classifiers.Classifier
value
*/
public void setClassifierTemplate(weka.classifiers.Classifier c) {
boolean loadImages = true;
if (c.getClass().getName().
compareTo(m_ClassifierTemplate.getClass().getName()) == 0) {
loadImages = false;
} else {
// classifier has changed so any batch training status is now
// invalid
m_trainingSet = null;
}
m_ClassifierTemplate = c;
String classifierName = c.getClass().toString();
classifierName = classifierName.substring(classifierName.
lastIndexOf('.')+1,
classifierName.length());
if (loadImages) {
if (!m_visual.loadIcons(BeanVisual.ICON_PATH+classifierName+".gif",
BeanVisual.ICON_PATH+classifierName+"_animated.gif")) {
useDefaultVisual();
}
}
m_visual.setText(classifierName);
if (!(m_ClassifierTemplate instanceof weka.classifiers.UpdateableClassifier) &&
(m_listenees.containsKey("instance"))) {
if (m_log != null) {
m_log.logMessage("[Classifier] " + statusMessagePrefix() + " WARNING : "
+ getCustomName() +" is not an incremental classifier");
}
}
// get global info
m_globalInfo = KnowledgeFlowApp.getGlobalInfo(m_ClassifierTemplate);
}
/**
* Return the classifier template currently in use.
*
* @return the classifier template currently in use.
*/
public weka.classifiers.Classifier getClassifierTemplate() {
return m_ClassifierTemplate;
}
private void setTrainedClassifier(weka.classifiers.Classifier tc) {
m_Classifier = tc;
// set the template
weka.classifiers.Classifier newTemplate = null;
try {
String[] options = ((OptionHandler)tc).getOptions();
newTemplate = weka.classifiers.AbstractClassifier.forName(tc.getClass().getName(), options);
setClassifierTemplate(newTemplate);
} catch (Exception ex) {
if (m_log != null) {
m_log.logMessage("[Classifier] " + statusMessagePrefix() + ex.getMessage());
String errorMessage = statusMessagePrefix()
+ "ERROR: see log for details.";
m_log.statusMessage(errorMessage);
} else {
ex.printStackTrace();
}
}
}
/**
* Returns true if this classifier has an incoming connection that is
* an instance stream
*
* @return true if has an incoming connection that is an instance stream
*/
public boolean hasIncomingStreamInstances() {
if (m_listenees.size() == 0) {
return false;
}
if (m_listenees.containsKey("instance")) {
return true;
}
return false;
}
/**
* Returns true if this classifier has an incoming connection that is
* a batch set of instances
*
* @return a boolean
value
*/
public boolean hasIncomingBatchInstances() {
if (m_listenees.size() == 0) {
return false;
}
if (m_listenees.containsKey("trainingSet") ||
m_listenees.containsKey("testSet")) {
return true;
}
return false;
}
/**
* Get the currently trained classifier.
*
* @return a weka.classifiers.Classifier
value
*/
public weka.classifiers.Classifier getClassifier() {
return m_Classifier;
}
/**
* Sets the algorithm (classifier) for this bean
*
* @param algorithm an Object
value
* @exception IllegalArgumentException if an error occurs
*/
public void setWrappedAlgorithm(Object algorithm)
{
if (!(algorithm instanceof weka.classifiers.Classifier)) {
throw new IllegalArgumentException(algorithm.getClass()+" : incorrect "
+"type of algorithm (Classifier)");
}
setClassifierTemplate((weka.classifiers.Classifier)algorithm);
}
/**
* Returns the wrapped classifier
*
* @return an Object
value
*/
public Object getWrappedAlgorithm() {
return getClassifierTemplate();
}
/**
* Get whether an incremental classifier will be updated on the
* incoming instance stream.
*
* @return true if an incremental classifier is to be updated.
*/
public boolean getUpdateIncrementalClassifier() {
return m_updateIncrementalClassifier;
}
/**
* Set whether an incremental classifier will be updated on the
* incoming instance stream.
*
* @param update true if an incremental classifier is to be updated.
*/
public void setUpdateIncrementalClassifier(boolean update) {
m_updateIncrementalClassifier = update;
}
/**
* Accepts an instance for incremental processing.
*
* @param e an InstanceEvent
value
*/
public void acceptInstance(InstanceEvent e) {
m_incrementalEvent = e;
handleIncrementalEvent();
}
/**
* Handles initializing and updating an incremental classifier
*/
private void handleIncrementalEvent() {
if (m_executorPool != null &&
(m_executorPool.getQueue().size() > 0 ||
m_executorPool.getActiveCount() > 0)) {
String messg = "[Classifier] " + statusMessagePrefix()
+ " is currently batch training!";
if (m_log != null) {
m_log.logMessage(messg);
m_log.statusMessage(statusMessagePrefix() + "WARNING: "
+ "Can't accept instance - batch training in progress.");
} else {
System.err.println(messg);
}
return;
}
if (m_incrementalEvent.getStatus() == InstanceEvent.FORMAT_AVAILABLE) {
// clear any warnings/errors from the log
if (m_log != null) {
m_log.statusMessage(statusMessagePrefix() + "remove");
}
// Instances dataset = m_incrementalEvent.getInstance().dataset();
Instances dataset = m_incrementalEvent.getStructure();
// default to the last column if no class is set
if (dataset.classIndex() < 0) {
stop();
String errorMessage = statusMessagePrefix()
+ "ERROR: no class attribute set in incoming stream!";
if (m_log != null) {
m_log.statusMessage(errorMessage);
m_log.logMessage("[" + getCustomName() + "] " + errorMessage);
} else {
System.err.println("[" + getCustomName() + "] " + errorMessage);
}
return;
// System.err.println("Classifier : setting class index...");
//dataset.setClassIndex(dataset.numAttributes()-1);
}
try {
// initialize classifier if m_trainingSet is null
// otherwise assume that classifier has been pre-trained in batch
// mode, *if* headers match
if (m_trainingSet == null || !m_trainingSet.equalHeaders(dataset)) {
if (!(m_ClassifierTemplate instanceof
weka.classifiers.UpdateableClassifier)) {
stop(); // stop all processing
if (m_log != null) {
String msg = (m_trainingSet == null)
? statusMessagePrefix()
+ "ERROR: classifier has not been batch "
+"trained; can't process instance events."
: statusMessagePrefix()
+ "ERROR: instance event's structure is different from "
+"the data that "
+ "was used to batch train this classifier; can't continue.";
m_log.logMessage("[Classifier] " + msg);
m_log.statusMessage(msg);
}
return;
}
if (m_trainingSet != null &&
(!dataset.equalHeaders(m_trainingSet))) {
if (m_log != null) {
String msg = statusMessagePrefix()
+ " WARNING : structure of instance events differ "
+"from data used in batch training this "
+"classifier. Resetting classifier...";
m_log.logMessage("[Classifier] " + msg);
m_log.statusMessage(msg);
}
m_trainingSet = null;
}
if (m_trainingSet == null) {
// initialize the classifier if it hasn't been trained yet
m_trainingSet = new Instances(dataset, 0);
m_Classifier = weka.classifiers.AbstractClassifier.makeCopy(m_ClassifierTemplate);
m_Classifier.buildClassifier(m_trainingSet);
}
}
} catch (Exception ex) {
stop();
if (m_log != null) {
m_log.statusMessage(statusMessagePrefix()
+ "ERROR (See log for details)");
m_log.logMessage("[Classifier] " + statusMessagePrefix()
+ " problem during incremental processing. "
+ ex.getMessage());
}
ex.printStackTrace();
}
// Notify incremental classifier listeners of new batch
System.err.println("NOTIFYING NEW BATCH");
m_ie.setStructure(dataset);
m_ie.setClassifier(m_Classifier);
notifyIncrementalClassifierListeners(m_ie);
return;
} else {
if (m_trainingSet == null) {
// simply return. If the training set is still null after
// the first instance then the classifier must not be updateable
// and hasn't been previously batch trained - therefore we can't
// do anything meaningful
return;
}
}
try {
// test on this instance
if (m_incrementalEvent.getInstance().dataset().classIndex() < 0) {
// System.err.println("Classifier : setting class index...");
m_incrementalEvent.getInstance().dataset().setClassIndex(
m_incrementalEvent.getInstance().dataset().numAttributes()-1);
}
int status = IncrementalClassifierEvent.WITHIN_BATCH;
/* if (m_incrementalEvent.getStatus() == InstanceEvent.FORMAT_AVAILABLE) {
status = IncrementalClassifierEvent.NEW_BATCH; */
/* } else */ if (m_incrementalEvent.getStatus() ==
InstanceEvent.BATCH_FINISHED) {
status = IncrementalClassifierEvent.BATCH_FINISHED;
}
m_ie.setStatus(status); m_ie.setClassifier(m_Classifier);
m_ie.setCurrentInstance(m_incrementalEvent.getInstance());
notifyIncrementalClassifierListeners(m_ie);
// now update on this instance (if class is not missing and classifier
// is updateable and user has specified that classifier is to be
// updated)
if (m_ClassifierTemplate instanceof weka.classifiers.UpdateableClassifier &&
m_updateIncrementalClassifier == true &&
!(m_incrementalEvent.getInstance().
isMissing(m_incrementalEvent.getInstance().
dataset().classIndex()))) {
((weka.classifiers.UpdateableClassifier)m_Classifier).
updateClassifier(m_incrementalEvent.getInstance());
}
if (m_incrementalEvent.getStatus() ==
InstanceEvent.BATCH_FINISHED) {
if (m_textListeners.size() > 0) {
String modelString = m_Classifier.toString();
String titleString = m_Classifier.getClass().getName();
titleString = titleString.
substring(titleString.lastIndexOf('.') + 1,
titleString.length());
modelString = "=== Classifier model ===\n\n" +
"Scheme: " +titleString+"\n" +
"Relation: " + m_trainingSet.relationName() + "\n\n"
+ modelString;
titleString = "Model: " + titleString;
TextEvent nt = new TextEvent(this,
modelString,
titleString);
notifyTextListeners(nt);
}
}
} catch (Exception ex) {
stop();
if (m_log != null) {
m_log.logMessage("[Classifier] " + statusMessagePrefix()
+ ex.getMessage());
m_log.statusMessage(statusMessagePrefix()
+ "ERROR (see log for details)");
ex.printStackTrace();
} else {
ex.printStackTrace();
}
}
}
protected class TrainingTask implements Runnable, Task {
private int m_runNum;
private int m_maxRunNum;
private int m_setNum;
private int m_maxSetNum;
private Instances m_train = null;
private TaskStatusInfo m_taskInfo = new TaskStatusInfo();
public TrainingTask(int runNum, int maxRunNum,
int setNum, int maxSetNum, Instances train) {
m_runNum = runNum;
m_maxRunNum = maxRunNum;
m_setNum = setNum;
m_maxSetNum = maxSetNum;
m_train = train;
m_taskInfo.setExecutionStatus(TaskStatusInfo.TO_BE_RUN);
}
public void run() {
execute();
}
public void execute() {
try {
if (m_train != null) {
if (m_train.classIndex() < 0) {
// stop all processing
stop();
String errorMessage = statusMessagePrefix()
+ "ERROR: no class attribute set in test data!";
if (m_log != null) {
m_log.statusMessage(errorMessage);
m_log.logMessage("[Classifier] " + errorMessage);
} else {
System.err.println("[Classifier] " + errorMessage);
}
return;
// assume last column is the class
/* m_train.setClassIndex(m_train.numAttributes()-1);
if (m_log != null) {
m_log.logMessage("[Classifier] " + statusMessagePrefix()
+ " : assuming last "
+"column is the class");
} */
}
if (m_runNum == 1 && m_setNum == 1) {
// set this back to idle once the last fold
// of the last run has completed
m_state = BUILDING_MODEL; // global state
// local status of this runnable
m_taskInfo.setExecutionStatus(TaskStatusInfo.PROCESSING);
}
//m_visual.setAnimated();
//m_visual.setText("Building model...");
String msg = statusMessagePrefix()
+ "Building model for run " + m_runNum + " fold " + m_setNum;
if (m_log != null) {
m_log.statusMessage(msg);
} else {
System.err.println(msg);
}
// buildClassifier();
// copy the classifier configuration
weka.classifiers.Classifier classifierCopy =
weka.classifiers.AbstractClassifier.makeCopy(m_ClassifierTemplate);
// build this model
classifierCopy.buildClassifier(m_train);
if (m_runNum == m_maxRunNum && m_setNum == m_maxSetNum) {
// Save the last classifier (might be used later on for
// classifying further test sets.
m_Classifier = classifierCopy;
m_trainingSet = m_train;
}
//if (m_batchClassifierListeners.size() > 0) {
// notify anyone who might be interested in just the model
// and training set.
BatchClassifierEvent ce =
new BatchClassifierEvent(Classifier.this, classifierCopy,
new DataSetEvent(this, m_train),
null, // no test set (yet)
m_setNum, m_maxSetNum);
ce.setGroupIdentifier(m_currentBatchIdentifier.getTime());
notifyBatchClassifierListeners(ce);
// store in the output queue (if we have incoming test set events)
ce =
new BatchClassifierEvent(Classifier.this, classifierCopy,
new DataSetEvent(this, m_train),
null, // no test set (yet)
m_setNum, m_maxSetNum);
ce.setGroupIdentifier(m_currentBatchIdentifier.getTime());
classifierTrainingComplete(ce);
//}
if (classifierCopy instanceof weka.core.Drawable &&
m_graphListeners.size() > 0) {
String grphString =
((weka.core.Drawable)classifierCopy).graph();
int grphType = ((weka.core.Drawable)classifierCopy).graphType();
String grphTitle = classifierCopy.getClass().getName();
grphTitle = grphTitle.substring(grphTitle.
lastIndexOf('.')+1,
grphTitle.length());
grphTitle = "Set " + m_setNum + " ("
+ m_train.relationName() + ") "
+ grphTitle;
GraphEvent ge = new GraphEvent(Classifier.this,
grphString,
grphTitle,
grphType);
notifyGraphListeners(ge);
}
if (m_textListeners.size() > 0) {
String modelString = classifierCopy.toString();
String titleString = classifierCopy.getClass().getName();
titleString = titleString.
substring(titleString.lastIndexOf('.') + 1,
titleString.length());
modelString = "=== Classifier model ===\n\n" +
"Scheme: " +titleString+"\n" +
"Relation: " + m_train.relationName() +
((m_maxSetNum > 1)
? "\nTraining Fold: " + m_setNum
:"")
+ "\n\n"
+ modelString;
titleString = "Model: " + titleString;
TextEvent nt = new TextEvent(Classifier.this,
modelString,
titleString);
notifyTextListeners(nt);
}
}
} catch (Exception ex) {
ex.printStackTrace();
if (m_log != null) {
String titleString = "[Classifier] " + statusMessagePrefix();
titleString += " run " + m_runNum + " fold " + m_setNum
+ " failed to complete.";
m_log.logMessage(titleString + " (build classifier). "
+ ex.getMessage());
m_log.statusMessage(statusMessagePrefix()
+ "ERROR (see log for details)");
ex.printStackTrace();
}
m_taskInfo.setExecutionStatus(TaskStatusInfo.FAILED);
// Stop all processing
stop();
} finally {
m_visual.setStatic();
if (m_log != null) {
m_log.statusMessage(statusMessagePrefix() + "Finished.");
}
m_state = IDLE;
if (Thread.currentThread().isInterrupted()) {
// prevent any classifier events from being fired
m_trainingSet = null;
if (m_log != null) {
String titleString = "[Classifier] " + statusMessagePrefix();
m_log.logMessage(titleString + " ("
+ " run " + m_runNum + " fold " + m_setNum + ") interrupted!");
m_log.statusMessage(statusMessagePrefix() + "INTERRUPTED");
/* // are we the last active thread?
if (m_executorPool.getActiveCount() == 1) {
String msg = "[Classifier] " + statusMessagePrefix()
+ " last classifier unblocking...";
System.err.println(msg + " (interrupted)");
m_log.logMessage(msg + " (interrupted)");
// m_log.statusMessage(statusMessagePrefix() + "finished.");
m_block = false;
m_state = IDLE;
block(false);
} */
}
/*System.err.println("Queue size: " + m_executorPool.getQueue().size() +
" Active count: " + m_executorPool.getActiveCount()); */
} /* else {
// check to see if we are the last active thread
if (m_executorPool == null ||
(m_executorPool.getQueue().size() == 0 &&
m_executorPool.getActiveCount() == 1)) {
String msg = "[Classifier] " + statusMessagePrefix()
+ " last classifier unblocking...";
System.err.println(msg);
if (m_log != null) {
m_log.logMessage(msg);
} else {
System.err.println(msg);
}
//m_visual.setText(m_oldText);
if (m_log != null) {
m_log.statusMessage(statusMessagePrefix() + "Finished.");
}
// m_outputQueues = null; // free memory
m_block = false;
block(false);
}
} */
}
}
public TaskStatusInfo getTaskStatus() {
// TODO
return null;
}
}
/**
* Accepts a training set and builds batch classifier
*
* @param e a TrainingSetEvent
value
*/
public void acceptTrainingSet(final TrainingSetEvent e) {
if (e.isStructureOnly()) {
// no need to build a classifier, instead just generate a dummy
// BatchClassifierEvent in order to pass on instance structure to
// any listeners - eg. PredictionAppender can use it to determine
// the final structure of instances with predictions appended
BatchClassifierEvent ce =
new BatchClassifierEvent(this, m_Classifier,
new DataSetEvent(this, e.getTrainingSet()),
new DataSetEvent(this, e.getTrainingSet()),
e.getSetNumber(), e.getMaxSetNumber());
notifyBatchClassifierListeners(ce);
return;
}
if (m_reject) {
//block(true);
if (m_log != null) {
m_log.statusMessage(statusMessagePrefix() + "BUSY. Can't accept data "
+ "at this time.");
m_log.logMessage("[Classifier] " + statusMessagePrefix()
+ " BUSY. Can't accept data at this time.");
}
return;
}
// Do some initialization if this is the first set of the first run
if (e.getRunNumber() == 1 && e.getSetNumber() == 1) {
// m_oldText = m_visual.getText();
// store the training header
m_trainingSet = new Instances(e.getTrainingSet(), 0);
m_state = BUILDING_MODEL;
String msg = "[Classifier] " + statusMessagePrefix()
+ " starting executor pool ("
+ getExecutionSlots() + " slots)...";
if (m_log != null) {
m_log.logMessage(msg);
} else {
System.err.println(msg);
}
// start the execution pool
if (m_executorPool == null) {
startExecutorPool();
}
// setup output queues
msg = "[Classifier] " + statusMessagePrefix() + " setup output queues.";
if (m_log != null) {
m_log.logMessage(msg);
} else {
System.err.println(msg);
}
m_outputQueues =
new BatchClassifierEvent[e.getMaxRunNumber()][e.getMaxSetNumber()];
m_completedSets = new boolean[e.getMaxRunNumber()][e.getMaxSetNumber()];
m_currentBatchIdentifier = new Date();
}
// create a new task and schedule for execution
TrainingTask newTask = new TrainingTask(e.getRunNumber(), e.getMaxRunNumber(),
e.getSetNumber(), e.getMaxSetNumber(), e.getTrainingSet());
String msg = "[Classifier] " + statusMessagePrefix() + " scheduling run "
+ e.getRunNumber() +" fold " + e.getSetNumber() + " for execution...";
if (m_log != null) {
m_log.logMessage(msg);
} else {
System.err.println(msg);
}
// delay just a little bit
/*try {
Thread.sleep(10);
} catch (Exception ex){} */
m_executorPool.execute(newTask);
}
/**
* Accepts a test set for a batch trained classifier
*
* @param e a TestSetEvent
value
*/
public synchronized void acceptTestSet(TestSetEvent e) {
if (m_reject) {
if (m_log != null) {
m_log.statusMessage(statusMessagePrefix() + "BUSY. Can't accept data "
+ "at this time.");
m_log.logMessage("[Classifier] " + statusMessagePrefix()
+ " BUSY. Can't accept data at this time.");
}
return;
}
Instances testSet = e.getTestSet();
if (testSet != null) {
if (testSet.classIndex() < 0) {
// testSet.setClassIndex(testSet.numAttributes() - 1);
// stop all processing
stop();
String errorMessage = statusMessagePrefix()
+ "ERROR: no class attribute set in test data!";
if (m_log != null) {
m_log.statusMessage(errorMessage);
m_log.logMessage("[Classifier] " + errorMessage);
} else {
System.err.println("[Classifier] " + errorMessage);
}
return;
}
}
// If we just have a test set connection or
// there is just one run involving one set (and we are not
// currently building a model), then use the
// last saved model
if (m_Classifier != null && m_state == IDLE &&
(!m_listenees.containsKey("trainingSet") ||
(e.getMaxRunNumber() == 1 && e.getMaxSetNumber() == 1))) {
// if this is structure only then just return at this point
if (e.getTestSet() != null && e.isStructureOnly()) {
return;
}
// check that we have a training set/header (if we don't,
// then it means that no model has been loaded
if (m_trainingSet == null) {
stop();
String errorMessage = statusMessagePrefix()
+ "ERROR: no trained/loaded classifier to use for prediction!";
if (m_log != null) {
m_log.statusMessage(errorMessage);
m_log.logMessage("[Classifier] " + errorMessage);
} else {
System.err.println("[Classifier] " + errorMessage);
}
return;
}
testSet = e.getTestSet();
if (e.getRunNumber() == 1 && e.getSetNumber() == 1) {
m_currentBatchIdentifier = new Date();
}
if (testSet != null) {
if (m_trainingSet.equalHeaders(testSet)) {
BatchClassifierEvent ce =
new BatchClassifierEvent(this, m_Classifier,
new DataSetEvent(this, m_trainingSet),
new DataSetEvent(this, e.getTestSet()),
e.getRunNumber(), e.getMaxRunNumber(),
e.getSetNumber(), e.getMaxSetNumber());
ce.setGroupIdentifier(m_currentBatchIdentifier.getTime());
if (m_log != null && !e.isStructureOnly()) {
m_log.statusMessage(statusMessagePrefix() + "Finished.");
}
notifyBatchClassifierListeners(ce);
} else {
// if headers do not match check to see if it's
// just the class that is different and that
// all class values are missing
if (testSet.numInstances() > 0) {
if (testSet.classIndex() == m_trainingSet.classIndex() &&
testSet.attributeStats(testSet.classIndex()).missingCount ==
testSet.numInstances()) {
// now check the other attributes against the training
// structure
boolean ok = true;
for (int i = 0; i < testSet.numAttributes(); i++) {
if (i != testSet.classIndex()) {
ok = testSet.attribute(i).equals(m_trainingSet.attribute(i));
if (!ok) {
break;
}
}
}
if (ok) {
BatchClassifierEvent ce =
new BatchClassifierEvent(this, m_Classifier,
new DataSetEvent(this, m_trainingSet),
new DataSetEvent(this, e.getTestSet()),
e.getRunNumber(), e.getMaxRunNumber(),
e.getSetNumber(), e.getMaxSetNumber());
ce.setGroupIdentifier(m_currentBatchIdentifier.getTime());
if (m_log != null && !e.isStructureOnly()) {
m_log.statusMessage(statusMessagePrefix() + "Finished.");
}
notifyBatchClassifierListeners(ce);
} else {
stop();
String errorMessage = statusMessagePrefix()
+ "ERROR: structure of training and test sets is not compatible!";
if (m_log != null) {
m_log.statusMessage(errorMessage);
m_log.logMessage("[Classifier] " + errorMessage);
} else {
System.err.println("[Classifier] " + errorMessage);
}
}
}
}
}
}
} else {
/* System.err.println("[Classifier] accepting test set: run "
+ e.getRunNumber() + " fold " + e.getSetNumber()); */
if (m_outputQueues[e.getRunNumber() - 1][e.getSetNumber() - 1] == null) {
// store an event with a null model and training set (to be filled in later)
m_outputQueues[e.getRunNumber() - 1][e.getSetNumber() - 1] =
new BatchClassifierEvent(this, null, null,
new DataSetEvent(this, e.getTestSet()),
e.getRunNumber(), e.getMaxRunNumber(),
e.getSetNumber(), e.getMaxSetNumber());
if (e.getRunNumber() == e.getMaxRunNumber() &&
e.getSetNumber() == e.getMaxSetNumber()) {
// block on the last fold of the last run
/* System.err.println("[Classifier] blocking on last fold of last run...");
block(true); */
m_reject = true;
if (m_block) {
block(true);
}
}
} else {
// Otherwise, there is a model here waiting for a test set...
m_outputQueues[e.getRunNumber() - 1][e.getSetNumber() - 1].
setTestSet(new DataSetEvent(this, e.getTestSet()));
checkCompletedRun(e.getRunNumber(), e.getMaxRunNumber(), e.getMaxSetNumber());
}
}
}
private synchronized void classifierTrainingComplete(BatchClassifierEvent ce) {
// check the output queues if we have an incoming test set connection
if (m_listenees.containsKey("testSet")) {
String msg = "[Classifier] " + statusMessagePrefix()
+ " storing model for run " + ce.getRunNumber()
+ " fold " + ce.getSetNumber();
if (m_log != null) {
m_log.logMessage(msg);
} else {
System.err.println(msg);
}
if (m_outputQueues[ce.getRunNumber() - 1][ce.getSetNumber() - 1] == null) {
// store the event - test data filled in later
m_outputQueues[ce.getRunNumber() - 1][ce.getSetNumber() - 1] = ce;
} else {
// there is a test set here waiting for a model and training set
m_outputQueues[ce.getRunNumber() - 1][ce.getSetNumber() - 1].
setClassifier(ce.getClassifier());
m_outputQueues[ce.getRunNumber() - 1][ce.getSetNumber() - 1].
setTrainSet(ce.getTrainSet());
}
checkCompletedRun(ce.getRunNumber(), ce.getMaxRunNumber(), ce.getMaxSetNumber());
}
}
private synchronized void checkCompletedRun(int runNum, int maxRunNum, int maxSets) {
// look to see if there are any completed classifiers that we can pass
// on for evaluation
for (int i = 0; i < maxSets; i++) {
if (m_outputQueues[runNum - 1][i] != null) {
if (m_outputQueues[runNum - 1][i].getClassifier() != null &&
m_outputQueues[runNum - 1][i].getTestSet() != null) {
String msg = "[Classifier] " + statusMessagePrefix()
+ " dispatching run/set " + runNum + "/" + (i+1) + " to listeners.";
if (m_log != null) {
m_log.logMessage(msg);
} else {
System.err.println(msg);
}
// dispatch this one
m_outputQueues[runNum - 1][i].setGroupIdentifier(m_currentBatchIdentifier.getTime());
notifyBatchClassifierListeners(m_outputQueues[runNum - 1][i]);
// save memory
m_outputQueues[runNum - 1][i] = null;
// mark as done
m_completedSets[runNum - 1][i] = true;
}
}
}
// scan for completion
boolean done = true;
for (int i = 0; i < maxRunNum; i++) {
for (int j = 0; j < maxSets; j++) {
if (!m_completedSets[i][j]) {
done = false;
break;
}
}
if (!done) {
break;
}
}
if (done) {
String msg = "[Classifier] " + statusMessagePrefix()
+ " last classifier unblocking...";
if (m_log != null) {
m_log.logMessage(msg);
} else {
System.err.println(msg);
}
//m_visual.setText(m_oldText);
if (m_log != null) {
m_log.statusMessage(statusMessagePrefix() + "Finished.");
}
// m_outputQueues = null; // free memory
m_reject = false;
block(false);
m_state = IDLE;
}
}
/*private synchronized void checkCompletedRun(int runNum, int maxRunNum, int maxSets) {
boolean runOK = true;
for (int i = 0; i < maxSets; i++) {
if (m_outputQueues[runNum - 1][i] == null) {
runOK = false;
break;
} else if (m_outputQueues[runNum - 1][i].getClassifier() == null ||
m_outputQueues[runNum - 1][i].getTestSet() == null) {
runOK = false;
break;
}
}
if (runOK) {
String msg = "[Classifier] " + statusMessagePrefix()
+ " dispatching run " + runNum + " to listeners.";
if (m_log != null) {
m_log.logMessage(msg);
} else {
System.err.println(msg);
}
// dispatch this run to listeners
for (int i = 0; i < maxSets; i++) {
notifyBatchClassifierListeners(m_outputQueues[runNum - 1][i]);
// save memory
m_outputQueues[runNum - 1][i] = null;
}
if (runNum == maxRunNum) {
// unblock
msg = "[Classifier] " + statusMessagePrefix()
+ " last classifier unblocking...";
if (m_log != null) {
m_log.logMessage(msg);
} else {
System.err.println(msg);
}
//m_visual.setText(m_oldText);
if (m_log != null) {
m_log.statusMessage(statusMessagePrefix() + "Finished.");
}
// m_outputQueues = null; // free memory
m_reject = false;
block(false);
m_state = IDLE;
}
}
} */
/**
* Sets the visual appearance of this wrapper bean
*
* @param newVisual a BeanVisual
value
*/
public void setVisual(BeanVisual newVisual) {
m_visual = newVisual;
}
/**
* Gets the visual appearance of this wrapper bean
*/
public BeanVisual getVisual() {
return m_visual;
}
/**
* Use the default visual appearance for this bean
*/
public void useDefaultVisual() {
// try to get a default for this package of classifiers
String name = m_ClassifierTemplate.getClass().toString();
String packageName = name.substring(0, name.lastIndexOf('.'));
packageName =
packageName.substring(packageName.lastIndexOf('.')+1,
packageName.length());
if (!m_visual.loadIcons(BeanVisual.ICON_PATH+"Default_"+packageName
+"Classifier.gif",
BeanVisual.ICON_PATH+"Default_"+packageName
+"Classifier_animated.gif")) {
m_visual.loadIcons(BeanVisual.
ICON_PATH+"DefaultClassifier.gif",
BeanVisual.
ICON_PATH+"DefaultClassifier_animated.gif");
}
}
/**
* Add a batch classifier listener
*
* @param cl a BatchClassifierListener
value
*/
public synchronized void
addBatchClassifierListener(BatchClassifierListener cl) {
m_batchClassifierListeners.addElement(cl);
}
/**
* Remove a batch classifier listener
*
* @param cl a BatchClassifierListener
value
*/
public synchronized void
removeBatchClassifierListener(BatchClassifierListener cl) {
m_batchClassifierListeners.remove(cl);
}
/**
* Notify all batch classifier listeners of a batch classifier event
*
* @param ce a BatchClassifierEvent
value
*/
private synchronized void notifyBatchClassifierListeners(BatchClassifierEvent ce) {
Vector l;
synchronized (this) {
l = (Vector)m_batchClassifierListeners.clone();
}
if (l.size() > 0) {
for(int i = 0; i < l.size(); i++) {
((BatchClassifierListener)l.elementAt(i)).acceptClassifier(ce);
}
}
}
/**
* Add a graph listener
*
* @param cl a GraphListener
value
*/
public synchronized void addGraphListener(GraphListener cl) {
m_graphListeners.addElement(cl);
}
/**
* Remove a graph listener
*
* @param cl a GraphListener
value
*/
public synchronized void removeGraphListener(GraphListener cl) {
m_graphListeners.remove(cl);
}
/**
* Notify all graph listeners of a graph event
*
* @param ge a GraphEvent
value
*/
private void notifyGraphListeners(GraphEvent ge) {
Vector l;
synchronized (this) {
l = (Vector)m_graphListeners.clone();
}
if (l.size() > 0) {
for(int i = 0; i < l.size(); i++) {
((GraphListener)l.elementAt(i)).acceptGraph(ge);
}
}
}
/**
* Add a text listener
*
* @param cl a TextListener
value
*/
public synchronized void addTextListener(TextListener cl) {
m_textListeners.addElement(cl);
}
/**
* Remove a text listener
*
* @param cl a TextListener
value
*/
public synchronized void removeTextListener(TextListener cl) {
m_textListeners.remove(cl);
}
/**
* We don't have to keep track of configuration listeners (see the
* documentation for ConfigurationListener/ConfigurationEvent).
*
* @param cl a ConfigurationListener.
*/
public synchronized void addConfigurationListener(ConfigurationListener cl) {
}
/**
* We don't have to keep track of configuration listeners (see the
* documentation for ConfigurationListener/ConfigurationEvent).
*
* @param cl a ConfigurationListener.
*/
public synchronized void removeConfigurationListener(ConfigurationListener cl) {
}
/**
* Notify all text listeners of a text event
*
* @param ge a TextEvent
value
*/
private void notifyTextListeners(TextEvent ge) {
Vector l;
synchronized (this) {
l = (Vector)m_textListeners.clone();
}
if (l.size() > 0) {
for(int i = 0; i < l.size(); i++) {
((TextListener)l.elementAt(i)).acceptText(ge);
}
}
}
/**
* Add an incremental classifier listener
*
* @param cl an IncrementalClassifierListener
value
*/
public synchronized void
addIncrementalClassifierListener(IncrementalClassifierListener cl) {
m_incrementalClassifierListeners.add(cl);
}
/**
* Remove an incremental classifier listener
*
* @param cl an IncrementalClassifierListener
value
*/
public synchronized void
removeIncrementalClassifierListener(IncrementalClassifierListener cl) {
m_incrementalClassifierListeners.remove(cl);
}
/**
* Notify all incremental classifier listeners of an incremental classifier
* event
*
* @param ce an IncrementalClassifierEvent
value
*/
private void
notifyIncrementalClassifierListeners(IncrementalClassifierEvent ce) {
Vector l;
synchronized (this) {
l = (Vector)m_incrementalClassifierListeners.clone();
}
if (l.size() > 0) {
for(int i = 0; i < l.size(); i++) {
((IncrementalClassifierListener)l.elementAt(i)).acceptClassifier(ce);
}
}
}
/**
* Returns true if, at this time,
* the object will accept a connection with respect to the named event
*
* @param eventName the event
* @return true if the object will accept a connection
*/
public boolean connectionAllowed(String eventName) {
/* if (eventName.compareTo("instance") == 0) {
if (!(m_Classifier instanceof weka.classifiers.UpdateableClassifier)) {
return false;
}
} */
if (m_listenees.containsKey(eventName)) {
return false;
}
return true;
}
/**
* Returns true if, at this time,
* the object will accept a connection according to the supplied
* EventSetDescriptor
*
* @param esd the EventSetDescriptor
* @return true if the object will accept a connection
*/
public boolean connectionAllowed(EventSetDescriptor esd) {
return connectionAllowed(esd.getName());
}
/**
* Notify this object that it has been registered as a listener with
* a source with respect to the named event
*
* @param eventName the event
* @param source the source with which this object has been registered as
* a listener
*/
public synchronized void connectionNotification(String eventName,
Object source) {
if (eventName.compareTo("instance") == 0) {
if (!(m_ClassifierTemplate instanceof weka.classifiers.UpdateableClassifier)) {
if (m_log != null) {
String msg = statusMessagePrefix() + "WARNING: "
+ m_ClassifierTemplate.getClass().getName()
+ " Is not an updateable classifier. This "
+"classifier will only be evaluated on incoming "
+"instance events and not trained on them.";
m_log.logMessage("[Classifier] " + msg);
m_log.statusMessage(msg);
}
}
}
if (connectionAllowed(eventName)) {
m_listenees.put(eventName, source);
/* if (eventName.compareTo("instance") == 0) {
startIncrementalHandler();
} */
}
}
/**
* Notify this object that it has been deregistered as a listener with
* a source with respect to the supplied event name
*
* @param eventName the event
* @param source the source with which this object has been registered as
* a listener
*/
public synchronized void disconnectionNotification(String eventName,
Object source) {
m_listenees.remove(eventName);
if (eventName.compareTo("instance") == 0) {
stop(); // kill the incremental handler thread if it is running
}
}
/**
* Function used to stop code that calls acceptTrainingSet. This is
* needed as classifier construction is performed inside a separate
* thread of execution.
*
* @param tf a boolean
value
*/
private synchronized void block(boolean tf) {
if (tf) {
try {
// only block if thread is still doing something useful!
// if (m_state != IDLE) {
wait();
//}
} catch (InterruptedException ex) {
}
} else {
notifyAll();
}
}
/**
* Stop any classifier action
*/
public void stop() {
// tell all listenees (upstream beans) to stop
Enumeration en = m_listenees.keys();
while (en.hasMoreElements()) {
Object tempO = m_listenees.get(en.nextElement());
if (tempO instanceof BeanCommon) {
((BeanCommon)tempO).stop();
}
}
// shutdown the executor pool and reclaim storage
if (m_executorPool != null) {
m_executorPool.shutdownNow();
m_executorPool.purge();
m_executorPool = null;
}
m_reject = false;
block(false);
m_visual.setStatic();
if (m_oldText.length() > 0) {
//m_visual.setText(m_oldText);
}
// stop the build thread
/*if (m_buildThread != null) {
m_buildThread.interrupt();
m_buildThread.stop();
m_buildThread = null;
m_visual.setStatic();
} */
}
public void loadModel() {
try {
if (m_fileChooser == null) {
// i.e. after de-serialization
setupFileChooser();
}
int returnVal = m_fileChooser.showOpenDialog(this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File loadFrom = m_fileChooser.getSelectedFile();
// add extension if necessary
if (m_fileChooser.getFileFilter() == m_binaryFilter) {
if (!loadFrom.getName().toLowerCase().endsWith("." + FILE_EXTENSION)) {
loadFrom = new File(loadFrom.getParent(),
loadFrom.getName() + "." + FILE_EXTENSION);
}
} else if (m_fileChooser.getFileFilter() == m_KOMLFilter) {
if (!loadFrom.getName().toLowerCase().endsWith(KOML.FILE_EXTENSION
+ FILE_EXTENSION)) {
loadFrom = new File(loadFrom.getParent(),
loadFrom.getName() + KOML.FILE_EXTENSION
+ FILE_EXTENSION);
}
} else if (m_fileChooser.getFileFilter() == m_XStreamFilter) {
if (!loadFrom.getName().toLowerCase().endsWith(XStream.FILE_EXTENSION
+ FILE_EXTENSION)) {
loadFrom = new File(loadFrom.getParent(),
loadFrom.getName() + XStream.FILE_EXTENSION
+ FILE_EXTENSION);
}
}
weka.classifiers.Classifier temp = null;
Instances tempHeader = null;
// KOML ?
if ((KOML.isPresent()) &&
(loadFrom.getAbsolutePath().toLowerCase().
endsWith(KOML.FILE_EXTENSION + FILE_EXTENSION))) {
Vector v = (Vector) KOML.read(loadFrom.getAbsolutePath());
temp = (weka.classifiers.Classifier) v.elementAt(0);
if (v.size() == 2) {
// try and grab the header
tempHeader = (Instances) v.elementAt(1);
}
} /* XStream */ else if ((XStream.isPresent()) &&
(loadFrom.getAbsolutePath().toLowerCase().
endsWith(XStream.FILE_EXTENSION + FILE_EXTENSION))) {
Vector v = (Vector) XStream.read(loadFrom.getAbsolutePath());
temp = (weka.classifiers.Classifier) v.elementAt(0);
if (v.size() == 2) {
// try and grab the header
tempHeader = (Instances) v.elementAt(1);
}
} /* binary */ else {
ObjectInputStream is =
new ObjectInputStream(new BufferedInputStream(
new FileInputStream(loadFrom)));
// try and read the model
temp = (weka.classifiers.Classifier)is.readObject();
// try and read the header (if present)
try {
tempHeader = (Instances)is.readObject();
} catch (Exception ex) {
// System.err.println("No header...");
// quietly ignore
}
is.close();
}
// Update name and icon
setTrainedClassifier(temp);
// restore header
m_trainingSet = tempHeader;
if (m_log != null) {
m_log.statusMessage(statusMessagePrefix() + "Loaded model.");
m_log.logMessage("[Classifier] " + statusMessagePrefix()
+ "Loaded classifier: "
+ m_Classifier.getClass().toString());
}
}
} catch (Exception ex) {
JOptionPane.showMessageDialog(Classifier.this,
"Problem loading classifier.\n",
"Load Model",
JOptionPane.ERROR_MESSAGE);
if (m_log != null) {
m_log.statusMessage(statusMessagePrefix() + "ERROR: unable to load " +
"model (see log).");
m_log.logMessage("[Classifier] " + statusMessagePrefix()
+ "Problem loading classifier. "
+ ex.getMessage());
}
}
}
public void saveModel() {
try {
if (m_fileChooser == null) {
// i.e. after de-serialization
setupFileChooser();
}
int returnVal = m_fileChooser.showSaveDialog(this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File saveTo = m_fileChooser.getSelectedFile();
String fn = saveTo.getAbsolutePath();
if (m_fileChooser.getFileFilter() == m_binaryFilter) {
if (!fn.toLowerCase().endsWith("." + FILE_EXTENSION)) {
fn += "." + FILE_EXTENSION;
}
} else if (m_fileChooser.getFileFilter() == m_KOMLFilter) {
if (!fn.toLowerCase().endsWith(KOML.FILE_EXTENSION + FILE_EXTENSION)) {
fn += KOML.FILE_EXTENSION + FILE_EXTENSION;
}
} else if (m_fileChooser.getFileFilter() == m_XStreamFilter) {
if (!fn.toLowerCase().endsWith(XStream.FILE_EXTENSION + FILE_EXTENSION)) {
fn += XStream.FILE_EXTENSION + FILE_EXTENSION;
}
}
saveTo = new File(fn);
// now serialize model
// KOML?
if ((KOML.isPresent()) &&
saveTo.getAbsolutePath().toLowerCase().
endsWith(KOML.FILE_EXTENSION + FILE_EXTENSION)) {
SerializedModelSaver.saveKOML(saveTo,
m_Classifier,
(m_trainingSet != null)
? new Instances(m_trainingSet, 0)
: null);
/* Vector v = new Vector();
v.add(m_Classifier);
if (m_trainingSet != null) {
v.add(new Instances(m_trainingSet, 0));
}
v.trimToSize();
KOML.write(saveTo.getAbsolutePath(), v); */
} /* XStream */ else if ((XStream.isPresent()) &&
saveTo.getAbsolutePath().toLowerCase().
endsWith(XStream.FILE_EXTENSION + FILE_EXTENSION)) {
SerializedModelSaver.saveXStream(saveTo,
m_Classifier,
(m_trainingSet != null)
? new Instances(m_trainingSet, 0)
: null);
/* Vector v = new Vector();
v.add(m_Classifier);
if (m_trainingSet != null) {
v.add(new Instances(m_trainingSet, 0));
}
v.trimToSize();
XStream.write(saveTo.getAbsolutePath(), v); */
} else /* binary */ {
ObjectOutputStream os =
new ObjectOutputStream(new BufferedOutputStream(
new FileOutputStream(saveTo)));
os.writeObject(m_Classifier);
if (m_trainingSet != null) {
Instances header = new Instances(m_trainingSet, 0);
os.writeObject(header);
}
os.close();
}
if (m_log != null) {
m_log.statusMessage(statusMessagePrefix() + "Model saved.");
m_log.logMessage("[Classifier] " + statusMessagePrefix()
+ " Saved classifier " + getCustomName());
}
}
} catch (Exception ex) {
JOptionPane.showMessageDialog(Classifier.this,
"Problem saving classifier.\n",
"Save Model",
JOptionPane.ERROR_MESSAGE);
if (m_log != null) {
m_log.statusMessage(statusMessagePrefix() + "ERROR: unable to" +
" save model (see log).");
m_log.logMessage("[Classifier] " + statusMessagePrefix()
+ " Problem saving classifier " + getCustomName()
+ ex.getMessage());
}
}
}
/**
* Set a logger
*
* @param logger a Logger
value
*/
public void setLog(Logger logger) {
m_log = logger;
}
/**
* Return an enumeration of requests that can be made by the user
*
* @return an Enumeration
value
*/
public Enumeration enumerateRequests() {
Vector newVector = new Vector(0);
if (m_executorPool != null &&
(m_executorPool.getQueue().size() > 0 ||
m_executorPool.getActiveCount() > 0)) {
newVector.addElement("Stop");
}
if ((m_executorPool == null ||
(m_executorPool.getQueue().size() == 0 &&
m_executorPool.getActiveCount() == 0)) &&
m_Classifier != null) {
newVector.addElement("Save model");
}
if (m_executorPool == null ||
(m_executorPool.getQueue().size() == 0 &&
m_executorPool.getActiveCount() == 0)) {
newVector.addElement("Load model");
}
return newVector.elements();
}
/**
* Perform a particular request
*
* @param request the request to perform
* @exception IllegalArgumentException if an error occurs
*/
public void performRequest(String request) {
if (request.compareTo("Stop") == 0) {
stop();
} else if (request.compareTo("Save model") == 0) {
saveModel();
} else if (request.compareTo("Load model") == 0) {
loadModel();
} else {
throw new IllegalArgumentException(request
+ " not supported (Classifier)");
}
}
/**
* Returns true, if at the current time, the event described by the
* supplied event descriptor could be generated.
*
* @param esd an EventSetDescriptor
value
* @return a boolean
value
*/
public boolean eventGeneratable(EventSetDescriptor esd) {
String eventName = esd.getName();
return eventGeneratable(eventName);
}
/**
* @param name of the event to check
* @return true if eventName is one of the possible events
* that this component can generate
*/
private boolean generatableEvent(String eventName) {
if (eventName.compareTo("graph") == 0
|| eventName.compareTo("text") == 0
|| eventName.compareTo("batchClassifier") == 0
|| eventName.compareTo("incrementalClassifier") == 0
|| eventName.compareTo("configuration") == 0) {
return true;
}
return false;
}
/**
* Returns true, if at the current time, the named event could
* be generated. Assumes that the supplied event name is
* an event that could be generated by this bean
*
* @param eventName the name of the event in question
* @return true if the named event could be generated at this point in
* time
*/
public boolean eventGeneratable(String eventName) {
if (!generatableEvent(eventName)) {
return false;
}
if (eventName.compareTo("graph") == 0) {
// can't generate a GraphEvent if classifier is not drawable
if (!(m_Classifier instanceof weka.core.Drawable)) {
return false;
}
// need to have a training set before the classifier
// can generate a graph!
if (!m_listenees.containsKey("trainingSet")) {
return false;
}
// Source needs to be able to generate a trainingSet
// before we can generate a graph
Object source = m_listenees.get("trainingSet");
if (source instanceof EventConstraints) {
if (!((EventConstraints)source).eventGeneratable("trainingSet")) {
return false;
}
}
}
if (eventName.compareTo("batchClassifier") == 0) {
/* if (!m_listenees.containsKey("testSet")) {
return false;
}
if (!m_listenees.containsKey("trainingSet") &&
m_trainingSet == null) {
return false;
} */
if (!m_listenees.containsKey("testSet") &&
!m_listenees.containsKey("trainingSet")) {
return false;
}
Object source = m_listenees.get("testSet");
if (source instanceof EventConstraints) {
if (!((EventConstraints)source).eventGeneratable("testSet")) {
return false;
}
}
/* source = m_listenees.get("trainingSet");
if (source instanceof EventConstraints) {
if (!((EventConstraints)source).eventGeneratable("trainingSet")) {
return false;
}
} */
}
if (eventName.compareTo("text") == 0) {
if (!m_listenees.containsKey("trainingSet") &&
!m_listenees.containsKey("instance")) {
return false;
}
Object source = m_listenees.get("trainingSet");
if (source != null && source instanceof EventConstraints) {
if (!((EventConstraints)source).eventGeneratable("trainingSet")) {
return false;
}
}
source = m_listenees.get("instance");
if (source != null && source instanceof EventConstraints) {
if (!((EventConstraints)source).eventGeneratable("instance")) {
return false;
}
}
}
if (eventName.compareTo("incrementalClassifier") == 0) {
/* if (!(m_Classifier instanceof weka.classifiers.UpdateableClassifier)) {
return false;
} */
if (!m_listenees.containsKey("instance")) {
return false;
}
Object source = m_listenees.get("instance");
if (source instanceof EventConstraints) {
if (!((EventConstraints)source).eventGeneratable("instance")) {
return false;
}
}
}
if (eventName.equals("configuration") && m_Classifier == null) {
return false;
}
return true;
}
/**
* Returns true if. at this time, the bean is busy with some
* (i.e. perhaps a worker thread is performing some calculation).
*
* @return true if the bean is busy.
*/
public boolean isBusy() {
if (m_executorPool == null ||
(m_executorPool.getQueue().size() == 0 &&
m_executorPool.getActiveCount() == 0) && m_state == IDLE) {
return false;
}
/* System.err.println("isBusy() Q:" + m_executorPool.getQueue().size()
+" A:" + m_executorPool.getActiveCount()); */
return true;
}
private String statusMessagePrefix() {
return getCustomName() + "$" + hashCode() + "|"
+ ((m_Classifier instanceof OptionHandler &&
Utils.joinOptions(((OptionHandler)m_Classifier).getOptions()).length() > 0)
? Utils.joinOptions(((OptionHandler)m_Classifier).getOptions()) + "|"
: "");
}
}