Skip to content
zenna7Public

About

Modular Python implementation of SEDC/SEDC-T counterfactual explanations for image classifiers (PyTorch & scikit-learn). Bachelor's thesis project in AI. University of Pavia, University of Milano Bicocca, University of Milano Statale.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

3 Commits

Folders and files

Repository files navigation

SEDCT

A modular, ready-to-use Python implementation of the Search for EviDence Counterfactual algorithm (SEDC and its multiclass variant, SEDC-T) for explaining image classifiers.

Given an image and a trained classifier, SEDCT returns a counterfactual explanation: the smallest set of image regions that, when removed or replaced, flips the model's prediction. Instead of asking "why did the model predict this?", it answers "what would have to change in this image for the model to predict something else?" — a more human-interpretable framing than most post-hoc explainability methods.

Example SEDCT counterfactual on the Cats vs. Dogs dataset: base image, segmented overlays, and the resulting counterfactual regions at increasing segment counts

This library was developed as part of a Bachelor's thesis in Artificial Intelligence, "Counterfactual Explanations for Image Classification: Implementation of a State of the Art Algorithm" (Marco Zennaro, supervised by Marco Calautti, University of Milan, 2025). The full thesis and defense presentation are included in docs/.

Table of Contents

How it works

SEDCT operates in three steps:

  1. Segmentation — the input image is split into segments (e.g. via superpixel algorithms), using a method of the user's choice.
  2. Replacement strategy — the user chooses how a removed segment is filled in (e.g. mean color, blur, a fixed value).
  3. Iterative search — at each iteration, the algorithm removes a candidate segment (or growing combination of segments) and re-classifies the resulting image. If the prediction flips, a counterfactual has been found. The search is greedy: it always expands the combination that has moved the classification score the most so far, without guaranteeing a globally optimal (i.e. smallest possible) result.

SEDC-T extends this to multiclass classifiers by letting the user specify a target class — the algorithm searches for the minimal perturbation that pushes the prediction specifically toward that class, rather than any class other than the original.

Project structure

SEDCT/
├── sedct/                          # the library
│   ├── __init__.py
│   ├── explainer.py                # SEDCTExplainer — runs the algorithm
│   ├── model_wrappers.py           # BaseModelWrapper, SKLearnModelWrapper, TorchModelWrapper
│   └── segmentation_wrappers.py    # BaseImageSegmentation, SKLearnImageSegmentation, TorchImageSegmentation
├── examples/                       # runnable notebooks showcasing the library
│   ├── classes_showcase.ipynb
│   ├── tests_sedct.ipynb
│   └── Models/                     # pretrained weights used by the examples (Git LFS)
├── notebooks/
│   └── data_collection_and_visualization.ipynb   # trains the demo classifiers from scratch
├── docs/
│   ├── thesis.pdf                  # full bachelor's thesis
│   ├── presentation.pdf            # thesis defense slides
│   └── sedc-sedct-article.pdf      # original SEDC / SEDC-T paper
├── pyproject.toml
├── requirements.txt
└── README.md

The library is deliberately split into three cooperating classes, following the same modularity described in the thesis (Ch. 3.2):

  • BaseImageSegmentation (and its TorchImageSegmentation / SKLearnImageSegmentation subclasses) handles image formats, segmentation, and segment replacement.
  • BaseModelWrapper (and its TorchModelWrapper / SKLearnModelWrapper subclasses) wraps a classifier so the algorithm can query predictions and scores without caring which framework trained the model.
  • SEDCTExplainer ties the two together and runs the SEDC/SEDC-T search.

Class diagram showing the relationships between SEDCTExplainer, BaseModelWrapper (with TorchModelWrapper / SKLearnModelWrapper), and BaseImageSegmentation (with TorchImageSegmentation / SKLearnImageSegmentation)

Installation

git clone https://github.com/zenna7/SEDCT.git
cd SEDCT
pip install -e .

This installs sedct as an importable package (editable mode, so local changes take effect immediately) along with its core dependencies (PyTorch, scikit-learn, NumPy, Pillow).

Some example notebooks additionally use TensorFlow, torchvision, scikit-image, and Matplotlib — install everything needed to run those with:

pip install -r requirements.txt

Usage

from sedct import SEDCTExplainer, TorchModelWrapper, TorchImageSegmentation

# Wrap your trained PyTorch model
classifier = TorchModelWrapper(model=my_model, transform=my_preprocessing)

# Wrap the image and choose a segmentation + replacement strategy
image_segmentation = TorchImageSegmentation(
    image=my_image,
    segmentation_method="slic",
    fill_method="mean",
)

# Run the explainer
explainer = SEDCTExplainer(classifier=classifier, image_segmentation=image_segmentation)
explainer.explain_instance(show=True)

counterfactual = explainer.get_counterfactual()
mask = explainer.get_counterfactual_mask()

For multiclass models, pass target=<class_index> to explain_instance to search for a counterfactual toward a specific class (the SEDC-T variant).

See examples/ for complete, runnable notebooks against real trained classifiers.

Supported frameworks

The wrapper-based design means SEDCT isn't tied to one ML framework:

  • Models: PyTorch (TorchModelWrapper) and scikit-learn (SKLearnModelWrapper) classifiers, binary or multiclass.
  • Images: multiple input formats supported via TorchImageSegmentation / SKLearnImageSegmentation (tensors, arrays, PIL images).

Extending SEDCT to a new framework means subclassing BaseModelWrapper or BaseImageSegmentation and implementing a handful of abstract methods (get_pred, get_score, compute_segmentation, remove_segment, etc.) — the core algorithm itself doesn't need to change.

Examples

examples/ contains notebooks that train and load real classifiers (a ResNet18 fine-tuned on Cats vs. Dogs, a multiclass ResNet18 on tf_flowers, and scikit-learn pipelines) and run SEDCT against them end-to-end. notebooks/data_collection_and_visualization.ipynb trains the MNIST/CIFAR-10/Cats-vs-Dogs classifiers used elsewhere in the thesis experiments from scratch, if you want to reproduce those instead of using the pretrained weights in examples/Models/.

Limitations

  • The search is greedy, not exhaustive — it isn't guaranteed to find the smallest possible counterfactual, only a locally optimal one at each step.
  • Runtime scales with the number of segments and how many need to be combined before a flip is found; max_iter in explain_instance caps this at the cost of a possible "no counterfactual found" result.
  • Explanation quality depends heavily on the chosen segmentation method and replacement strategy — there's no single best default across all image types.

A fuller discussion of these trade-offs is in the thesis, Section 3.4.2.

Documentation

  • docs/thesis.pdf — full write-up: literature review, algorithm design, implementation details, and experimental results on MNIST, CIFAR-10, and Cats vs. Dogs.
  • docs/presentation.pdf — condensed defense presentation.
  • docs/sedc-sedct-article.pdf — the original paper introducing SEDC/SEDC-T that this implementation is based on.

License

MIT — see LICENSE.

Citation

If you use this library, please cite the accompanying thesis:

Marco Zennaro, "Counterfactual Explanations for Image Classification:
Implementation of a State of the Art Algorithm", Bachelor's Thesis,
Bachelor in Artificial Intelligence, 2025.

About

Modular Python implementation of SEDC/SEDC-T counterfactual explanations for image classifiers (PyTorch & scikit-learn). Bachelor's thesis project in AI. University of Pavia, University of Milano Bicocca, University of Milano Statale.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages