spacr.attribution

Attribution — what a trained classifier attends to, and whether that means anything.

spaCR already drew Grad-CAM and saliency maps (spacr.utils.GradCAMGenerator, spacr.utils.SaliencyMapGenerator, spacr.utils.IntegratedGradients). This module is the library those two were the first entries in: the CAM family (Grad-CAM, Grad-CAM++, Score-CAM, XGrad-CAM, Layer-CAM, Eigen-CAM, HiRes-CAM, Ablation-CAM), the gradient family (saliency, integrated gradients, guided backprop, input×gradient, DeepLIFT, and SmoothGrad wrapped around any of them), the SHAP family (GradientSHAP, DeepSHAP), the perturbation family (occlusion, feature ablation), and for Vision Transformers attention rollout plus Chefer et al.’s class-specific relevance propagation.

Not every method applies to every backbone. method_applicability() says which do for a model or a model_type name, and why the others do not, so a method selector can grey an entry with its reason instead of failing mid-run.

Almost none of it is written here. The CAM variants come from the optional torchcam attribution extra, while the gradient and perturbation methods come from the core captum dependency. What is written here is the part no library can supply: the adapters that make every method agree on one output shape, the handling of spaCR’s two classifier head shapes, and — the reason this module exists — the analyses in the second half.

An attribution map is not an explanation. It is a number per pixel produced by a procedure. It does not show what the model “looked at”, it does not establish that the highlighted pixels caused the prediction, and it will render a confident, beautiful, plausible picture for a model with random weights. Four checks stand between a map and wishful thinking, and they are the point of this module:

  • deletion_curve() / insertion_curve() — remove (or add) the pixels the map ranks highest and watch the score. A map whose deletion curve is flat is not describing what the model uses, however good it looks.

  • pointing_game() — does the map’s peak land inside the object at all? spaCR has the object masks already (merged/*.npy), so this costs nothing.

  • randomization_sanity_check() — Adebayo et al. 2018. Randomise the model’s weights layer by layer and attribute again. Several popular methods return a nearly identical map for a randomised model, which means they are edge detectors that happen to be plotted over a classifier. This is the single most informative check here and the one most often skipped.

  • method_agreement() — rank correlation between methods on the same image. Agreement is weak evidence. Disagreement is strong evidence that no single map should be trusted.

Every criterion measures a different property and they routinely disagree. None of them is ground truth, because for attribution there is none.

Two head shapes, one contract. A spaCR classifier’s head emits either one logit (binary; class 1 when the logit is positive) or C logits. Code that assumes one shape is wrong for the other, and silently so: attributing the raw logit of a single-logit head always explains class 1, so for an image the model called class 0 the map you get is the map for the class it rejected — the negation of what you asked for. Every method here goes through ClassScoreModel, which presents a single logit z as the two-column view [-z, +z]. Both classes then have a real gradient, target means the same thing for both head shapes, and no caller has to know which head it has.

author:

spaCR

Exceptions

AttributionError

Base class for the failures this module reports instead of guessing.

NoSpatialLayerError

Raised when a CAM is asked of a model that has no spatial layer to hook.

UnknownMethodError

Raised for a method name that is not registered.

Classes

Agreement

Pairwise rank correlation between several methods on the same image.

Attribution

One attribution map plus everything needed to judge it.

AttributionMapGenerator

Batch adapter with the interface generate_activation_map already uses.

ClassScoreModel

Present any spaCR classifier head as C >= 2 per-class scores.

Curve

A deletion or insertion curve and its area.

MethodSpec

One registered attribution method.

SanityCheck

Result of randomising the model's weights and attributing again.

Functions

applicable_methods(→ Dict[str, Tuple[bool, str]])

method_applicability() for every registered method, by name.

architecture_kind(→ str)

Classify a backbone into what the attribution families can use.

attention_rollout(→ Attribution)

Attention rollout (Abnar & Zuidema 2020) for transformer backbones.

attribute(→ Attribution)

Attribute one image with one method.

cam_type_applicability(→ Tuple[bool, str])

method_applicability() for a cam_type setting value.

cam_type_choices(→ Tuple[str, ...])

Every cam_type the Activation Maps settings can name, in menu order.

chefer_relevance(→ Attribution)

Class-specific transformer relevance (Chefer, Gur & Wolf 2021).

class_scores(→ torch.Tensor)

Per-class scores for x, for either head shape.

compare_methods(, *, target, layer, model_type, ...)

Attribute one image with several methods, for side-by-side reading.

conv_layer_names(→ List[str])

Every Conv2d layer name in model, in definition order.

deletion_curve(→ Curve)

Remove the highest-ranked pixels first and track the class probability.

faithfulness(→ Dict[str, Any])

Every faithfulness number for one map, with the caveats attached.

insertion_curve(→ Curve)

Start from a blanked image and add the highest-ranked pixels first.

list_methods(→ List[str])

Registered method names, optionally restricted to one family.

method_agreement(→ Agreement)

Rank correlation between several attribution maps of the same image.

method_applicability(→ Tuple[bool, str])

Whether a registered method can give a meaningful map for this backbone.

methods_by_family(→ Dict[str, List[str]])

Method names grouped by family, families in a stable order.

pointing_game(→ float)

Does the map's brightest pixel land inside the object?

pointing_game_rate(→ Dict[str, Any])

Pointing-game hit rate over a set of images.

randomization_sanity_check(→ SanityCheck)

Adebayo et al. 2018: does the map change when the weights are destroyed?

recommended_layer(→ Optional[str])

The last convolutional layer — the usual CAM target — or None.

resolve_cam_type(→ Optional[str])

The registry method a cam_type names, or None for a legacy one.

resolve_layer(→ torch.nn.Module)

Resolve a dotted layer name against model.

smoothgrad(→ Attribution)

SmoothGrad: average base_method over n_samples noisy copies.

Module Contents

exception spacr.attribution.AttributionError[source]

Bases: RuntimeError

Base class for the failures this module reports instead of guessing.

Initialize self. See help(type(self)) for accurate signature.

exception spacr.attribution.NoSpatialLayerError[source]

Bases: AttributionError

Raised when a CAM is asked of a model that has no spatial layer to hook.

A CAM is a weighted sum of one convolutional layer’s feature maps. A pure transformer has no such layer, and hooking its patch embedding produces a picture that is not a CAM of anything. Rather than return that picture, the CAM adapters raise this and name the model so the caller can switch to attention_rollout() or to a gradient / perturbation method, which work on any architecture.

Initialize self. See help(type(self)) for accurate signature.

exception spacr.attribution.UnknownMethodError[source]

Bases: AttributionError

Raised for a method name that is not registered.

Initialize self. See help(type(self)) for accurate signature.

class spacr.attribution.Agreement[source]

Pairwise rank correlation between several methods on the same image.

Parameters:
  • methods – attribution-method names in matrix row and column order.

  • matrix – symmetric Spearman rank-correlation matrix for those methods.

  • mean – mean of the finite off-diagonal correlations.

  • minimum – smallest finite off-diagonal correlation.

  • pairs – (method_a, method_b, rho) comparisons ordered from greatest disagreement upward.

  • notes – verdict and interpretive caveats accompanying the agreement result.

verdict() → str[source]

One sentence, deliberately asymmetric — see the note in the source.

class spacr.attribution.Attribution[source]

One attribution map plus everything needed to judge it.

Parameters:
  • method – Requested attribution-method name; normally a key of ATTRIBUTION_METHODS, and retained verbatim on a skipped-failure placeholder.

  • map – Finite 2-D float32 map at input spatial resolution; larger values rank pixels higher, while a skipped failure carries an all-zero placeholder.

  • target – Class index this map was requested to explain.

  • n_classes – Number of classes exposed by the normalized head, including two for a single-logit binary head.

  • single_logit – Whether the underlying model head emitted one binary logit.

  • predicted – Class predicted by the model for the attributed input.

  • raw – Signed per-channel attribution retained by methods that expose it, or None when no such representation exists.

  • layer – Resolved CAM target-layer name, the caller’s requested layer on a skipped failure, or None when no layer applies.

  • family – Registered family ("cam", "gradient", "perturbation", or "attention"), or an empty string for an unregistered skipped failure.

  • backend – Registered implementation provider ("torchcam", "captum", or "spacr"), or an empty string for an unregistered skipped failure.

  • params – Recorded call options, including the resolved layer and SmoothGrad flag for registry-dispatched methods; this is not a complete expansion of every effective default.

  • notes – User-facing caveats, flat-map or single-logit context, or the reason a placeholder method failed.

is_flat() → bool[source]

True when the map has no variation and therefore ranks nothing.

normalized() → numpy.ndarray[source]

The map rescaled to [0, 1]; all-zero when the map is flat.

A flat map is exactly what a fully suppressed CAM produces, and the unguarded min-max rescale of one is 0/0.

peak() → Tuple[int, int][source]

(row, col) of the single highest-ranked pixel.

property shape: Tuple[int, int][source]

Spatial shape of the map.

class spacr.attribution.AttributionMapGenerator(model, method: str = 'gradcam', target_layer: str | None = None, model_type: str | None = None, smoothgrad_samples: int = 0, smoothgrad_sigma: float = 0.15, **kw)[source]

Batch adapter with the interface generate_activation_map already uses.

spacr.utils.GradCAMGenerator and spacr.utils.SaliencyMapGenerator expose compute_*_and_predictions(X) plus plot_activation_grid. This offers the same two calls for every method in ATTRIBUTION_METHODS, so the existing batch loop gains twelve methods without changing shape.

Parameters:
  • model – the trained classifier.

  • method – a key of ATTRIBUTION_METHODS.

  • target_layer – CAM target layer, or None for the last convolution.

  • model_type – architecture name, used in the error messages.

  • smoothgrad_samples – when above 1, each map is SmoothGrad-averaged.

  • smoothgrad_sigma – SmoothGrad noise as a fraction of the input range.

  • kw – forwarded to the method.

Raises:

UnknownMethodError – if method is not registered in ATTRIBUTION_METHODS.

Validate the method name up front rather than mid-batch.

compute_maps_and_predictions(X)[source]

Attribute every image in a batch.

Parameters:

X – batch tensor (N, C, H, W).

Returns:

(maps, predictions) — maps is (N, H, W) float32, predictions is (N,) long, correct for either head shape.

plot_activation_grid(X, maps, predictions, overlay=True, normalize=False)[source]

Render the batch grid, reusing spaCR’s existing layout.

Parameters:
  • X – the input batch.

  • maps – the attribution maps.

  • predictions – predicted class per image.

  • overlay – draw the map over the image.

  • normalize – percentile-stretch the image under the overlay.

Returns:

the matplotlib Figure.

class spacr.attribution.ClassScoreModel(model: torch.nn.Module, n_out: int | None = None)[source]

Bases: torch.nn.Module

Present any spaCR classifier head as C >= 2 per-class scores.

A head emitting one logit z is presented as [-z, +z]: column 1 is the evidence for class 1, column 0 the evidence for class 0, and argmax reproduces the z > 0 rule spaCR’s binary models use. The obvious alternative, [0, z], is exactly equivalent under softmax but has zero gradient for class 0, so every attribution for class 0 would be an all-zero map — a silent, plausible-looking wrong answer.

A head emitting C > 1 logits is passed through untouched.

Parameters:
  • model – the classifier to wrap.

  • n_out – optional raw output width. If omitted, the first forward pass infers it from the wrapped model’s output.

Variables:
  • n_out – the wrapped model’s raw output width (1 or C).

  • n_classes – the number of classes the wrapper exposes (2 or C).

  • single_logit – True when the wrapped head emits one logit.

Wrap model, recording whether its head is single-logit.

forward(x: torch.Tensor) → torch.Tensor[source]

Return (B, n_classes) scores for x.

Parameters:

x – input batch passed to the wrapped classifier.

property n_classes: int[source]

How many classes the wrapper exposes.

class spacr.attribution.Curve[source]

A deletion or insertion curve and its area.

Variables:
  • kind – 'deletion' or 'insertion'.

  • fractions – fraction of pixels removed / inserted at each step.

  • scores – the target class’s probability at each step.

  • auc – area under the curve, trapezoidal over fractions. Bounded in [0, 1] because the scores are probabilities.

  • baseline – what removed pixels were replaced with.

  • target – the class whose probability was tracked.

  • notes – caveats.

property drop: float[source]

How far the score fell (deletion) or rose (insertion), start to end.

property higher_is_better: bool[source]

Whether a larger AUC is the better outcome for this curve’s kind.

class spacr.attribution.MethodSpec[source]

One registered attribution method.

Parameters:
  • name – registry key callers pass as method=.

  • family – method family—"cam", "gradient", "shap", "perturbation", or "attention".

  • backend – implementation provider—"torchcam", "captum", or "spacr".

  • fn – adapter callable that computes this method’s attribution.

  • needs_layer – whether the method requires a spatial target layer.

  • smoothed – whether the Captum adapter wraps the base attributor in a SmoothGrad noise tunnel.

  • description – concise user-facing explanation suitable for method selectors.

smoothgrad_variant() → MethodSpec[source]

This method with SmoothGrad averaging turned on.

class spacr.attribution.SanityCheck[source]

Result of randomising the model’s weights and attributing again.

Variables:
  • method – the method under test.

  • mode – 'cascading' (randomise layers from the output backwards, accumulating) or 'independent' (one layer at a time, from a fresh copy).

  • stages – (layer_name, similarity) after each randomisation step, in the order applied.

  • final_similarity – similarity to the trained model’s map once every parameterised layer has been randomised. This is the number that matters: at that point the model is noise.

  • max_similarity – the largest similarity over all stages.

  • threshold – the value final_similarity must fall below to pass.

  • passed – whether the method’s map changed when the weights did.

  • metric – name of the similarity measure.

  • notes – the verdict in words.

verdict() → str[source]

One sentence a user can act on.

property gap: float[source]

higher is better.

This is the form the hyperparameter search ranks on, so that every criterion there points the same way.

Type:

1 - final_similarity, clipped to [0, 1]

spacr.attribution.applicable_methods(model: torch.nn.Module | None = None, model_type: str | None = None) → Dict[str, Tuple[bool, str]][source]

method_applicability() for every registered method, by name.

spacr.attribution.architecture_kind(model: torch.nn.Module | None = None, model_type: str | None = None) → str[source]

Classify a backbone into what the attribution families can use.

Parameters:
  • model – the model itself, when it is loaded.

  • model_type – the architecture name spaCR stores in settings ('resnet50', 'maxvit_t', 'vit_b_16' …).

Returns:

'vit' (global self-attention over a patch grid), 'swin' (shifted-window attention), 'hybrid' (MaxViT: convolutions after attention), 'cnn', 'other' (a loaded model with neither convolution nor attention) or 'unknown' (nothing to go on).

spacr.attribution.attention_rollout(model: torch.nn.Module, image: Any, *, target: int | None = None, head_fusion: str = 'mean', discard_ratio: float = 0.0, model_type: str | None = None) → Attribution[source]

Attention rollout (Abnar & Zuidema 2020) for transformer backbones.

A CAM needs a convolutional feature map. A pure transformer has none, so this is the substitute: the per-layer attention matrices are averaged over heads, mixed with the identity to account for the residual stream, row- normalised and multiplied together, giving how much each input token contributes to the class token.

It reads spaCR’s torch.nn.MultiheadAttention blocks, which return their attention weights from forward. Backbones whose attention is a fused kernel (timm’s ViT via scaled_dot_product_attention) expose no weights, and this raises rather than inventing a map.

Rollout is not class-conditional. The result is the same for every target: it describes where information flowed, not what the model concluded. It cannot show the model separated your classes; a gradient or perturbation method can.

Parameters:
  • model – the transformer classifier.

  • image – one image, (C, H, W) or (1, C, H, W).

  • target – recorded on the result; does not change the map.

  • head_fusion – 'mean', 'max' or 'min' over attention heads.

  • discard_ratio – fraction of the lowest attention weights zeroed per layer before rollout, which sharpens the map and is pure cosmetics.

  • model_type – architecture name for the error messages.

Returns:

the Attribution.

Raises:

NoSpatialLayerError – when the model exposes no attention weights.

spacr.attribution.attribute(model: torch.nn.Module, image: Any, method: str = 'gradcam', *, target: int | None = None, layer: str | None = None, model_type: str | None = None, **kw) → Attribution[source]

Attribute one image with one method.

Every registered method returns the same thing: a finite (H, W) map at the input’s resolution, for the class target names, for either head shape. What differs is what the number means, which is why Attribution carries the family and the notes.

Parameters:
  • model – the trained classifier. Left untouched — the wrapper and the hooks are removed before this returns.

  • image – one image, (H, W), (C, H, W) or (1, C, H, W).

  • method – a key of ATTRIBUTION_METHODS.

  • target – class index to explain; defaults to the model’s prediction. For a single-logit head, 0 and 1 are both valid and give opposite maps.

  • layer – dotted target-layer name for the CAM family; defaults to the last convolution.

  • model_type – architecture name, used only to make errors readable.

  • kw – method-specific options — n_steps/baseline (integrated gradients, DeepLIFT), window/stride (occlusion), block (feature ablation), head_fusion/discard_ratio (rollout), n_samples/sigma when called through smoothgrad().

Returns:

the Attribution.

Raises:
spacr.attribution.cam_type_applicability(cam_type: str, *, model: torch.nn.Module | None = None, model_type: str | None = None) → Tuple[bool, str][source]

method_applicability() for a cam_type setting value.

The legacy Grad-CAM generators need a spatial layer like any CAM; the legacy saliency maps apply to every backbone.

Parameters:
  • cam_type – the cam_type setting value, as resolve_cam_type() accepts it.

  • model – the loaded model, when there is one.

  • model_type – the architecture name, when there is no model.

Returns:

(applies, reason); the reason is empty when it applies.

Raises:

UnknownMethodError – for a name resolve_cam_type() refuses.

spacr.attribution.cam_type_choices() → Tuple[str, ...][source]

Every cam_type the Activation Maps settings can name, in menu order.

spacr.attribution.chefer_relevance(model: torch.nn.Module, image: Any, *, target: int | None = None, model_type: str | None = None) → Attribution[source]

Class-specific transformer relevance (Chefer, Gur & Wolf 2021).

Rollout multiplies raw attention and so gives one map whatever class is asked about. This weights every head’s attention by the class score’s gradient on it, keeps the positive part, averages the heads and accumulates the result through the residual stream, R <- R + A_bar R from the identity. The class token’s row of R is the relevance of each patch for the target class, so asking for the other class gives a different map.

It reads torch.nn.MultiheadAttention blocks (torchvision’s ViT and spaCR’s own), asking each for per-head weights the way attention_rollout() does.

Parameters:
  • model – the transformer classifier.

  • image – one image, (C, H, W) or (1, C, H, W).

  • target – class to explain; defaults to the prediction.

  • model_type – architecture name for the error messages.

Returns:

the Attribution.

Raises:

NoSpatialLayerError – when the model has no attention blocks whose weights sit on the gradient path, or its tokens do not form a grid.

spacr.attribution.class_scores(model: torch.nn.Module, x: torch.Tensor, *, probability: bool = True) → torch.Tensor[source]

Per-class scores for x, for either head shape.

Parameters:
  • model – the classifier (raw or already wrapped).

  • x – input batch (B, C, H, W).

  • probability – return softmax probabilities rather than raw scores. Probabilities are what the deletion / insertion curves track, because a bounded quantity makes their areas comparable across images.

Returns:

(B, n_classes) tensor.

spacr.attribution.compare_methods(model: torch.nn.Module, image: Any, methods: Sequence[str] = (), *, target: int | None = None, layer: str | None = None, model_type: str | None = None, skip_failures: bool = True, **kw) → List[Attribution][source]

Attribute one image with several methods, for side-by-side reading.

The deliverable is the panel plus method_agreement() over it, not any single map. Methods that cannot run on this architecture (a CAM on a pure transformer) are skipped with their reason recorded rather than aborting the comparison, unless skip_failures is off.

Parameters:
  • model – the trained classifier.

  • image – one image.

  • methods – method names; defaults to one representative of each family that works on any architecture, plus Grad-CAM.

  • target – class to explain; resolved once so every method explains the same class.

  • layer – CAM target layer.

  • model_type – architecture name for the error messages.

  • skip_failures – record and skip a failing method instead of raising.

  • kw – forwarded to every method.

Returns:

the attributions, in the order requested. Failures appear as Attribution objects with a flat map and the error in notes only when skip_failures is True.

spacr.attribution.conv_layer_names(model: torch.nn.Module) → List[str][source]

Every Conv2d layer name in model, in definition order.

Parameters:

model – the model to scan.

Returns:

dotted layer names; empty for a model with no convolutions.

spacr.attribution.deletion_curve(model: torch.nn.Module, image: Any, amap: Any, *, target: int | None = None, n_steps: int = 20, baseline: Any = 'blur') → Curve[source]

Remove the highest-ranked pixels first and track the class probability.

A faithful map removes the pixels the model actually uses, so the probability collapses early and the area under the curve is small. A flat deletion curve is the finding: the map ranked pixels the model does not use, whatever the picture looked like.

Parameters:
  • model – the trained classifier.

  • image – the image the map explains.

  • amap – an Attribution or a raw (H, W) map.

  • target – class whose probability is tracked; defaults to the prediction on the unperturbed image.

  • n_steps – perturbation steps between 0 % and 100 % removed.

  • baseline – what removed pixels become — 'blur' (default, the least out-of-distribution), 'zero', 'mean', 'uniform', a number or a tensor.

Returns:

the Curve; lower auc is better.

spacr.attribution.faithfulness(model: torch.nn.Module, image: Any, amap: Any, *, target: int | None = None, n_steps: int = 20, baseline: Any = 'blur', mask: Any | None = None) → Dict[str, Any][source]

Every faithfulness number for one map, with the caveats attached.

Parameters:
  • model – the trained classifier.

  • image – the image the map explains.

  • amap – an Attribution or a raw (H, W) map.

  • target – class to score.

  • n_steps – steps for both curves.

  • baseline – removal baseline for both curves.

  • mask – optional boolean object mask enabling the pointing game.

Returns:

dict with deletion_auc, insertion_auc, deletion and insertion Curve objects, pointing_game (or None), flat and notes.

spacr.attribution.insertion_curve(model: torch.nn.Module, image: Any, amap: Any, *, target: int | None = None, n_steps: int = 20, baseline: Any = 'blur') → Curve[source]

Start from a blanked image and add the highest-ranked pixels first.

The mirror of deletion_curve(), and it answers a different question: deletion asks whether the map found pixels that are necessary, insertion whether it found pixels that are sufficient. The two routinely rank methods differently, and that disagreement is information, not an error.

Parameters:
  • model – the trained classifier.

  • image – the image the map explains.

  • amap – an Attribution or a raw (H, W) map.

  • target – class whose probability is tracked.

  • n_steps – insertion steps between 0 % and 100 % inserted.

  • baseline – what the not-yet-inserted pixels are.

Returns:

the Curve; higher auc is better.

spacr.attribution.list_methods(family: str | None = None) → List[str][source]

Registered method names, optionally restricted to one family.

spacr.attribution.method_agreement(attributions: Sequence[Any]) → Agreement[source]

Rank correlation between several attribution maps of the same image.

Agreement and disagreement are not symmetric evidence. Methods agreeing is weak: the gradient family shares failure modes, so its members agree with each other whether or not any of them is faithful. Methods disagreeing is strong: at most one of them can be right, so none should be quoted alone.

Parameters:

attributions – Attribution objects or raw (H, W) maps; at least two, all the same shape.

Returns:

the Agreement.

Raises:

AttributionError – for fewer than two maps or a shape mismatch.

spacr.attribution.method_applicability(method: str, *, model: torch.nn.Module | None = None, model_type: str | None = None) → Tuple[bool, str][source]

Whether a registered method can give a meaningful map for this backbone.

Decided from the architecture and from which optional backends are installed, without running the model, so a settings form can grey the methods that do not apply and show the reason.

Parameters:
  • method – a key of ATTRIBUTION_METHODS.

  • model – the loaded model, when there is one.

  • model_type – the architecture name, when there is no model.

Returns:

(applies, reason); the reason is empty when it applies.

Raises:

UnknownMethodError – for an unregistered method name.

spacr.attribution.methods_by_family() → Dict[str, List[str]][source]

Method names grouped by family, families in a stable order.

spacr.attribution.pointing_game(amap: Any, mask: Any, *, tolerance: int = 0) → float[source]

Does the map’s brightest pixel land inside the object?

spaCR already has the answer key: merged/*.npy stores the label-mask planes next to the image channels, so a boolean object mask is free. The game is deliberately crude — one pixel, hit or miss — because that is all it claims to measure.

Parameters:
  • amap – an Attribution or a (H, W) map.

  • mask – object mask, same spatial shape. Any non-zero value is inside the object, so a spaCR integer label plane can be passed directly.

  • tolerance – dilate the mask by this many pixels before testing, the allowance the original pointing-game protocol uses for maps computed at a coarser resolution than the image.

Returns:

1.0 for a hit, 0.0 for a miss.

Raises:

AttributionError – on a shape mismatch or an empty mask — an empty mask would score 0.0 and look like a method failure rather than a missing annotation.

spacr.attribution.pointing_game_rate(maps: Sequence[Any], masks: Sequence[Any], *, tolerance: int = 0) → Dict[str, Any][source]

Pointing-game hit rate over a set of images.

Parameters:
  • maps – attributions or raw maps.

  • masks – the matching object masks.

  • tolerance – passed to pointing_game().

Returns:

dict with rate, hits, n, skipped (images whose mask was empty or mismatched, which are excluded rather than counted as misses) and notes.

Raises:

AttributionError – when the two sequences differ in length.

spacr.attribution.randomization_sanity_check(model: torch.nn.Module, image: Any, method: str | Callable = 'gradcam', *, target: int | None = None, layer: str | None = None, model_type: str | None = None, mode: str = 'cascading', threshold: float = 0.5, seed: int = 0, max_stages: int | None = None, attribute_fn: Callable[..., Any] | None = None, **kw) → SanityCheck[source]

Adebayo et al. 2018: does the map change when the weights are destroyed?

Randomise the model’s parameters layer by layer, from the output backwards, re-attribute at every stage, and correlate each map with the map from the trained model. A method that depends on what the model learned produces an uncorrelated map once the weights are noise. Several widely used methods do not: guided backprop and guided Grad-CAM are the canonical failures, and a method that fails this is an edge detector being read as an explanation.

This is the most informative check in the module. A map that passes deletion and insertion but fails this one is describing the image; a method that fails here cannot be rescued by smoothing, a better colormap or a different target layer.

Parameters:
  • model – the trained classifier. Deep-copied — the original is never modified.

  • image – one image.

  • method – a registered method name, or any callable when attribute_fn is not given.

  • target – class to explain, resolved once against the trained model and held fixed, so a stage’s map is not silently for a different class.

  • layer – CAM target layer.

  • model_type – architecture name for the error messages.

  • mode – 'cascading' (default, the paper’s) or 'independent'.

  • threshold – rank correlation below which the method passes.

  • seed – RNG seed for the randomisation, so the check reproduces.

  • max_stages – cap on the number of layers randomised, from the output backwards. The final stage always randomises everything regardless.

  • attribute_fn – fn(model, image, target=...) -> map or Attribution, for testing a method that is not in the registry.

  • kw – forwarded to the attribution call.

Returns:

the SanityCheck.

Raises:

AttributionError – for an unknown mode, or a model with no parameters to randomise.

spacr.attribution.recommended_layer(model: torch.nn.Module) → str | None[source]

The last convolutional layer — the usual CAM target — or None.

Parameters:

model – model whose convolutional layers are scanned.

Mirrors spacr.utils.recommend_target_layers(), but returns None for a model with no convolutions instead of raising, so the CAM adapters can raise NoSpatialLayerError with the architecture named.

spacr.attribution.resolve_cam_type(cam_type: str) → str | None[source]

The registry method a cam_type names, or None for a legacy one.

Parameters:

cam_type – a cam_type setting value: a legacy generator, a CAM_TYPE_ALIASES spelling or a key of ATTRIBUTION_METHODS.

Returns:

the registry method name, or None for a legacy generator.

Raises:

UnknownMethodError – for a name that is neither.

spacr.attribution.resolve_layer(model: torch.nn.Module, name: str) → torch.nn.Module[source]

Resolve a dotted layer name against model.

Parameters:
  • model – the model to look in.

  • name – dotted module path, e.g. 'features.2'.

Returns:

the submodule.

Raises:

AttributionError – naming the closest available layers. A wrong target layer is the most common way a CAM run dies, and a bare AttributeError: 'Sequential' object has no attribute 'conv_b' does not tell the user what to type instead.

spacr.attribution.smoothgrad(model: torch.nn.Module, image: Any, base_method: str = 'saliency', *, n_samples: int = 25, sigma: float = 0.15, target: int | None = None, layer: str | None = None, model_type: str | None = None, seed: int | None = None, **kw) → Attribution[source]

SmoothGrad: average base_method over n_samples noisy copies.

Gradient maps are visually noisy because the gradient of a ReLU network fluctuates sharply between neighbouring inputs. Averaging over Gaussian perturbations of the input suppresses that fluctuation as 1/sqrt(n) while keeping the structure that survives perturbation.

For the captum-backed methods this is captum’s own NoiseTunnel. The CAM family and rollout are not captum attributors, so for those the averaging is done here over re-runs of the adapter — same definition, applied to the map.

Smoothing makes a map look better. It does not make it more faithful, and a smoothed map that still fails randomization_sanity_check() fails it just as badly.

Parameters:
  • model – the trained classifier.

  • image – one image.

  • base_method – any key of ATTRIBUTION_METHODS.

  • n_samples – number of noisy copies to average. 1 is a single noisy sample, not the clean map — call attribute() for that.

  • sigma – noise standard deviation as a fraction of the input’s dynamic range (the SmoothGrad paper’s stdev_spread).

  • target – class to explain; resolved once on the clean image so the noise cannot flip which class is being explained sample to sample.

  • layer – CAM target layer.

  • model_type – architecture name for the error messages.

  • seed – torch seed, so a repeated call reproduces.

  • kw – forwarded to the base method.

Returns:

the averaged Attribution.

Raises:

AttributionError – for n_samples below 1.

Nested helpers

_ablation_cam._ablate(_m, _inp, out)

Record the clean feature maps, or zero one channel per batch row.

spacr/attribution.py:1125

_ask_for_attention_weights._wrap(block)

Install the attention-weight adapter and return the original call.

spacr/attribution.py:868

_ask_for_attention_weights._wrap.forward(*args, **kwargs)

Request per-head weights unless a positional request owns them.

spacr/attribution.py:872

_ask_for_attention_weights.restore()

Restore every block’s exact original bound forward method.

spacr/attribution.py:890

_check_spatial_activation._attn_hook(_m, _inp, _out)

Record that an attention block ran.

spacr/attribution.py:430

_check_spatial_activation._target_hook(_m, _inp, out)

Capture the target layer’s output and its position in the pass.

spacr/attribution.py:425

_eigen_cam._hook(_m, _inp, out)

Capture the target layer’s feature maps.

spacr/attribution.py:637

_layer_activation_and_gradient._hook(_m, _inp, out)

Copy the feature maps and ask for their gradient.

spacr/attribution.py:1062

_layer_activation_and_gradient._keep_gradient(grad)

Store the gradient that reaches the hooked feature map.

spacr/attribution.py:1058

attention_rollout._hook(_m, _inp, out)

Capture the (B, L, S) attention weights an MHA block returns.

spacr/attribution.py:954

chefer_relevance._hook(_m, _inp, out)

Keep the attention weights an MHA block returns, graph attached.

spacr/attribution.py:1422

randomization_sanity_check._run(m: nn.Module) → np.ndarray

Attribute image with the method under test on model m.

spacr/attribution.py:2280