Contributing to the transparency module¶
Generic adapter mechanics are covered elsewhere:
New library wrapper: Adding an adapter.
New algorithm on an existing wrapper: Adding an algorithm.
New top-level module: Adding a module.
This page documents what's specific to transparency: the explainer hierarchy, the visualiser semantic contract, and the typed ExplanationResult.semantics.
Explainer hierarchy¶
Explainers form a three-level hierarchy (see src/raitap/transparency/explainers/base_explainer.py and full_explainer.py):
BaseExplainer # root: owns output_payload_kind + algorithm_registry contract
├── AttributionOnlyExplainer # you implement compute_attributions(); base owns explain()
│ ├── CaptumExplainer
│ └── ShapExplainer
└── FullExplainer # you implement the full explain() pipeline end-to-end
BaseExplainer: root base class. Ownsoutput_payload_kind: ClassVar[ExplanationPayloadKind](defaultATTRIBUTIONS) and thealgorithm_registrycontract. Backend gating is inherited fromAdapterMixin(check_backend_compat), not declared here. Per-algorithm capability requirements live onExplainerAlgorithmSpec.requires. Never subclass directly.AttributionOnlyExplainer: extend this when the framework maps cleanly to a singlecompute_attributions(model, inputs, **kwargs) -> torch.Tensorcall. Batching, normalisation, result wrapping, andwrite_artifactsare handled for you. Captum and SHAP both subclass this.FullExplainer: extend this when you own the entireexplainpipeline yourself (data conversion, model invocation, result construction, persistence).
output_payload_kind: ClassVar[ExplanationPayloadKind] (default ATTRIBUTIONS) records what
artefact shape the explainer emits. It's set via the @adapters.transparency decorator kwarg.
Baseline contract (reference-input methods)¶
Methods that take a reference input (IG baselines=, SHAP background_data=) document that
baseline in metadata.json + the report (issue #210). The user sets it library-agnostically via
raitap.baseline, routed to the adapter's own kwarg. Three declarations on the adapter drive this:
Declaration |
Where |
Purpose |
|---|---|---|
|
|
The call kwarg holding the reference ( |
|
per-algorithm |
Per-algorithm implicit default mode ( |
|
per-algorithm |
|
|
per-algorithm |
|
Capture happens once at the AttributionOnlyExplainer.explain chokepoint via build_baseline_record
(transparency/baselines.py), which resolves the BaselineMode (configured / user_tensor /
zero / input_batch), hashes the tensor, and renders an image preview. It is wrapped so a
render/hash failure degrades to no baseline rather than discarding attributions.
See Adding an algorithm.
Structured payloads (extra tuple outputs)¶
Some library methods return a tuple: the principal attribution plus extra
diagnostics (e.g. Captum's return_convergence_delta=True appends a delta). To
capture these as first-class payloads, declare one StructuredOutputSpec per
extra positional element on the algorithm's ExplainerAlgorithmSpec.extra_outputs:
from raitap.transparency.contracts import (
ExplainerAlgorithmSpec,
StructuredOutputSpec,
StructuredPayloadKind,
)
ExplainerAlgorithmSpec(
{MethodFamily.GRADIENT},
extra_outputs=(
StructuredOutputSpec("convergence_delta", StructuredPayloadKind.CONVERGENCE_DELTA),
),
)
Tuple position 0 is always the principal attribution. Positions 1.. map
positionally to the declared specs. If the method returns a tuple but the count
of extras does not match extra_outputs, the framework raises a ValueError
naming the mismatch, so an undeclared extra output never passes silently. The
captured payloads are persisted to payloads/<name>.pt and described in
metadata.json; see Output. StructuredPayloadKind
(CONVERGENCE_DELTA, BASE_VALUE) and StructuredOutputSpec live in
raitap.transparency.contracts.
To consume payloads in a visualiser, declare
supported_structured_payload_kinds={StructuredPayloadKind.CONVERGENCE_DELTA}
(the kinds it needs) via the @visualisers.transparency decorator kwarg, then
read context.structured_payloads in visualise(). validate_explanation
rejects the visualiser when the explanation lacks a declared kind. The registered
StructuredPayloadSummaryVisualiser (structured_payload_summary) is the
reference renderer.
ShapExplainer internals¶
ShapExplainer.compute_attributions builds an AttributionInvokeCtx and dispatches via the
invoker field on each registry entry (ExplainerAlgorithmSpec.invoker, added in #266). An
unknown algorithm name produces a None entry, falling back to _shap_legacy_invoker which raises
the helpful "unsupported algorithm" ValueError.
Legacy path (
_shap_legacy_invoker->_compute_legacy->.shap_values()API):GradientExplainer,DeepExplainer,KernelExplainer,TreeExplainer,SamplingExplainer. Constructs the SHAP explainer with the background tensor and calls.shap_values()directly.Modern path (
_shap_modern_invoker->_compute_modern->__call__ -> ExplanationAPI):PartitionExplainer,ExactExplainer,PermutationExplainer. Calls_build_maskerto select a per-modality masker, wraps the predict callable via_modern_predict_fn, constructs the explainer, and calls it with numpy inputs.
_build_masker¶
Selects the masker based on input_spec.kind:
IMAGE:shap.maskers.Image("inpaint_telea", (h, w, c)). Requiresopencv-python(included in theshapextra) and an NCHW shape ininput_spec.TABULAR:shap.maskers.Partition(background_np).
Other modalities raise ValueError. input_spec is threaded into compute_attributions from the
AttributionOnlyExplainer.explain chokepoint via infer_input_spec.
_normalise_modern_explanation¶
Maps the raw Explanation.values (class-last layout) to an input-shaped float32 tensor:
Cast to float32.
Select the target class with
_select_target_attributions(shared with the legacy path).For image inputs with a class-selected 4-D result, permute NHWC to NCHW to match RAITAP's tensor convention.
Visualiser semantic contract¶
All visualisers extend BaseVisualiser (src/raitap/transparency/visualisers/base_visualiser.py).
On top of visualise(...) -> Figure and the optional save(...), each visualiser must declare its
semantic compatibility via ClassVars. The runtime validates these against ExplanationResult.semantics
before calling visualise.
ClassVar |
Type |
Purpose |
|---|---|---|
|
|
Payload categories the visualiser can render. |
|
|
Explanation scopes the visualiser can consume (e.g. |
|
|
Attribution coordinate spaces the visualiser handles. |
|
|
Method families the visualiser understands. |
|
|
Optional algorithm allowlist; empty = all algorithms. |
|
|
Structured payload kinds the visualiser can render. Non-empty means |
|
|
Set only when the visualiser changes the result scope (e.g. summarising local to aggregated). Leave |
|
|
Where the produced scope was defined; set when |
|
|
Metadata for summary visualisations. |
|
|
Whether the normal layout includes an original-input panel alongside the rendered explanation. |
Plus two instance-level hooks:
renders_attribution_only_when_original_hidden() -> bool: whetherinclude_original_input=Falsestill yields a meaningful figure.validate_explanation(explanation, attributions, inputs) -> None: render-time compatibility validation.
Rules of thumb
Per-sample renderers (heatmap, overlay): preserve scope, leave
produces_scope = None.Summary renderers (SHAP bar/beeswarm, tabular bar): consume local attributions and produce
AGGREGATED. Setproduces_scope = ExplanationScope.AGGREGATEDandscope_definition_step = ScopeDefinitionStep.VISUALISER_SUMMARY.Don't promote arbitrary debug batches or representative montages to
GLOBAL.Image visualisers with
embeds_original_input = Truemust accept the runtime kwarginclude_original_input. Reporting uses this to render one shared sample thumbnail and suppress repeated originals in sample-major compact local report sections. Keep YAML constructor names backward-compatible (the built-in image visualisers still acceptinclude_original_image).
For the decorator/registration scaffolding (@visualisers.transparency, registry_name, exports),
see Adding an adapter. The bullets above are the transparency-specific additions on top of
that scaffolding.
Adding an image renderer¶
visualise() forwards style kwargs to the resolved renderer's draw(**style). Any renderer
registered via @image_renderer must accept **style (the ImageAttributionRenderer
protocol declares it); a renderer that omits it raises TypeError once a user sets method.
To participate in the unhonoured-field warning, declare the optional honours_method (bool)
and honoured_signs (frozenset[str]) class constants. They are read via getattr with
honour-all defaults, so they are not required.
Typed semantics contract¶
ExplanationResult.semantics describes the computed explanation artefact. It is a typed contract,
not a narrative description. The contract records the artefact scope, scope definition step, payload
kind, method families, target, sample selection, input metadata, and output-space metadata.
ExplanationScope¶
Describes the semantic breadth of an explanation or rendered visualisation:
Scope |
Meaning |
|---|---|
|
Explains individual input samples. Current Captum and SHAP attribution explainers produce local explanation artefacts. |
|
Summarises the selected input batch. Current SHAP bar, SHAP beeswarm, and tabular bar visualisers produce aggregated visual summaries when they aggregate local attributions. |
|
Represents a dataset, population, or model-wide result. The enum keeps this concept available, but built-in visualisers do not promote arbitrary batches to global outputs. |
The AGGREGATED scope distinction is intentional. A SHAP plotting API may call a bar or beeswarm
figure "global", but RAITAP only treats it as global when a first-class dataset, population, or
model-level contract proves that scope.
ScopeDefinitionStep¶
Records where the scope was defined:
Step |
Meaning |
|---|---|
|
The explainer produced an artefact with this scope. |
|
The visualiser changed the result scope by summarising another explanation artefact. |
For example, an attribution explainer produces local attributions with EXPLAINER_OUTPUT. A summary
visualiser consumes those local attributions and produces an aggregated figure with
VISUALISER_SUMMARY.
VisualisationResult.scope describes what the rendered figure represents. Reporting placement comes
from this rendered visualisation scope, not from legacy report-placement strings.
ExplanationOutputSpace¶
Describes what attribution values are aligned to:
Output space |
Typical use |
|---|---|
|
Attributions aligned with input features, pixels, or tabular columns. |
|
Attributions aligned with an interpretable feature representation. |
|
Attributions aligned with an internal model layer. |
|
CAM-style or spatial image maps that may need interpolation. |
|
Token-level text attributions. |
Output-space inference relies on explicit input metadata and algorithm semantics. Shape alone is not enough to decide whether a tensor is tabular, token, image, or time-series data.
LAYER_ACTIVATION inference branch. When layer_path is set and MethodFamily.CAM is not in the resolved method families, infer_output_space short-circuits to LAYER_ACTIVATION (skipping input-shape validation). CAM methods (LayerGradCam, GuidedGradCam) match the earlier CAM branch first and produce IMAGE_SPATIAL_MAP for image input, so they never reach this branch.
_needs_layer_resolution prefix rule. Any algorithm whose name starts with Layer (e.g. LayerConductance, LayerIntegratedGradients) or equals GuidedGradCam triggers layer resolution: the layer_path string in constructor is resolved to a live nn.Module before the Captum object is constructed. This happens automatically in CaptumExplainer; there is no extra flag to set.
Sample identity vs display labels¶
RAITAP separates stable sample identity from display labels:
Field |
Purpose |
|---|---|
|
Stable IDs from the data pipeline, when available. |
|
Optional labels used for plot titles. |
Display names are not stable identity. RAITAP must not infer dataset, population, or global semantics from sample names shown in plots.
Runtime flow¶
Transparency runs after the forward pass via src/raitap/transparency/phase.py
(assess_transparency). For each configured explainer:
Explanation(config, name, model, data)instantiates the explainer and its visualisers via hydra-zen.explainer.explain()returns anExplanationResult(forAttributionOnlyExplainer, after callingcompute_attributions()).ExplanationResult.write_artifacts()persists attributions and typed semantics to disk.ExplanationResult.visualise()iterates the configured visualisers, validates each one against the explanation semantics, callsvisualise(), and saves the figures.
Each explainer writes to its own subdirectory under the Hydra run folder. See Understanding outputs for the on-disk layout.
Important files¶
src/raitap/transparency/contracts.py:ExplanationScope,ScopeDefinitionStep,ExplanationPayloadKind,ExplanationOutputSpace,MethodFamily,VisualisationContext,VisualSummarySpec. Also definesExplainerAlgorithmSpec, including therequires: frozenset[Capability]field for per-algorithm capability declarations.src/raitap/transparency/results.py:ExplanationResult(semantics,write_artifacts,visualise) andVisualisationResult.src/raitap/transparency/factory.py: theExplanationclass and helpers that turn config into live explainer + visualiser instances.src/raitap/transparency/explainers/base_explainer.py:BaseExplainer+AttributionOnlyExplainer.src/raitap/transparency/explainers/full_explainer.py:FullExplainer.src/raitap/transparency/visualisers/base_visualiser.py:BaseVisualiserand the semantic-contract ClassVars.
Name resolution. Bare class names in YAML _target_ keys (e.g. _target_: CaptumExplainer) are resolved through the @adapters.transparency / @visualisers.transparency decorators and raitap._adapters.lookup("transparency", name), not via the legacy class-kwarg path. To make a new class addressable by bare name, decorate it; that's the only requirement.