use of qupath.process.gui.ml.legacy.PathClassificationLabellingHelper.SplitType in project qupath by qupath.
the class ClassifierBuilderPane method crossValidateAcrossImages.
private void crossValidateAcrossImages() {
// Try to put the current image data information into the tempMap, which stores training data separated by image path
updateRetainedObjectsMap();
Map<String, Map<PathClass, List<PathObject>>> tempMap = new LinkedHashMap<>(retainedObjectsMap.getMap());
Normalization normalization = (Normalization) paramsUpdate.getChoiceParameterValue("normalizationMethod");
for (String key : tempMap.keySet()) {
Map<PathClass, List<PathObject>> validationMap = tempMap.get(key);
Map<PathClass, List<PathObject>> trainingMap = new LinkedHashMap<>();
for (Entry<String, Map<PathClass, List<PathObject>>> entry : tempMap.entrySet()) {
if (entry.getKey().equals(key))
continue;
for (Entry<PathClass, List<PathObject>> entry2 : entry.getValue().entrySet()) {
if (trainingMap.containsKey(entry2.getKey())) {
trainingMap.get(entry2.getKey()).addAll(entry2.getValue());
} else {
trainingMap.put(entry2.getKey(), new ArrayList<>(entry2.getValue()));
}
}
}
// Perform subsampling
SplitType splitType = (SplitType) paramsUpdate.getChoiceParameterValue("splitType");
double maxTrainingProportion = paramsUpdate.getIntParameterValue("maxTrainingPercent") / 100.;
long seed = paramsUpdate.getIntParameterValue("randomSeed");
trainingMap = PathClassificationLabellingHelper.resampleClassificationMap(trainingMap, splitType, maxTrainingProportion, seed);
// Get the current classifier - unfortunately, there's no easy way to duplicate/create a new one,
// so we are left working with the 'live' classifier
PathObjectClassifier classifier = (T) comboClassifiers.getSelectionModel().getSelectedItem();
classifier.updateClassifier(trainingMap, featurePanel.getSelectedFeatures(), normalization);
int nCorrect = 0;
int nTested = 0;
for (Entry<PathClass, List<PathObject>> entryValidation : validationMap.entrySet()) {
classifier.classifyPathObjects(entryValidation.getValue());
for (PathObject temp : entryValidation.getValue()) {
if (entryValidation.getKey().equals(temp.getPathClass()))
nCorrect++;
nTested++;
}
}
double percent = nCorrect * 100.0 / nTested;
logger.info(String.format("Percentage correct for %s: %.2f%%", key, percent));
System.err.println(String.format("Percentage correct for %s: %.2f%% (%d/%d)", key, percent, nCorrect, nTested));
}
// Force a normal classifier update, to compensate for the fact we had to modify the 'live' classifier
updateClassification(false);
}
use of qupath.process.gui.ml.legacy.PathClassificationLabellingHelper.SplitType in project qupath by qupath.
the class ClassifierBuilderPane method updateClassification.
private synchronized void updateClassification(boolean interactive) {
PathObjectHierarchy hierarchy = getHierarchy();
if (hierarchy == null) {
if (interactive)
Dialogs.showErrorMessage("Classification error", "No objects available to classify!");
btnSaveClassifier.setDisable(!classifier.isValid());
return;
}
List<String> features = featurePanel.getSelectedFeatures();
// If we've no features, default to trying to get
if (features.isEmpty() && interactive) {
selectAllFeatures();
features = featurePanel.getSelectedFeatures();
if (features.size() == 1)
Dialogs.showInfoNotification("Feature selection", "Classifier set to train using the only available feature");
else if (!features.isEmpty())
Dialogs.showInfoNotification("Feature selection", "Classifier set to train using all " + features.size() + " available features");
}
// If still got no features, we're rather stuck
if (features.isEmpty()) {
Dialogs.showErrorMessage("Classification error", "No features available to use for classification!");
btnSaveClassifier.setDisable(classifier == null || !classifier.isValid());
return;
}
updatingClassification = true;
// Get training map
double maxTrainingProportion = paramsUpdate.getIntParameterValue("maxTrainingPercent") / 100.;
long seed = paramsUpdate.getIntParameterValue("randomSeed");
SplitType splitType = (SplitType) paramsUpdate.getChoiceParameterValue("splitType");
Map<PathClass, List<PathObject>> map = getTrainingMap();
// Apply limit if needed
if (paramsUpdate.getBooleanParameterValue("limitTrainingToRepresentedClasses")) {
Set<PathClass> representedClasses = map.keySet();
for (List<PathObject> values : map.values()) {
Iterator<PathObject> iter = values.iterator();
while (iter.hasNext()) {
PathClass pathClass = iter.next().getPathClass();
if (pathClass != null && !representedClasses.contains(pathClass.getBaseClass()))
iter.remove();
}
}
}
// TODO: The order of entries in the map is not necessarily consistent (e.g. when a new annotation is added to the hierarchy -
// irrespective of whether or not it has a classification). Consequently, classifiers that rely on 'randomness' (e.g. random forests...)
// can give different results for the same training data. With 'auto-update' selected, this looks somewhat disturbing...
Map<PathClass, List<PathObject>> mapTraining = PathClassificationLabellingHelper.resampleClassificationMap(map, splitType, maxTrainingProportion, seed);
if (mapTraining.size() <= 1) {
logger.error("Training samples from at least two different classes required to train a classifier!");
updatingClassification = false;
return;
}
// Try to create a separate test map, if we can
Map<PathClass, List<PathObject>> mapTest = map;
boolean testOnTrainingData = true;
if (mapTraining != map) {
for (Entry<PathClass, List<PathObject>> entry : mapTraining.entrySet()) {
mapTest.get(entry.getKey()).removeAll(entry.getValue());
logger.info("Number of training samples for " + entry.getKey() + ": " + entry.getValue().size());
}
testOnTrainingData = false;
}
// Balance the classes for training, if necessary
if (paramsUpdate.getBooleanParameterValue("balanceClasses")) {
logger.debug("Balancing classes...");
int maxSize = -1;
for (List<PathObject> temp : mapTraining.values()) maxSize = Math.max(maxSize, temp.size());
Random random = new Random(seed);
for (PathClass key : mapTraining.keySet()) {
List<PathObject> temp = mapTraining.get(key);
int size = temp.size();
if (maxSize == size)
continue;
// Ensure a copy is made
List<PathObject> list = new ArrayList<>(temp);
for (int i = 0; i < maxSize - size; i++) {
list.add(temp.get(random.nextInt(size)));
}
mapTraining.put(key, list);
}
}
BackgroundClassificationTask task = new BackgroundClassificationTask(hierarchy, features, mapTraining, mapTest, testOnTrainingData);
qupath.submitShortTask(task);
// doClassification(hierarchy, features, mapTraining, mapTest, testOnTrainingData);
}
Aggregations