Search in sources :

Example 1 with SplitType

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);
}
Also used : PathObjectClassifier(qupath.lib.classifiers.PathObjectClassifier) LinkedHashMap(java.util.LinkedHashMap) PathClass(qupath.lib.objects.classes.PathClass) PathObject(qupath.lib.objects.PathObject) SplitType(qupath.process.gui.ml.legacy.PathClassificationLabellingHelper.SplitType) Normalization(qupath.lib.classifiers.Normalization) ParameterList(qupath.lib.plugins.parameters.ParameterList) List(java.util.List) ArrayList(java.util.ArrayList) Map(java.util.Map) LinkedHashMap(java.util.LinkedHashMap) TreeMap(java.util.TreeMap)

Example 2 with SplitType

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);
}
Also used : PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) ArrayList(java.util.ArrayList) PathClass(qupath.lib.objects.classes.PathClass) PathObject(qupath.lib.objects.PathObject) Random(java.util.Random) SplitType(qupath.process.gui.ml.legacy.PathClassificationLabellingHelper.SplitType) ParameterList(qupath.lib.plugins.parameters.ParameterList) List(java.util.List) ArrayList(java.util.ArrayList)

Aggregations

ArrayList (java.util.ArrayList)2 List (java.util.List)2 PathObject (qupath.lib.objects.PathObject)2 PathClass (qupath.lib.objects.classes.PathClass)2 ParameterList (qupath.lib.plugins.parameters.ParameterList)2 SplitType (qupath.process.gui.ml.legacy.PathClassificationLabellingHelper.SplitType)2 LinkedHashMap (java.util.LinkedHashMap)1 Map (java.util.Map)1 Random (java.util.Random)1 TreeMap (java.util.TreeMap)1 Normalization (qupath.lib.classifiers.Normalization)1 PathObjectClassifier (qupath.lib.classifiers.PathObjectClassifier)1 PathObjectHierarchy (qupath.lib.objects.hierarchy.PathObjectHierarchy)1