skills/shap/references/modalities.md
Structured inputs require structured maskers. Token deletion, image inpainting, and neural-network reference activations define different explanation games; do not reduce them to ordinary independent tabular columns without justification.
explainer = shap.Explainer(
model_fn,
masker,
output_names=output_names,
seed=7,
)
explanation = explainer(
inputs,
max_evals=evaluation_budget,
outputs=selected_outputs,
)
Use the model's tokenizer:
masker = shap.maskers.Text(tokenizer)
explainer = shap.Explainer(
model_fn,
masker,
algorithm="partition",
output_names=class_names,
seed=7,
)
all_outputs = explainer(texts)
class_exp = all_outputs[..., class_index]
shap.plots.text(class_exp)
The model callable should accept a batch of strings and return a 2-D numeric array (batch, outputs).
def model_fn(text_batch):
encoded = tokenizer(
list(text_batch),
padding=True,
truncation=True,
return_tensors="pt",
).to(device)
with torch.inference_mode():
logits = model(**encoded).logits
return logits.detach().cpu().numpy()
Decide whether to explain:
State the choice in every plot.
SHAP provides shap.models.TransformersPipeline:
wrapped = shap.models.TransformersPipeline(
classifier_pipeline,
rescale_to_logits=True,
)
masker = shap.maskers.Text(classifier_pipeline.tokenizer)
explainer = shap.Explainer(wrapped, masker, algorithm="partition")
explanation = explainer(texts)
rescale_to_logits=True changes the explained output. Do not compare these values directly with probability-space explanations.
Check:
SHAP can merge tokens hierarchically for display. A displayed phrase attribution may be a grouped coalition, not the sum from an independent-token game.
SHAP includes model wrappers such as:
TeacherForcing;TextGeneration;TopKLM.Generation explanations are target- and decoding-dependent. Record:
Do not describe one generation explanation as a general explanation of the language model.
shap.maskers.Image requires OpenCV (cv2), which is not installed by the plots extra. Add an OpenCV build compatible with the project's platform and dependency lock before using image maskers.
Wrap preprocessing inside the model callable so masked images receive the same transformations:
def image_model_fn(image_batch):
prepared = preprocess(image_batch.copy())
return model(prepared)
Choose an image masker:
masker = shap.maskers.Image(
"inpaint_telea",
shape=image_shape,
)
explainer = shap.Explainer(
image_model_fn,
masker,
output_names=class_names,
seed=7,
)
explanation = explainer(
images,
max_evals=500,
batch_size=50,
outputs=shap.Explanation.argsort.flip[:3],
)
shap.plots.image(explanation)
The current constructor also accepts blur specifications or constant mask values. Compare plausible alternatives:
inpaint = shap.maskers.Image("inpaint_telea", image_shape)
blur = shap.maskers.Image("blur(32,32)", image_shape)
zero = shap.maskers.Image(0, image_shape)
These answer different questions:
None guarantees an in-distribution counterfactual.
Image classifiers may have thousands of outputs. Use:
outputs=shap.Explanation.argsort.flip[:k]
The selected outputs can vary by row. Preserve output_indexes/output_names and never assume position zero refers to the same class for every image when ranked outputs are used.
Attribution overlays:
Test:
Before constructing the explainer, switch the PyTorch nn.Module to evaluation mode with its standard eval method. This disables training behavior such as dropout; it is not Python's built-in code-evaluation function.
background = training_tensor[background_indices].to(device)
to_explain = test_tensor[:batch_size].to(device)
explainer = shap.DeepExplainer(model, background)
values = explainer.shap_values(to_explain, check_additivity=True)
For layer-input attribution:
explainer = shap.DeepExplainer(
(model, model.feature_extractor.target_layer),
background,
)
Requirements:
Do not disable check_additivity merely to suppress unsupported-operation failures.
background = X_train[background_indices]
to_explain = X_test[:batch_size]
explainer = shap.DeepExplainer(model, background)
values = explainer.shap_values(to_explain, check_additivity=True)
SHAP 0.46 added NumPy 2, Keras 3, and TensorFlow 2.16 compatibility. Later versions may still have architecture-specific limitations. Verify installed TensorFlow/Keras versions against SHAP release notes and test a minimal batch.
For graph-specific use, TensorFlow may accept an (input_tensors, output_tensor) pair. The selected output tensor should be one-dimensional per sample.
GradientExplainerExpected gradients can be an alternative when DeepExplainer lacks an operator rule:
explainer = shap.GradientExplainer(
model,
background,
batch_size=50,
local_smoothing=0,
)
values = explainer.shap_values(
to_explain,
nsamples=200,
rseed=7,
)
Report:
nsamples;Since SHAP 0.45:
(samples, *input_shape);(samples, *input_shape, outputs);For DeepExplainer(ranked_outputs=k), the return is (values, indexes). The indexes tell which outputs were selected for each row.
Always inspect rather than branch on a remembered version:
if isinstance(values, list):
print([value.shape for value in values])
else:
print(values.shape)
Define the attribution unit:
Independent one-hot channels can create invalid bases. Prefer a masker or grouping that preserves valid sequence states. For motif-level claims, aggregate or test motifs explicitly and validate across background sequences.
For genomic models, background choice can represent:
Each implies a different baseline and scientific question.
For models with multiple inputs, use a compatible list/composite masker or framework explainer:
values = deep_explainer.shap_values([input_a, input_b])
Do not sum values across inputs unless they share the same additive output and the aggregation is documented. Keep names and shapes per input.
max_evals/nsamples only after validating shape and semantics.Approximation variance and model stochasticity are different. Put the model in deterministic inference mode before estimating explainer variability.
Text and image plots can reproduce sensitive input content. Before export: