Contributing to the transparency module¶
This page describes the internal transparency architecture and how to extend it with new algorithms, frameworks, and visualisers.
Overview¶
The transparency module wraps XAI frameworks (Captum, SHAP, optional Alibi) behind a unified interface driven by Hydra _target_ instantiation. Explainers produce an ExplanationResult; visualisers render attribution tensors to PNG on disk.
Explainers form a three-level hierarchy (see src/raitap/transparency/explainers/base_explainer.py and full_explainer.py):
AbstractExplainer # root — owns output_payload_kind + check_backend_compat no-op
├── AttributionOnlyExplainer # you implement compute_attributions(); framework owns explain()
│ ├── CaptumExplainer
│ └── ShapExplainer
└── FullExplainer # you implement the full explain() pipeline end-to-end
└── AlibiExplainer
AbstractExplainer— root base class. Owns the shared contract:output_payload_kindclass variable (defaultATTRIBUTIONS) and thecheck_backend_compatno-op. Never subclass directly.AttributionOnlyExplainer— extend this when the framework should manage the fullexplainpipeline. Subclasses implement onlycompute_attributions(model, inputs, **kwargs) → Tensor; batching, normalisation, result wrapping, andwrite_artifactsare handled by this class.FullExplainer— extend this when you own the entireexplainpipeline yourself (data conversion, model invocation, result construction, persistence). Used for Alibi, whose API does not map to a simple tensor-in/tensor-out attribution step.
Each explainer class sets output_payload_kind: ClassVar[ExplanationPayloadKind] (default ATTRIBUTIONS). ExplanationResult stores payload_kind and includes it in metadata.json.
All visualisers implement BaseVisualiser, which defines:
visualise(attributions, inputs, **kwargs) -> Figure(abstract, required)save(attributions, output_path, inputs, **kwargs) -> None(optional, has default implementation)compatible_algorithms: frozenset[str](empty = all algorithms)supported_payload_kinds: ClassVar[frozenset[ExplanationPayloadKind]]— default{ATTRIBUTIONS}. An emptyfrozenset()means the visualiser accepts all payload kinds (wildcard). The factory raisesPayloadVisualiserIncompatibilityErrorif the explainer’soutput_payload_kindis not listed when the set is non-empty.
After create_explainer, factory.Explanation may emit third-party license warnings (e.g. Alibi BSL) at most once per process via logging.warning.
Important files¶
The factory.py module provides the Explanation class and helper functions, which use Hydra’s instantiate() to build explainers and visualisers from _target_ keys. Bare class names are automatically resolved to raitap.transparency.* paths.
Runtime flow¶
Transparency runs after the forward pass in src/raitap/run/pipeline.py. For each configured explainer:
Explanation(config, name, model, data)creates the explainer and visualisers using the factory functionsThe explainer’s
explain()method returns anExplanationResult(forAttributionOnlyExplainer, after callingcompute_attributions())ExplanationResult.write_artifacts()saves attributions and metadata to disk (payload_kindis recorded in metadata)ExplanationResult.visualise()iterates through configured visualisers, calling each one’svisualise()method and saving the figures
Each explainer writes to its own subdirectory under the Hydra run folder. See Understanding outputs for more details.
Adding a new algorithm¶
Captum and SHAP wrappers dispatch to algorithms dynamically via getattr, so most new methods require no code changes. Override the algorithm on a specific explainer entry in your transparency config:
uv run raitap transparency=demo transparency.captum_ig.algorithm=Saliency
uv run raitap transparency=demo transparency.shap_gradient.algorithm=GradientShap
Add an integration test to confirm the method works end-to-end. Reference src/raitap/transparency/explainers/tests/test_captum_explainer.py for examples.
Some SHAP methods require special init logic. Check src/raitap/transparency/explainers/shap_explainer.py for conditionals and add a branch if needed.
Alibi Explain¶
Class:
AlibiExplainer(FullExplainer). Algorithms:KernelShap(PyTorchnn.Moduleblack-box, default inalibi_kernel.yaml),TreeShap(fitted tree-based model — sklearn/XGBoost/LightGBM/CatBoost, pass viaconstructor: {tree_model: ...}), andIntegratedGradients(TensorFlow/Keras only — passkeras_modelin Hydraconstructor).Licensing: Alibi is BSL 1.1, not GPLv3. See Alibi (transparency) and the one-time
logging.warningfromfactory._maybe_emit_third_party_license_warningswhenALIBI_BSL_LICENSE_WARNINGis true on the explainer class.Installation (this repo):
uv syncwith--extra alibi; the rootpyproject.tomlalready supplies[tool.uv]overrides, so you do not add them manually. Downstream projects that depend onraitap[alibi]must mirror those overrides — see Alibi (transparency).Tests:
src/raitap/transparency/explainers/tests/test_alibi_explainer.pyusesneeds_alibiand skips whenalibiis not installed.
Alibi algorithms not currently supported¶
The following Alibi explainers are intentionally absent. This section documents the blockers so future contributors know what needs to change before they can be added.
AnchorTabular, AnchorImage, AnchorText, DistributedAnchorTabular
These produce structured explanations (anchor feature conditions, precision, coverage) rather than attribution tensors. ExplanationResult currently requires an attributions: torch.Tensor and write_artifacts() raises NotImplementedError for ExplanationPayloadKind.STRUCTURED. Before adding any Anchor method: (1) make attributions optional on ExplanationResult, (2) implement STRUCTURED persistence (a JSON dump of the anchor result), and (3) fix the hardcoded attributions.pt copy in ExplanationResult.log(). AnchorImage and AnchorText also pull in extra-heavy dependencies (image segmentation / spaCy) that do not belong in the [alibi] optional extra without deliberate scoping.
ALE (Accumulated Local Effects)
ALE is a global, population-level explanation method. Its output is one effect curve per feature across the dataset — not a tensor shaped like the inputs. It does not fit the per-sample attribution model that ExplanationResult expects. Supporting it properly would require a different result type (or a well-defined mapping from global ALE curves to per-sample estimates, which is non-standard).
CEM, CounterFactual, CounterFactualProto
All three are TensorFlow/Keras-only and produce counterfactual instances (STRUCTURED output), not attribution tensors. Adding them would require (1) TensorFlow as a hard dependency inside [alibi] — a significant architectural choice — and (2) STRUCTURED persistence (same blocker as Anchor methods).
Adding a new framework¶
To integrate a new explainability framework:
Implement the wrapper
Prefer
AttributionOnlyExplainerwhen the library maps tocompute_attributions(model, inputs, ...) -> torch.Tensor. Otherwise subclassFullExplainerand implementexplain(...)end-to-end (seealibi_explainer.py).Create a new explainer class under
src/raitap/transparency/explainers/:# src/raitap/transparency/explainers/new_framework_explainer.py from .base_explainer import AttributionOnlyExplainer import torch class NewFrameworkExplainer(AttributionOnlyExplainer): # Optional: Define ONNX-compatible algorithms ONNX_COMPATIBLE_ALGORITHMS: frozenset[str] = frozenset({...}) def __init__(self, algorithm: str, **init_kwargs): super().__init__() self.algorithm = algorithm self.init_kwargs = init_kwargs def check_backend_compat(self, backend: object) -> None: """Optional: Validate algorithm compatibility with backend (e.g., ONNX).""" # See CaptumExplainer for reference implementation pass def compute_attributions( self, model: torch.nn.Module, inputs: torch.Tensor, backend: object | None = None, **kwargs ) -> torch.Tensor: """Required: Compute attributions and return torch.Tensor.""" # Your implementation here pass
Reference
src/raitap/transparency/explainers/captum_explainer.pyorshap_explainer.pyfor complete examples.Export from
__init__.pyExport the class from both the explainers package and the top-level transparency package:
# src/raitap/transparency/explainers/__init__.py from .new_framework_explainer import NewFrameworkExplainer __all__ = [..., "NewFrameworkExplainer"]
# src/raitap/transparency/__init__.py from .explainers import NewFrameworkExplainer __all__ = [..., "NewFrameworkExplainer"]
Create a config preset
Add a config file under
src/raitap/configs/transparency/:# src/raitap/configs/transparency/new_framework.yaml my_explainer: _target_: NewFrameworkExplainer algorithm: SomeAlgorithm visualisers: - _target_: CaptumImageVisualiser
Use it
uv run raitap transparency=new_framework uv run raitap transparency=new_framework transparency.my_explainer.algorithm=AnotherAlgorithm
Update documentation
Add the new framework to
docs/modules/transparency/frameworks-and-libraries.mdwith supported algorithms, ONNX compatibility notes, and visualiser compatibility.
Adding a visualiser¶
To add a new visualiser:
Implement the visualiser
Create a new visualiser class that extends
BaseVisualiserinsrc/raitap/transparency/visualisers/:# src/raitap/transparency/visualisers/new_visualiser.py from .base_visualiser import BaseVisualiser import torch from matplotlib.figure import Figure class NewVisualiser(BaseVisualiser): # Optional: Restrict to specific algorithms (empty = compatible with all) compatible_algorithms: frozenset[str] = frozenset() # Optional: restrict payload kinds (empty frozenset = all kinds) # supported_payload_kinds: ClassVar[frozenset[ExplanationPayloadKind]] = frozenset({ExplanationPayloadKind.ATTRIBUTIONS}) def __init__(self, **config_kwargs): super().__init__() # Store configuration def visualise( self, attributions: torch.Tensor, inputs: torch.Tensor | None = None, **kwargs ) -> Figure: """Required: Create and return a matplotlib Figure.""" # Your implementation here pass def save( self, attributions: torch.Tensor, output_path: str | Path, inputs: torch.Tensor | None = None, **kwargs ) -> None: """Optional: Override for custom save logic (default uses visualise()).""" # Default implementation in BaseVisualiser usually sufficient super().save(attributions, output_path, inputs, **kwargs)
Reference
src/raitap/transparency/visualisers/captum_visualisers.pyorshap_visualisers.pyfor complete examples.Export from
__init__.pyExport the class from both the visualisers package and the top-level transparency package:
# src/raitap/transparency/visualisers/__init__.py from .new_visualiser import NewVisualiser __all__ = [..., "NewVisualiser"]
# src/raitap/transparency/__init__.py from .visualisers import NewVisualiser __all__ = [..., "NewVisualiser"]
Use it
Override the visualisers list on the CLI:
uv run raitap "transparency.my_explainer.visualisers=[{_target_: NewVisualiser}]"
Or embed it in a custom transparency config:
# src/raitap/configs/transparency/custom.yaml _target_: CaptumExplainer algorithm: IntegratedGradients visualisers: - _target_: NewVisualiser
uv run raitap transparency=custom
Extension points¶
To add dataset configs, see the data module contributor documentation. Dataset handling is separate from transparency.