Holistic CRAFT¶
View concept localization TensorFlow tutorial |
View source |
📰 CRAFT Paper |
📰 Holistic Paper
Holistic CRAFT (Concept Recursive Activation FacTorization) is a variant of the CRAFT method designed to extract concepts from full activation maps rather than image patches.
This approach preserves the global spatial context and is particularly suitable for object detection models and other tasks where spatial structure across the entire image is important.
The crop-based approach works well for classification because images of classification datasets are typically dominated by a single, well-centred object: random crops are therefore likely to contain parts of the object of interest and carry relevant signal for concept extraction. In Object Detection, the scenes generally contain multiple objects of varying sizes, often occupying only a small fraction of the image. Random crops drawn from such images are mostly background; the target objects are absent or heavily under-represented in the resulting crop dataset, making the NMF factorization blind to the very patterns it should capture.
Supported Object Detection Models¶
Holistic CRAFT works with various object detection architectures through specialized latent extractors provided by the xplique-adapters package:
PyTorch (torchvision & ultralytics):
- RetinaNet - RetinanetExtractorBuilder
- Faster R-CNN - FasterRcnnExtractorBuilder
- FCOS - FcosExtractorBuilder
- SSD - SSDExtractorBuilder
- YOLO (v11) - YoloExtractorBuilder
- DETR - DetrExtractorBuilder
TensorFlow:
- RetinaNet - RetinaNetExtractorBuilder
Each extractor handles the model-specific architecture to split it into the required g(.) and h(.) functions.
Supported Classification Models¶
For standard classification models, Holistic CRAFT does not require a custom extractor per architecture. Instead, the built-in LayeredModelExtractorBuilder can split any layered model at a chosen intermediate layer:
PyTorch:
- Any torch.nn.Module — LayeredModelExtractorBuilder (from xplique.concepts.torch.layered_model_latent_extractor)
TensorFlow:
- Any tf.keras.Model — LayeredModelExtractorBuilder (from xplique.concepts.tf.layered_model_latent_extractor)
The builder takes the model and a layer index to define the split point. Everything before that layer becomes g(.), and everything after becomes h(.).
Key Differences from Regular CRAFT¶
| Aspect | Regular CRAFT | Holistic CRAFT |
|---|---|---|
| Input | Image patches/crops | Full activation maps |
| Use Case | Classification tasks | Object detection, Classification |
| Spatial Context | Local (patch-level) | Global (full image) |
| Concepts | Visual patterns in patches | Spatial activation patterns |
| Performance | Extracts many crops per image | Processes full feature maps directly |
Workflow¶
Holistic CRAFT follows the same principle as CRAFT but operates on full images instead of patches:
- Fit concepts: pass model inputs through the encoder \(g\) to obtain intermediate activations, then factorize them to discover recurring concepts.
- Measure concept activation: encode each image as concept activation maps in
coeffs_u. - Estimate concept importance: attribute the task prediction to concept activations (by perturbing them with Sobol, or using a gradient-based method).
- Interpret concepts: either visualize concept activation maps or perturb the input and attribute changes in concept activation scores to obtain localization maps.
Warning
Activations must be non-negative to use the standard NMF. Ensure a ReLU or similar activation function is applied before the extraction layer. Third-party NMF implementations may not have this limitation (e.g., the Semi-NMF from the Overcomplete library).
The corresponding methods form a compact end-to-end sequence:
The example assumes that images, the task operator, class_id, and a configured
partial_explainer are already available.
# 1. Fit concepts.
craft.fit(images)
# 2. Compute concept activation maps.
coeffs_u = craft.transform(images)
# 3. Estimate concept importance.
importances = craft.estimate_importance(images, operator, class_id)
# 4. Interpret concept activation maps.
craft.display_images_per_concept(images, coeffs_u=coeffs_u)
# Alternatively, compute concept localization maps with a PartialExplainer.
concept_maps = craft.attribute_concepts_to_inputs(images, partial_explainer)
Like regular CRAFT, Holistic CRAFT splits the model into \((g, h)\) such that \(f(x) = h(g(x))\). The encoder \(g\) maps an input to latent activations, and \(h\) maps those activations to predictions. Concepts are extracted in this latent space.
This split is implemented through three abstractions:
-
LatentData: A container that holds the intermediate activations produced by \(g\). It abstracts away framework-specific tensor formats, providing a unified interface for reading (get_activations) and writing (set_activations) activations, with the necessary shape conversions (e.g., channel-first to channel-last). -
LatentExtractor: Wraps both \(g\) (input_to_latent_model) and \(h\) (latent_to_logit_model). It orchestrates the full forward pass, batching, device management, and output formatting. TheTorchLatentExtractorandTfLatentExtractorsubclasses provide framework-specific implementations. -
LatentExtractorBuilder: A factory that constructs aLatentExtractorfor a specific model architecture. It handles all the architecture-specific wiring (defining how to split the model, which layer to extract from, and how to format outputs) so that the rest of the CRAFT pipeline remains model-agnostic.
Concept Activation, Importance, and Localization¶
Holistic CRAFT exposes four related quantities with distinct meanings:
| Quantity | Definition | Question answered |
|---|---|---|
| Concept activation map | Spatial factorization coefficients \(U_k(x)\), stored in coeffs_u |
Where and how strongly is concept \(k\) activated in latent space? |
| Concept activation score | A scalar reduction \(s_k(x) = R(U_k(x))\) | How strongly is concept \(k\) activated overall? |
| Concept importance | Attribution of the task prediction to concept activations | How much do the activations contribute to the task prediction? |
| Concept localization map | Attribution of a concept activation score to the input | Which input regions drive the activation score? |
For a CNN activation tensor (N, H', W', C), factorization preserves the spatial axes and
produces coeffs_u with shape (N, H', W', K). Thus, \(U_k(x)\) is a concept activation map.
Spatial vision-transformer tokens can likewise form a concept activation map when the latent
extractor maps them back to their patch grid.
If the extracted representation has no spatial axes, coeffs_u has shape (N, K) and each
coefficient is already global; no spatial concept activation map is available to resize.
With the default spatial mean reducer, \(s_k(x) = \operatorname{mean}_{i,j} U_k(x)_{i,j}\). Resizing \(U_k(x)\) therefore gives a CAM-style map for the concept score: the spatial map underlying that score is displayed directly. Unlike Grad-CAM, this visualization does not use gradients or class-prediction weights.
For perturbation-based methods, concept importance and localization look at different quantities:
Concept importance (Sobol): perturb latent concept activations
-> measure the task prediction -> attribute it to concepts
Concept localization (RISE/Sobol): perturb input regions
-> measure concept activation score s_k(x) -> attribute it to input regions
Concept importance also supports gradient-based methods because Holistic CRAFT can decode
concept activations toward the task prediction. Input-to-concept localization instead follows
the fitted NumPy factorizer's encode() path, which is not differentiable, and therefore
requires a black-box attribution method.
Interpretation Alternatives¶
Choose the representation that matches the question:
| Alternative | Use | Meaning |
|---|---|---|
| Concept activation map visualization | Pass coeffs_u to a display method |
Resizes latent \(U_k(x)\) as a CAM-style map for the concept score (with spatial mean reduction) |
| Concept localization map | Call attribute_concepts_to_inputs() and pass the result as concept_maps |
Shows which input perturbations change \(s_k(x)\) |
These alternatives can differ when latent maps are coarse or positions have large receptive
fields. Neither is a segmentation mask. Top-image ranking always uses coeffs_u, not the
magnitude of a concept localization map. Black-box localization is not inherently more
accurate than concept activation map visualization, and different black-box methods can
produce different maps.
Example¶
Basic Usage with Object Detection¶
import xplique
from xplique.concepts import HolisticCraftTorch as Craft
from xplique_adapters.concepts.torch.latent_data_retinanet import RetinanetExtractorBuilder
# Build a latent extractor that splits the model into g(.) and h(.)
# This provides the input_to_latent (g) and latent_to_logit (h) functions
latent_extractor = RetinanetExtractorBuilder.build(
model,
device="cuda",
nb_classes=91,
extraction_location='resnet', # Choose 'resnet' or 'fpn'
extraction_layer=-1 # Extract from last ResNet feature layer
)
# Create Holistic CRAFT instance
craft = Craft(
latent_extractor=latent_extractor,
number_of_concepts=10,
device="cuda"
)
# Fit CRAFT on input images to discover concepts
craft.fit(input_images, class_id=class_id)
# Display concept activation maps overlaid on images
craft.display_images_per_concept(images=input_images[:5])
# Display top 3 images for each concept ranked by concept activation score
craft.display_top_images_per_concept(images=input_images, topk=3)
# Estimate concept importance on the 20 first images using Gradient×Input method
# (GradientxInput is the default method)
importances_gi = craft.estimate_importance(
images=input_images[:20],
operator=xplique.Tasks.OBJECT_DETECTION,
class_id=class_id,
confidence=0.8
)
# Estimate concept importance on the 20 first images using Sobol method
importances_sobol = craft.estimate_importance(
images=input_images[:20],
operator=xplique.Tasks.OBJECT_DETECTION,
class_id=class_id,
confidence=0.8,
# Use Sobol method & its arguments
method="sobol",
grid_size=4,
nb_design=8,
perturbation_function="amplitude",
)
Using Different Attribution Methods to Compute Concept Importance¶
Holistic CRAFT supports various attribution methods for concept importance estimation:
import xplique
from xplique.concepts import PartialExplainer
from xplique.attributions import VarGrad
# Use VarGrad for robust importance estimation
vargrad_explainer = PartialExplainer(
explainer_class=VarGrad,
operator=xplique.Tasks.OBJECT_DETECTION,
nb_samples=20,
noise=0.15
)
# Compute VarGrad explanation for each concept
explanation_vargrad = craft.compute_explanation_per_concept(
partial_explainer=vargrad_explainer,
images=input_images,
class_id=class_id,
confidence=0.3,
)
# Reduce the spatial dimensions to compute concept importance
importances_vargrad = craft.reduce_to_importance(
explanation=explanation_vargrad,
)
Localizing Concepts with Black-Box Attribution¶
attribute_concepts_to_inputs() exposes the fitted encoder and factorizer as a callable that
returns one concept activation score per learned concept. It creates one-hot concept targets
and applies a compatible black-box explainer to each requested concept. No task operator is
needed because the targets select concept activation scores directly.
For an input batch with shape (N, H, W, C), the returned maps have shape
(N, H, W, number_of_concepts). Channel k always corresponds to concept k. When only a
subset is requested, uncomputed channels are filled entirely with NaN so that they cannot
be confused with valid zero attributions.
TensorFlow callers pass channel-last images. PyTorch callers pass their native channel-first
(N, C, H, W) images to the same high-level method; Xplique handles the layout conversion
for the shared localizer. Calling make_concept_localizer() directly expects channel-last
images for either backend and returns a float32 TensorFlow tensor of concept scores.
Sobol Example¶
from xplique.attributions import SobolAttributionMethod
from xplique.concepts import PartialExplainer
sobol = PartialExplainer(
SobolAttributionMethod,
grid_size=8,
nb_design=32,
perturbation_function="inpainting",
)
# `model_images` are preprocessed inputs; `display_images` are matching display images.
concept_maps = craft.attribute_concepts_to_inputs(
model_images,
partial_explainer=sobol,
concept_ids=[0, 3, 7],
concept_reducer="mean",
)
craft.display_images_per_concept(
display_images,
concept_maps=concept_maps,
order=[0, 3, 7],
)
concept_reducer="mean" reduces every \(U_k(x)\) to the concept activation score attributed to
the input. It is the default and matches the reduction used to rank representative images.
The localizer preserves signed scores; use a custom callable reducer only when another scalar
definition is intended.
nb_design must be a nonzero power of two. Keep Sobol's default nb_channels=1: localization
computes a separate single-channel map for each selected concept. Sobol-based concept
importance is a different operation: it perturbs latent concept activations and attributes
changes in the task prediction to concepts, rather than perturbing inputs to measure \(s_k(x)\).
RISE is an alternative black-box configuration; pass rise as partial_explainer to the same
method:
from xplique.attributions import Rise
rise = PartialExplainer(
Rise, nb_samples=2000, grid_size=7, preservation_probability=0.5, mask_value=0.0
)
When displaying top images with a concept localization map, coeffs_u still determines the
ranking and concept_maps determines only the overlay:
coeffs_u = craft.transform(model_images)
craft.display_top_images_per_concept(
display_images,
coeffs_u=coeffs_u,
concept_maps=concept_maps,
order=[0, 3, 7],
topk=3,
)
If order is omitted, display methods show only the concept channels that were computed.
Explicitly requesting an uncomputed NaN channel raises a ValueError. Signed localization
maps are displayed by absolute magnitude; attribution direction is not represented by the
current renderer.
Computational cost
Cost scales with concepts x images x perturbations because localization evaluates the
encoder and factorizer.encode() for each perturbation and selected concept. Rank concepts
by importance first, then localize a small subset. Reduce images, samples or designs, and
grid resolution for exploratory runs.
Model and display inputs
model_images must be model-ready, preprocessed inputs; perturbations operate in that
space. For example, zero after ImageNet normalization represents the dataset mean, not a
raw black pixel. display_images may instead contain human-readable RGB images, but must
have the same order and spatial correspondence as model_images.
Factorizer compatibility
Localization evaluates the fitted factorizer on unseen, perturbed activations. The
factorizer must therefore support out-of-sample encode(). Activations should not be
clipped merely to satisfy a factorizer because that would change the function being
explained.
Black-box scope
Input-to-concept localization supports black-box attribution methods only. Do not use
Integrated Gradients for localization: the NumPy factorizer.encode() path is not
differentiable. This does not prevent gradient-based concept importance, which follows
the separate concept-activation-to-task-prediction path.
Using a Different NMF Factorizer¶
By default, the standard Sklearn NMF is used to factorize the concepts. But other types of factorizers are supported, such as the ones provided by the Overcomplete project.
from overcomplete.optimization import SemiNMF
from xplique.concepts.torch.factorizer import OvercompleteFactorizer
nb_concepts=10
# Create a SemiNMF factorizer which allows negative activations
factorizer = OvercompleteFactorizer(
optimizer_class=SemiNMF,
nb_concepts=nb_concepts,
device=device
)
# Setup Craft to use this factorizer
craft = Craft(
latent_extractor=latent_extractor,
number_of_concepts=nb_concepts,
device=device,
factorizer=factorizer,
)
craft.fit(input_images)
Implementing Your Own Latent Extractor¶
If you're working with a model architecture that isn't supported out-of-the-box, you can implement your own latent extractor by following these steps:
1. Create a Custom LatentData Class¶
First, create a class that stores the intermediate activations from your model:
from xplique.concepts.latent_extractor import LatentData
import torch
class CustomLatentData(LatentData):
def __init__(self, fpn_outs: list, extraction_layer: int = 0):
super().__init__()
self.fpn_outs = fpn_outs
self.extraction_layer = extraction_layer
def get_activations(self, as_numpy: bool = True, keep_gradients: bool = False):
"""Extract activations from the specified layer."""
activations = self.fpn_outs[self.extraction_layer]
if not keep_gradients:
activations = activations.detach()
# Convert from (N, C, H, W) to (N, H, W, C) for Xplique
if len(activations.shape) == 4:
activations = activations.permute(0, 2, 3, 1)
if as_numpy:
activations = activations.cpu().numpy()
return activations
def set_activations(self, values: torch.Tensor) -> None:
"""Set activations back into the latent data structure."""
# Convert from (N, H, W, C) to (N, C, H, W)
if len(values.shape) == 4:
values = values.permute(0, 3, 1, 2)
self.fpn_outs[self.extraction_layer] = values
def to(self, device: torch.device) -> 'CustomLatentData':
"""Move latent data to specified device."""
self.fpn_outs = [fpn_out.to(device) for fpn_out in self.fpn_outs]
return CustomLatentData(self.fpn_outs, self.extraction_layer)
2. Create a Custom ExtractorBuilder¶
Next, implement a builder that splits your model into g(.) and h(.) functions:
import types
from xplique.concepts.latent_extractor import LatentExtractorBuilder
from xplique.concepts.torch.latent_extractor import TorchLatentExtractor
class CustomExtractorBuilder(LatentExtractorBuilder):
@classmethod
def build(
cls,
model,
device: str = 'cuda',
extraction_layer: int = -1,
batch_size: int = 1
) -> TorchLatentExtractor:
# Define g(.) function: input → latent activations
def g(self, x):
# Example: extract from backbone/feature pyramid
fpn_outs = self.backbone(x)
return CustomLatentData(
fpn_outs=list(fpn_outs),
extraction_layer=latent_extractor.extraction_layer
)
# Define h(.) function: latent activations → predictions
def h(self, latent_data: CustomLatentData):
fpn_outs = latent_data.fpn_outs
outputs = self.head(fpn_outs)
return outputs
# Bind g and h methods to the model
model.g = types.MethodType(g, model)
model.h = types.MethodType(h, model)
# Create output formatter (converts raw predictions to MultiBoxTensor)
output_formatter = CustomBoxFormatter()
# Build the latent extractor
latent_extractor = TorchLatentExtractor(
model,
model.g,
model.h,
latent_data_class=CustomLatentData,
output_formatter=output_formatter,
batch_size=batch_size,
device=device
)
# Store extraction layer for later use
latent_extractor.extraction_layer = extraction_layer
return latent_extractor
3. Use Your Custom Extractor with CRAFT¶
Once you have your custom extractor, you can use it just like the built-in ones:
from xplique.concepts import HolisticCraftTorch as Craft
# Build your custom latent extractor
latent_extractor = CustomExtractorBuilder.build(
model,
device="cuda",
extraction_layer=-1,
batch_size=16
)
# Use it with CRAFT
craft = Craft(
latent_extractor=latent_extractor,
number_of_concepts=10,
device="cuda"
)
# Fit and visualize concepts
craft.fit(input_images)
craft.display_images_per_concept(input_images[:5])
Key Points¶
- g(.) function: Maps input images to intermediate activations at a chosen layer
- h(.) function: Maps latent activations back to final predictions
- LatentData: Handles activation extraction with proper shape conversions (PyTorch uses channel-first, Xplique expects channel-last)
- Output formatter: Converts model predictions to
MultiBoxTensorformat for compatibility with Xplique
API Reference¶
HolisticCraft¶
Framework-agnostic CRAFT implementation for holistic model explanations.
__init__(self,
latent_extractor: xplique.concepts.latent_extractor.LatentExtractor,
number_of_concepts: int = 20,
device: str = None,
factorizer: Optional[Any] = None)¶
latent_extractor: xplique.concepts.latent_extractor.LatentExtractor,
number_of_concepts: int = 20,
device: str = None,
factorizer: Optional[Any] = None)
Parameters
-
latent_extractor : xplique.concepts.latent_extractor.LatentExtractor
Extractor that splits the model into encoder (input to activations) and decoder (activations to predictions) for concept extraction
-
number_of_concepts : int = 20
Number of concepts to extract via NMF decomposition
-
device : str = None
Device specification for tensor operations (framework-specific)
-
factorizer : Optional[Any] = None
Optional factorizer instance implementing the ConceptFactorizer protocol.
If None, creates a default SklearnNMFFactorizer with alpha_W=1e-2 and max_iter=200
check_if_fitted(self)¶
Checks if the factorization model has been fitted to input data.
compute_explanation_per_concept(self,
images: numpy.ndarray,
partial_explainer: xplique.concepts.holistic_craft.PartialExplainer,
class_id: Optional[int] = None,
confidence: Optional[float] = None,
verbose: bool = False) -> numpy.ndarray¶
images: numpy.ndarray,
partial_explainer: xplique.concepts.holistic_craft.PartialExplainer,
class_id: Optional[int] = None,
confidence: Optional[float] = None,
verbose: bool = False) -> numpy.ndarray
Compute explanations per concept using the provided explainer.
Parameters
-
images : numpy.ndarray
Input images as numpy arrays
-
partial_explainer : xplique.concepts.holistic_craft.PartialExplainer
PartialExplainer instance that creates an attribution explainer when called with model and batch_size arguments
-
class_id : Optional[int] = None
Target class ID for filtering detections
-
confidence : Optional[float] = None
Confidence threshold for filtering detections
-
verbose : bool = False
If True, prints progress information during processing
Return
-
explanations : numpy.ndarray
Concatenated explanations for all images, shape (N, H, W, n_concepts)
decode(self,
latent_data: xplique.concepts.latent_extractor.LatentData,
coeffs_u: Union[numpy.ndarray, Any]) -> xplique.commons.prediction_types.StructuredPrediction¶
latent_data: xplique.concepts.latent_extractor.LatentData,
coeffs_u: Union[numpy.ndarray, Any]) -> xplique.commons.prediction_types.StructuredPrediction
Decode concept coefficients back to predictions.
Parameters
-
latent_data : xplique.concepts.latent_extractor.LatentData
Single image's latent representation (not batched)
-
coeffs_u : Union[numpy.ndarray, Any]
Concept coefficients for reconstruction
Return
-
predictions : xplique.commons.prediction_types.StructuredPrediction
Predictions implementing StructuredPrediction protocol (has filter() and to_batched_tensor() methods). Concrete types are MultiBoxTensor for object detection or ClassifierTensor for classification.
display_concept_heatmap(self,
image: numpy.ndarray,
concept_heatmap: numpy.ndarray,
concept_idx: int,
ax: Any,
filter_percentile: int = 80,
clip_percentile: int = 5) -> None¶
image: numpy.ndarray,
concept_heatmap: numpy.ndarray,
concept_idx: int,
ax: Any,
filter_percentile: int = 80,
clip_percentile: int = 5) -> None
Overlay a single concept heatmap on a single image.
Parameters
-
image : numpy.ndarray
Single image as HWC numpy array, shape (H, W, C)
-
concept_heatmap : numpy.ndarray
Concept heatmap, shape (H', W') or (H', W', 1)
-
concept_idx : int
Index of the concept, used to select the colormap
-
ax : Any
Matplotlib axis on which to draw
-
filter_percentile : int = 80
Percentile used to filter the concept heatmap (only show concept if excess N-th percentile). Defaults to 80.
-
clip_percentile : int = 5
Percentile value to use if clipping is needed when drawing the concept, e.g a value of 1 will perform a clipping between percentile 1 and 99.
This parameter allows to avoid outliers in case of too extreme values.
Default to 5.
display_images_per_concept(self,
images: numpy.ndarray,
coeffs_u: Optional[numpy.ndarray] = None,
filter_percentile: int = 80,
clip_percentile: int = 5,
order: Optional[List[int]] = None,
concept_maps: Optional[numpy.ndarray] = None) -> matplotlib.figure.Figure¶
images: numpy.ndarray,
coeffs_u: Optional[numpy.ndarray] = None,
filter_percentile: int = 80,
clip_percentile: int = 5,
order: Optional[List[int]] = None,
concept_maps: Optional[numpy.ndarray] = None) -> matplotlib.figure.Figure
Display concept heatmaps overlaid on images.
Parameters
-
images : numpy.ndarray
Input images to visualize (array of shape (N, H, W, C) for tensorflow or (N, C, H, W) for pytorch)
-
coeffs_u : Optional[numpy.ndarray] = None
Optional pre-computed coefficients, shape (N, H, W, C) or (N, Tokens, C).
If None, coefficients will be computed via transform(images).
-
filter_percentile : int = 80
Percentile used to filter the concept heatmap (only show concept if excess N-th percentile). Defaults to 80.
-
clip_percentile : int = 5
Percentile value to use if clipping is needed when drawing the concept, e.g a value of 1 will perform a clipping between percentile 1 and 99.
This parameter allows to avoid outliers in case of too extreme values.
Default to 5.
-
order : Optional[List[int]] = None
Optional list of concept IDs to specify display order. If None, concepts are shown in sequential order
-
concept_maps : Optional[numpy.ndarray] = None
Optional concept localization maps of shape (N, H, W, n_concepts). When provided, maps are used for overlays while
coeffs_ukeeps its original meaning. Each concept channel must be either entirely finite or entirelyNaN. Iforderis None, only finite channels are displayed. Signed maps are displayed by absolute magnitude.
Return
-
fig : matplotlib.figure.Figure
matplotlib figure with len(images) rows and number_of_concepts columns
display_top_images_per_concept(self,
images: Union[numpy.ndarray, List[Any]],
topk: int = 3,
filter_percentile: int = 80,
clip_percentile: int = 5,
order: Optional[List[int]] = None,
coeffs_u: Optional[numpy.ndarray] = None,
concept_maps: Optional[numpy.ndarray] = None) -> matplotlib.figure.Figure¶
images: Union[numpy.ndarray, List[Any]],
topk: int = 3,
filter_percentile: int = 80,
clip_percentile: int = 5,
order: Optional[List[int]] = None,
coeffs_u: Optional[numpy.ndarray] = None,
concept_maps: Optional[numpy.ndarray] = None) -> matplotlib.figure.Figure
Display top N images per concept ranked by average activation.
Parameters
-
images : Union[numpy.ndarray, List[Any]]
Input images (as framework tensors or numpy arrays)
-
topk : int = 3
Number of top images to display per concept (default: 3)
-
filter_percentile : int = 80
Percentile threshold for filtering heatmaps (default: 80)
-
clip_percentile : int = 5
Percentile for clipping heatmap values (default: 5)
-
order : Optional[List[int]] = None
Optional list of concept IDs to specify display order
-
coeffs_u : Optional[numpy.ndarray] = None
Optional pre-computed concept coefficients. If None, will call self.transform(images) to compute them. Use this to pass the coefficients stored in factorization.coeffs_u after fit().
-
concept_maps : Optional[numpy.ndarray] = None
Optional concept localization maps of shape (N, H, W, n_concepts) used only for display overlays. Top-image ranking remains based on mean concept coefficients. Each concept channel must be either entirely finite or entirely
NaN. Iforderis None, only finite channels are displayed. Signed maps are displayed by absolute magnitude.
Return
-
fig : matplotlib.figure.Figure
matplotlib figure with topk rows and number_of_concepts columns
encode(self,
inputs: Union[numpy.ndarray, Any],
resize: Optional[Tuple[int, int]] = None,
differentiable: bool = False) -> List[xplique.concepts.latent_extractor.EncodedData]¶
inputs: Union[numpy.ndarray, Any],
resize: Optional[Tuple[int, int]] = None,
differentiable: bool = False) -> List[xplique.concepts.latent_extractor.EncodedData]
Encode inputs to latent data and concept coefficients.
Parameters
-
inputs : Union[numpy.ndarray, Any]
Input images to encode
-
resize : Optional[Tuple[int, int]] = None
Target size for resizing images
-
differentiable : bool = False
If True, preserves gradients for backpropagation using differentiable non-negative optimization. If False (default), uses standard NMF transform which is faster but does not preserve gradients.
Return
-
encoded_data : List[xplique.concepts.latent_extractor.EncodedData]
List of EncodedData named tuples, each containing: - latent_data: LatentData object with intermediate activations - coeffs_u: Concept coefficients (numpy array or tensor with gradients) When differentiable=False, coeffs_u are numpy arrays.
When differentiable=True, coeffs_u are framework tensors (torch.Tensor or tf.Tensor) with gradients preserved.
fit(self,
inputs,
class_id: int = 0)¶
inputs,
class_id: int = 0)
Fit NMF to extract concepts from latent activations.
Parameters
-
inputs : inputs
Input images to extract concepts from, as framework tensors or arrays
-
class_id : int = 0
Target class ID for object detection (used in factorization metadata)
get_topk_images_per_concept(self,
coeffs_u: numpy.ndarray,
topk: int = 3) -> numpy.ndarray¶
coeffs_u: numpy.ndarray,
topk: int = 3) -> numpy.ndarray
Return the indices of the top images for each concept, ranked by mean activation.
Parameters
-
coeffs_u : numpy.ndarray
Concept coefficients, shape (N, H, W, n_concepts)
-
topk : int = 3
Number of top images to return per concept (default: 3)
Return
-
top_image_ids : numpy.ndarray
Array of shape (n_concepts, topk) containing the indices of the top images for each concept, ranked by descending mean activation
latent_to_concept(self,
latent_data: xplique.concepts.latent_extractor.LatentData) -> numpy.ndarray¶
latent_data: xplique.concepts.latent_extractor.LatentData) -> numpy.ndarray
Transform latent data to concept coefficients.
Parameters
-
latent_data : xplique.concepts.latent_extractor.LatentData
Single image's latent representation containing activations
Return
-
coeffs_u : numpy.ndarray
Concept coefficients, shape (H, W, n_concepts)
latent_to_concept_differentiable(self,
latent_data: xplique.concepts.latent_extractor.LatentData) -> Any¶
latent_data: xplique.concepts.latent_extractor.LatentData) -> Any
Transform latent data to concept coefficients with gradient preservation.
Parameters
-
latent_data : xplique.concepts.latent_extractor.LatentData
Single image's latent representation containing activations
Return
-
coeffs_u : Any
Concept coefficients as framework tensor with gradients
make_concept_decoder(self,
latent_data: xplique.concepts.latent_extractor.LatentData) -> Any¶
latent_data: xplique.concepts.latent_extractor.LatentData) -> Any
Creates a concept decoder for gradient-based attribution.
Parameters
-
latent_data : xplique.concepts.latent_extractor.LatentData
Image-specific latent representation
Return
-
decoder : Any
ConceptDecoder instance with signature: (coeffs_u) -> predictions
make_concept_localizer(self,
concept_reducer: Union[str, Callable] = 'mean') -> xplique.concepts.holistic_craft._ConceptLocalizer¶
concept_reducer: Union[str, Callable] = 'mean') -> xplique.concepts.holistic_craft._ConceptLocalizer
Create a callable mapping input images to one score per concept.
Parameters
-
concept_reducer : Union[str, Callable] = 'mean'
Reduction used to turn each concept activation map into a concept activation score. Supported strings are
"mean","sum", and"max". A callable must return shape(batch_size, number_of_concepts).
Return
-
localizer : xplique.concepts.holistic_craft._ConceptLocalizer
Callable suitable for Xplique black-box explainers, returning float32 TensorFlow scores with shape
(batch_size, number_of_concepts).Expects channel-last image batches for either backend.
reduce_to_importance(self,
explanation: numpy.ndarray,
spatial_reducer: Optional[str] = 'max',
abs_before_reduce: bool = True,
aggregation_reducer: Optional[str] = 'mean') -> numpy.ndarray¶
explanation: numpy.ndarray,
spatial_reducer: Optional[str] = 'max',
abs_before_reduce: bool = True,
aggregation_reducer: Optional[str] = 'mean') -> numpy.ndarray
Reduce pre-computed concept explanations to global importance scores.
Parameters
-
explanation : numpy.ndarray
Per-concept explanations, shape (N, H, W, n_concepts), as returned by :meth:
compute_explanation_per_concept.
-
spatial_reducer : Optional[str] = 'max'
Reducer applied over the spatial dimensions (H, W) to collapse each image to a per-concept score vector. Either "min", "mean", "max", "sum", "median" or
Noneto skip. Default is "max".
-
abs_before_reduce : bool = True
Whether to take the absolute value of explanations before spatial reduction.
Default is True.
-
aggregation_reducer : Optional[str] = 'mean'
Reducer applied over the image dimension after spatial reduction to produce a single score per concept. Either "min", "mean", "max", "sum", "median" or
Noneto skip (returns per-image scores). Default is "mean".
Return
-
importances : numpy.ndarray
Importance scores for each concept, shape (n_concepts,).
reduce_to_prevalence(self,
explanation: numpy.ndarray) -> numpy.ndarray¶
explanation: numpy.ndarray) -> numpy.ndarray
Compute concept prevalence from pre-computed explanations.
Parameters
-
explanation : numpy.ndarray
Per-concept explanations, shape (N, H, W, n_concepts), as returned by :meth:
compute_explanation_per_concept.
Return
-
prevalence : numpy.ndarray
Fraction of images for which each concept is dominant, shape (n_concepts,).
Values sum to 1.
reduce_to_reliability(self,
explanation: numpy.ndarray,
accuracy: numpy.ndarray) -> numpy.ndarray¶
explanation: numpy.ndarray,
accuracy: numpy.ndarray) -> numpy.ndarray
Compute concept reliability from pre-computed explanations and per-image accuracy.
Parameters
-
explanation : numpy.ndarray
Per-concept explanations, shape (N, H, W, n_concepts), as returned by :meth:
compute_explanation_per_concept.
-
accuracy : numpy.ndarray
Per-image accuracy scores, shape (N,). For classification: 0.0 or 1.0.
For object detection: per-image IoU, AP, or any scalar correctness metric computed externally by the user.
Return
-
reliability : numpy.ndarray
Mean accuracy per dominant-concept group, shape (n_concepts,).
Concepts with no dominant image get a reliability of 0.0.
transform(self,
inputs=None,
resize=None) -> numpy.ndarray¶
inputs=None,
resize=None) -> numpy.ndarray
Transform inputs to concept coefficients.
Parameters
-
inputs : inputs=None
Input images to transform. If None, returns stored coefficients from fit().
-
resize : resize=None
Target size for resizing images
Return
-
coeffs_u : numpy.ndarray
Concept coefficients for the inputs (or stored coefficients if inputs=None)
PartialExplainer¶
Wrapper for explainer classes to enable deferred instantiation.
__init__(self,
explainer_class,
**kwargs)¶
explainer_class,
**kwargs)
Parameters
-
explainer_class : explainer_class
The explainer class to instantiate (e.g., GradientInput, SobolAttributionMethod).
Must be callable and accept 'model' and 'batch_size' as keyword arguments.
-
kwargs : **kwargs
Configuration arguments for the explainer (e.g., operator, reducer, grid_size).
Should NOT include 'model' or 'batch_size' as these will be provided during instantiation.