Skill Tracer
Skills › Scientific Agent Skills › shap › troubleshooting.md

troubleshooting.md

460 lines · skills/shap/references/troubleshooting.md at 5f2bc03

Open on GitHub
1# SHAP Troubleshooting
2
3Diagnose SHAP problems from environment and semantics outward. Do not start by disabling checks or changing tolerances.
4

SHAP Troubleshooting

Diagnose SHAP problems from environment and semantics outward. Do not start by disabling checks or changing tolerances.

Raw lines 1 to 4
5## Minimal Diagnostic Packet
6
7Run without printing environment variables:
8
9```python
10import importlib.metadata
11import platform
12from pathlib import Path
14import numpy as np
15import shap
17print("Python:", platform.python_version())
18print("SHAP:", shap.__version__)
19print("NumPy:", np.__version__)
20print("SHAP import path:", Path(shap.__file__).resolve())
22for package in [
23 "pandas",
24 "scikit-learn",
25 "xgboost",
26 "lightgbm",
27 "catboost",
28 "tensorflow",
29 "keras",
30 "torch",
31]:
32 try:
33 print(f"{package}:", importlib.metadata.version(package))
34 except importlib.metadata.PackageNotFoundError:
35 pass
36```
38For an explanation:
40```python
41print("values:", np.shape(explanation.values))
42print("base_values:", np.shape(explanation.base_values))
43print("data:", np.shape(explanation.data))
44print("feature_names:", explanation.feature_names)
45print("output_names:", explanation.output_names)
46print("model input:", X_eval.shape)
47```
49Do not include sensitive row values in a public bug report.

Minimal Diagnostic Packet

Run without printing environment variables:

import importlib.metadata
import platform
from pathlib import Path

import numpy as np
import shap

print("Python:", platform.python_version())
print("SHAP:", shap.__version__)
print("NumPy:", np.__version__)
print("SHAP import path:", Path(shap.__file__).resolve())

for package in [
    "pandas",
    "scikit-learn",
    "xgboost",
    "lightgbm",
    "catboost",
    "tensorflow",
    "keras",
    "torch",
]:
    try:
        print(f"{package}:", importlib.metadata.version(package))
    except importlib.metadata.PackageNotFoundError:
        pass

For an explanation:

print("values:", np.shape(explanation.values))
print("base_values:", np.shape(explanation.base_values))
print("data:", np.shape(explanation.data))
print("feature_names:", explanation.feature_names)
print("output_names:", explanation.output_names)
print("model input:", X_eval.shape)

Do not include sensitive row values in a public bug report.

Raw lines 5 to 50
51## Installation and Import Problems
53SHAP 0.52.0 requires Python 3.12+ and NumPy 2+.
55```bash
56uv run python --version
57uv pip show shap
58uv pip tree
59```
61If Python 3.11 is required, pin SHAP 0.51.0. For Python 3.9/3.10, 0.49.1 is the final compatible line; upgrading Python is preferable.

Installation and Import Problems

SHAP 0.52.0 requires Python 3.12+ and NumPy 2+.

uv run python --version
uv pip show shap
uv pip tree

If Python 3.11 is required, pin SHAP 0.51.0. For Python 3.9/3.10, 0.49.1 is the final compatible line; upgrading Python is preferable.

Raw lines 51 to 62
63### Wrong module imported
65Never name a local script or directory:
67- `shap.py`;
68- `numpy.py`;
69- `pandas.py`;
70- `xgboost.py`;
71- `lightgbm.py`;
72- `joblib.py`;
73- `sklearn.py`.
75Local files can shadow installed packages. Verify:
77```python
78from pathlib import Path
79import shap
81print(Path(shap.__file__).resolve())
82```
84The path should point into the intended environment's installed package, not the project working directory.
86After renaming a shadowing module, remove only its corresponding local `__pycache__` entries and restart Python.

Wrong module imported

Never name a local script or directory:

  • shap.py;
  • numpy.py;
  • pandas.py;
  • xgboost.py;
  • lightgbm.py;
  • joblib.py;
  • sklearn.py.

Local files can shadow installed packages. Verify:

from pathlib import Path
import shap

print(Path(shap.__file__).resolve())

The path should point into the intended environment's installed package, not the project working directory.

After renaming a shadowing module, remove only its corresponding local __pycache__ entries and restart Python.

Raw lines 63 to 87
88## Shape Problems

Shape Problems

Raw lines 88 to 89
90### Old list logic fails
92Since 0.45, one-input multi-output explainers return an array with outputs last:
94```python
95class_exp = explanation[..., class_index]
96```
98Do not use:
100```python
101class_exp = explanation[class_index] # selects a sample
102```

Old list logic fails

Since 0.45, one-input multi-output explainers return an array with outputs last:

class_exp = explanation[..., class_index]

Do not use:

class_exp = explanation[class_index]  # selects a sample
Raw lines 90 to 103
104### Plot expects 2-D values
106Select an output:
108```python
109print(explanation.values.shape)
110single_output = explanation[..., output_index]
111assert single_output.values.ndim == 2
112shap.plots.beeswarm(single_output)
113```

Plot expects 2-D values

Select an output:

print(explanation.values.shape)
single_output = explanation[..., output_index]
assert single_output.values.ndim == 2
shap.plots.beeswarm(single_output)
Raw lines 104 to 114
115### Waterfall expects one row
117```python
118single_output = explanation[..., output_index]
119shap.plots.waterfall(single_output[row_index])
120```

Waterfall expects one row

single_output = explanation[..., output_index]
shap.plots.waterfall(single_output[row_index])
Raw lines 115 to 121
122### Feature count mismatch
124Check:
126```python
127assert explanation.values.shape[1] == X_eval.shape[1]
128assert len(explanation.feature_names) == X_eval.shape[1]
129```
131Likely causes:
133- preprocessing was applied to only one of background/evaluation data;
134- columns are reordered;
135- one-hot categories differ;
136- the model receives a NumPy array while names came from a different DataFrame;
137- an identifier/target column was accidentally included.

Feature count mismatch

Check:

assert explanation.values.shape[1] == X_eval.shape[1]
assert len(explanation.feature_names) == X_eval.shape[1]

Likely causes:

  • preprocessing was applied to only one of background/evaluation data;
  • columns are reordered;
  • one-hot categories differ;
  • the model receives a NumPy array while names came from a different DataFrame;
  • an identifier/target column was accidentally included.
Raw lines 122 to 138
139## Additivity Failures
141An additivity failure means the selected SHAP values do not reconstruct the selected output within expected tolerance.

Additivity Failures

An additivity failure means the selected SHAP values do not reconstruct the selected output within expected tolerance.

Raw lines 139 to 142
143### Diagnostic order
1451. Confirm rows and order match.
1462. Confirm transformed columns, order, dtype, missingness, and sparse/dense form match model fitting.
1473. Confirm model artifact and explainer refer to the same fitted model.
1484. Confirm output index and units.
1495. Recompute on one row.
1506. Use an explicit, representative background.
1517. Upgrade/downgrade only after checking model-library compatibility notes.
1528. Test a minimal model/dataset reproducer.
154Explicit check for one tabular output:
156```python
157values = np.asarray(explanation.values)
158base = np.asarray(explanation.base_values)
159reconstructed = base + values.sum(axis=1)
161error = reconstructed - expected_output
162print("max abs error:", np.max(np.abs(error)))
163print("mean abs error:", np.mean(np.abs(error)))
164```

Diagnostic order

  1. Confirm rows and order match.
  2. Confirm transformed columns, order, dtype, missingness, and sparse/dense form match model fitting.
  3. Confirm model artifact and explainer refer to the same fitted model.
  4. Confirm output index and units.
  5. Recompute on one row.
  6. Use an explicit, representative background.
  7. Upgrade/downgrade only after checking model-library compatibility notes.
  8. Test a minimal model/dataset reproducer.

Explicit check for one tabular output:

values = np.asarray(explanation.values)
base = np.asarray(explanation.base_values)
reconstructed = base + values.sum(axis=1)

error = reconstructed - expected_output
print("max abs error:", np.max(np.abs(error)))
print("mean abs error:", np.mean(np.abs(error)))
Raw lines 143 to 165
166### Common output mismatch
168For an XGBoost classifier, default TreeExplainer output may be a raw margin:
170```python
171raw_margin = model.predict(dmatrix, output_margin=True)
172```
174Comparing raw-margin SHAP reconstruction to `predict_proba` will fail conceptually even if both implementations are correct.
176For probability explanations, configure:
178```python
179explainer = shap.TreeExplainer(
180 model,
181 data=background,
182 feature_perturbation="interventional",
183 model_output="probability",
185```

Common output mismatch

For an XGBoost classifier, default TreeExplainer output may be a raw margin:

raw_margin = model.predict(dmatrix, output_margin=True)

Comparing raw-margin SHAP reconstruction to predict_proba will fail conceptually even if both implementations are correct.

For probability explanations, configure:

explainer = shap.TreeExplainer(
    model,
    data=background,
    feature_perturbation="interventional",
    model_output="probability",
)
Raw lines 166 to 186
187### Do not immediately disable checks
189`check_additivity=False` is appropriate only after:
191- identifying a documented numerical or model-library limitation;
192- quantifying the discrepancy;
193- confirming it is acceptable for the use case;
194- recording the decision.
196It is not a fix for huge, non-finite, or wrong-unit values.

Do not immediately disable checks

check_additivity=False is appropriate only after:

  • identifying a documented numerical or model-library limitation;
  • quantifying the discrepancy;
  • confirming it is acceptable for the use case;
  • recording the decision.

It is not a fix for huge, non-finite, or wrong-unit values.

Raw lines 187 to 197
198### Finite values and label-dependent loss
200Require finite predictions, base values, contributions, and tolerances before trusting a reconstruction. NaN/Infinity cannot be repaired by larger tolerances. A tree-loss explanation may have callable `base_values`; evaluate `loss_explainer.expected_value(label)` for each row's encoded label before addition. Then check the exact model-specific loss, not just whether the call completed. See [workflows.md](workflows.md) for the explicit callable fallback and 0.52.0 sklearn loss limitations.

Finite values and label-dependent loss

Require finite predictions, base values, contributions, and tolerances before trusting a reconstruction. NaN/Infinity cannot be repaired by larger tolerances. A tree-loss explanation may have callable base_values; evaluate loss_explainer.expected_value(label) for each row's encoded label before addition. Then check the exact model-specific loss, not just whether the call completed. See workflows.md for the explicit callable fallback and 0.52.0 sklearn loss limitations.

Raw lines 198 to 201
202### Background size appears ineffective
204Inspect `len(explainer.data)` or `len(explainer.masker.data)`. A bare 250-row frame can be internally sampled to 100 rows. Retain a selected sample with `shap.maskers.Independent(background, max_samples=len(background))`; changing the cap can change the baseline as well as runtime.

Background size appears ineffective

Inspect len(explainer.data) or len(explainer.masker.data). A bare 250-row frame can be internally sampled to 100 rows. Retain a selected sample with shap.maskers.Independent(background, max_samples=len(background)); changing the cap can change the baseline as well as runtime.

Raw lines 202 to 205
206## Tree Model Problems

Tree Model Problems

Raw lines 206 to 207
208### Categorical splits
210Support differs across XGBoost, LightGBM, CatBoost, and scikit-learn categorical configurations. SHAP 0.49 added categorical-split support in the C++ library, and 0.52 tightened unsupported categorical handling in GPU/sklearn paths.
212XGBoost 3.4.1 defaults `enable_categorical=True`. SHAP 0.52.0 rejects that flag in the interventional path even on an all-numeric fixture. For a new numeric-only model, construct it with `enable_categorical=False` and validate predictions/explanations. For actual categorical models, retain the schema and use a supported path-dependent/raw or model-agnostic workflow; changing the flag or arbitrary integer coding is not a general fix. Tiny 3.4.1 numeric fixtures passed raw, probability, log-loss, `predict_proba`, multiclass, and interaction checks.
214If an error mentions categorical splits:
2161. capture SHAP and model-library versions;
2172. test CPU `TreeExplainer`;
2183. test one row;
2194. verify model-native prediction works on the same frame;
2205. consult the current release notes and open issues.
222Do not convert categories to arbitrary integer codes merely to silence the explainer; that can change model semantics.

Categorical splits

Support differs across XGBoost, LightGBM, CatBoost, and scikit-learn categorical configurations. SHAP 0.49 added categorical-split support in the C++ library, and 0.52 tightened unsupported categorical handling in GPU/sklearn paths.

XGBoost 3.4.1 defaults enable_categorical=True. SHAP 0.52.0 rejects that flag in the interventional path even on an all-numeric fixture. For a new numeric-only model, construct it with enable_categorical=False and validate predictions/explanations. For actual categorical models, retain the schema and use a supported path-dependent/raw or model-agnostic workflow; changing the flag or arbitrary integer coding is not a general fix. Tiny 3.4.1 numeric fixtures passed raw, probability, log-loss, predict_proba, multiclass, and interaction checks.

If an error mentions categorical splits:

  1. capture SHAP and model-library versions;
  2. test CPU TreeExplainer;
  3. test one row;
  4. verify model-native prediction works on the same frame;
  5. consult the current release notes and open issues.

Do not convert categories to arbitrary integer codes merely to silence the explainer; that can change model semantics.

Raw lines 208 to 223
224### Nullable pandas dtypes
226SHAP 0.52 fixed a TreeExplainer crash with pandas nullable dtypes. If constrained to an older release, convert only after checking that missing values and category semantics remain identical:
228```python
229print(X_eval.dtypes)
230```
232Prefer upgrading within the project's compatibility window.

Nullable pandas dtypes

SHAP 0.52 fixed a TreeExplainer crash with pandas nullable dtypes. If constrained to an older release, convert only after checking that missing values and category semantics remain identical:

print(X_eval.dtypes)

Prefer upgrading within the project's compatibility window.

Raw lines 224 to 233
234### A small constant reconstruction offset
236For scikit-learn forests, prediction converts inputs to float32, but that alone does not guarantee native SHAP threshold parity: the 0.52.0 independent-tree implementation stores thresholds as float. The bundled seed-3/150-background-row configuration still has about a 2.8e-5 reconstruction offset after matching float32 inputs. Its baseline agrees with the model's background mean, so changing the baseline or subtracting that offset would conceal an attribution error. The report rejects this configuration. Use a separately validated model-agnostic explainer for the same output/background when exact tree reconstruction fails; do not silently switch the explanation game or relax tolerance.

A small constant reconstruction offset

For scikit-learn forests, prediction converts inputs to float32, but that alone does not guarantee native SHAP threshold parity: the 0.52.0 independent-tree implementation stores thresholds as float. The bundled seed-3/150-background-row configuration still has about a 2.8e-5 reconstruction offset after matching float32 inputs. Its baseline agrees with the model's background mean, so changing the baseline or subtracting that offset would conceal an attribution error. The report rejects this configuration. Use a separately validated model-agnostic explainer for the same output/background when exact tree reconstruction fails; do not silently switch the explanation game or relax tolerance.

Raw lines 234 to 237
238### Missing values
240Verify:
242- training and explanation use the same missing-value sentinel;
243- background includes realistic missingness;
244- the model's default missing branch is preserved;
245- CPU/GPU outputs agree on a small fixture.

Missing values

Verify:

  • training and explanation use the same missing-value sentinel;
  • background includes realistic missingness;
  • the model's default missing branch is preserved;
  • CPU/GPU outputs agree on a small fixture.
Raw lines 238 to 246
247### Path-dependent NaNs
249Small or inconsistent path-dependent backgrounds/model counts can cause failures in older versions. SHAP 0.51 included a path-dependent NaN fix. Reproduce on the latest compatible patch before working around it.

Path-dependent NaNs

Small or inconsistent path-dependent backgrounds/model counts can cause failures in older versions. SHAP 0.51 included a path-dependent NaN fix. Reproduce on the latest compatible patch before working around it.

Raw lines 247 to 250
251## Pipeline Problems

Pipeline Problems

Raw lines 251 to 252
253### DataFrame lost inside callable
255A model-agnostic masker may call the model with an array. If the pipeline requires column names:
257```python
258columns = raw_background.columns.tolist()
260def model_fn(array):
261 frame = pd.DataFrame(array, columns=columns)
262 return pipeline.predict_proba(frame)
263```
265This numeric-column pattern restores names after delta masking. Mixed categorical/object inputs can fail earlier in the masker's numeric invariance checks; use a tested domain masker or a validated numeric transformed representation. Restoring a DataFrame alone does not solve that failure.

DataFrame lost inside callable

A model-agnostic masker may call the model with an array. If the pipeline requires column names:

columns = raw_background.columns.tolist()

def model_fn(array):
    frame = pd.DataFrame(array, columns=columns)
    return pipeline.predict_proba(frame)

This numeric-column pattern restores names after delta masking. Mixed categorical/object inputs can fail earlier in the masker's numeric invariance checks; use a tested domain masker or a validated numeric transformed representation. Restoring a DataFrame alone does not solve that failure.

Raw lines 253 to 266
267### Transformed names are wrong
269```python
270names = preprocessor.get_feature_names_out()
271X_eval_t = preprocessor.transform(X_eval)
272assert X_eval_t.shape[1] == len(names)
273```
275Inspect the transformer version and fitted categories. Do not borrow names from a separately fitted preprocessor.

Transformed names are wrong

names = preprocessor.get_feature_names_out()
X_eval_t = preprocessor.transform(X_eval)
assert X_eval_t.shape[1] == len(names)

Inspect the transformer version and fitted categories. Do not borrow names from a separately fitted preprocessor.

Raw lines 267 to 276
277### Sparse matrices
279Model-agnostic explainers can become expensive or densify data. Test memory on a small batch. Keep sparse input only if the selected explainer and model callable support it end to end.

Sparse matrices

Model-agnostic explainers can become expensive or densify data. Test memory on a small batch. Keep sparse input only if the selected explainer and model callable support it end to end.

Raw lines 277 to 280
281## DeepExplainer Problems

DeepExplainer Problems

Raw lines 281 to 282
283### Unsupported operation / additivity assertion
285Likely causes:
287- unsupported framework operator;
288- multiple outputs not sliced as expected;
289- stochastic training mode;
290- custom layer;
291- model returns a tuple/dictionary;
292- background and input structures differ.
294PyTorch:
296First switch the PyTorch `nn.Module` to evaluation mode with its standard `eval` method. This is a framework state change, not Python's built-in code-evaluation function.
298```python
299with torch.inference_mode():
300 output = model(test_batch)
301print(output.shape)
302```
304If gradients are needed by the explainer, do not wrap the explainer call itself in `torch.inference_mode()`.
306Try:
3081. a selected-output wrapper (for PyTorch, retain `(batch, 1)`);
3092. one input row;
3103. a smaller background;
3114. `GradientExplainer`;
3125. `PartitionExplainer` with a model callable.
314Document changed semantics when switching explainers.

Unsupported operation / additivity assertion

Likely causes:

  • unsupported framework operator;
  • multiple outputs not sliced as expected;
  • stochastic training mode;
  • custom layer;
  • model returns a tuple/dictionary;
  • background and input structures differ.

PyTorch:

First switch the PyTorch nn.Module to evaluation mode with its standard eval method. This is a framework state change, not Python's built-in code-evaluation function.

with torch.inference_mode():
    output = model(test_batch)
print(output.shape)

If gradients are needed by the explainer, do not wrap the explainer call itself in torch.inference_mode().

Try:

  1. a selected-output wrapper (for PyTorch, retain (batch, 1));
  2. one input row;
  3. a smaller background;
  4. GradientExplainer;
  5. PartitionExplainer with a model callable.

Document changed semantics when switching explainers.

Raw lines 283 to 315
316### Device mismatch
318Ensure model, background, and explained tensors use the same device and compatible dtype:
320```python
321device = next(model.parameters()).device
322background = background.to(device)
323to_explain = to_explain.to(device)
324```
326Move returned values to CPU only after explanation.

Device mismatch

Ensure model, background, and explained tensors use the same device and compatible dtype:

device = next(model.parameters()).device
background = background.to(device)
to_explain = to_explain.to(device)

Move returned values to CPU only after explanation.

Raw lines 316 to 327
328### Ranked outputs
330`ranked_outputs=k` returns values and indexes:
332```python
333values, indexes = explainer.shap_values(
334 X,
335 ranked_outputs=3,
337```
339Use `indexes` to label each row. Do not assume rank one is the same class across rows.

Ranked outputs

ranked_outputs=k returns values and indexes:

values, indexes = explainer.shap_values(
    X,
    ranked_outputs=3,
)

Use indexes to label each row. Do not assume rank one is the same class across rows.

Raw lines 328 to 340
341## Text and Image Problems

Text and Image Problems

Raw lines 341 to 342
343### Tokenizer mismatch
345Use the exact tokenizer revision paired with the model. Check truncation, padding, special tokens, and mask token.

Tokenizer mismatch

Use the exact tokenizer revision paired with the model. Check truncation, padding, special tokens, and mask token.

Raw lines 343 to 346
347### Image preprocessing mismatch
349The model callable must apply the same resize, channel order, range, and normalization used in validation:
351```python
352def model_fn(images):
353 return model(preprocess(images.copy()))
354```

Image preprocessing mismatch

The model callable must apply the same resize, channel order, range, and normalization used in validation:

def model_fn(images):
    return model(preprocess(images.copy()))
Raw lines 347 to 355
356### Explanations change drastically with masker
358This may be expected. Blur, inpainting, constants, and token masking define different hidden-feature operations. Report a sensitivity analysis instead of selecting one silently.

Explanations change drastically with masker

This may be expected. Blur, inpainting, constants, and token masking define different hidden-feature operations. Report a sensitivity analysis instead of selecting one silently.

Raw lines 356 to 359
360## Plotting Problems

Plotting Problems

Raw lines 360 to 361
362### Plot does not display
364For scripts:
366```python
367ax = shap.plots.beeswarm(explanation, show=False)
368ax.figure.savefig("beeswarm.png", dpi=200, bbox_inches="tight")
369plt.close(ax.figure)
370```
372For JavaScript force plots in notebooks:
374```python
375shap.plots.initjs()
376shap.plots.force(local_exp)
377```
379Use `matplotlib=True` for a static force plot.

Plot does not display

For scripts:

ax = shap.plots.beeswarm(explanation, show=False)
ax.figure.savefig("beeswarm.png", dpi=200, bbox_inches="tight")
plt.close(ax.figure)

For JavaScript force plots in notebooks:

shap.plots.initjs()
shap.plots.force(local_exp)

Use matplotlib=True for a static force plot.

Raw lines 362 to 380
381### Overlapping or blank saved plots
383Capture and close each figure before creating another:
385```python
386ax = shap.plots.bar(explanation, show=False)
387figure = ax.figure
388figure.savefig("bar.png", bbox_inches="tight")
389plt.close(figure)
390```

Overlapping or blank saved plots

Capture and close each figure before creating another:

ax = shap.plots.bar(explanation, show=False)
figure = ax.figure
figure.savefig("bar.png", bbox_inches="tight")
plt.close(figure)
Raw lines 381 to 391
392### Colors or feature values missing
394Check `explanation.data` and `display_data`. Values-only objects cannot color by original feature values.

Colors or feature values missing

Check explanation.data and display_data. Values-only objects cannot color by original feature values.

Raw lines 392 to 395
396## Performance Problems
398Estimate cost before a large run:
400```text
401evaluation rows
402× model evaluations per row
403× background rows per masked evaluation
404× model latency
405```
407Reduce cost in this order:
4091. explain a predeclared row subset;
4102. restrict outputs;
4113. use a specialized explainer;
4124. reduce background after convergence testing;
4135. batch model calls;
4146. reduce permutations/evaluation budget with an accuracy check;
4157. use hierarchical partitioning when scientifically defensible.
417Do not subset only after viewing which explanations look interesting.

Performance Problems

Estimate cost before a large run:

evaluation rows
× model evaluations per row
× background rows per masked evaluation
× model latency

Reduce cost in this order:

  1. explain a predeclared row subset;
  2. restrict outputs;
  3. use a specialized explainer;
  4. reduce background after convergence testing;
  5. batch model calls;
  6. reduce permutations/evaluation budget with an accuracy check;
  7. use hierarchical partitioning when scientifically defensible.

Do not subset only after viewing which explanations look interesting.

Raw lines 396 to 418
419## Serialization Problems
421Model and explainer pickle/joblib/cloudpickle artifacts can execute code when loaded. Only load artifacts produced by a trusted pipeline and stored with integrity controls.
423For long-lived systems, prefer:
425- versioned model registry artifacts;
426- immutable background data identifiers;
427- code/config that rebuilds the explainer;
428- smoke tests for predictions and additivity;
429- explicit dependency locks.
431Do not accept an uploaded explainer file from an untrusted user.

Serialization Problems

Model and explainer pickle/joblib/cloudpickle artifacts can execute code when loaded. Only load artifacts produced by a trusted pipeline and stored with integrity controls.

For long-lived systems, prefer:

  • versioned model registry artifacts;
  • immutable background data identifiers;
  • code/config that rebuilds the explainer;
  • smoke tests for predictions and additivity;
  • explicit dependency locks.

Do not accept an uploaded explainer file from an untrusted user.

Raw lines 419 to 432
433## Minimal Bug Reproducer
435A useful issue report contains:
437- package versions;
438- platform and Python version;
439- minimal synthetic/public data;
440- model construction;
441- background construction;
442- explainer construction;
443- one failing row;
444- input/output shapes;
445- full exception;
446- expected model output and reconstructed output.
448Remove credentials, environment variables, private paths, and sensitive data.

Minimal Bug Reproducer

A useful issue report contains:

  • package versions;
  • platform and Python version;
  • minimal synthetic/public data;
  • model construction;
  • background construction;
  • explainer construction;
  • one failing row;
  • input/output shapes;
  • full exception;
  • expected model output and reconstructed output.

Remove credentials, environment variables, private paths, and sensitive data.

Raw lines 433 to 449
450## Sources
452- SHAP release notes: https://shap.readthedocs.io/en/latest/release_notes.html
453- TreeExplainer API: https://shap.readthedocs.io/en/latest/generated/shap.TreeExplainer.html
454- DeepExplainer API: https://shap.readthedocs.io/en/latest/generated/shap.DeepExplainer.html
455- Plot API: https://shap.readthedocs.io/en/latest/api.html#plots
456- SHAP GitHub issues: https://github.com/shap/shap/issues
458- Released source / upstream contract: https://github.com/shap/shap/blob/v0.52.0/shap/cext/tree_shap.h
459- Released source / upstream contract: https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html
460- XGBoost API (live docs may track a newer patch; native target here is 3.4.1): https://xgboost.readthedocs.io/en/stable/python/python_api.html

Sources

  • SHAP release notes: https://shap.readthedocs.io/en/latest/release_notes.html
  • TreeExplainer API: https://shap.readthedocs.io/en/latest/generated/shap.TreeExplainer.html
  • DeepExplainer API: https://shap.readthedocs.io/en/latest/generated/shap.DeepExplainer.html
  • Plot API: https://shap.readthedocs.io/en/latest/api.html#plots
  • SHAP GitHub issues: https://github.com/shap/shap/issues
  • Released source / upstream contract: https://github.com/shap/shap/blob/v0.52.0/shap/cext/tree_shap.h
  • Released source / upstream contract: https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html
  • XGBoost API (live docs may track a newer patch; native target here is 3.4.1): https://xgboost.readthedocs.io/en/stable/python/python_api.html
Raw lines 450 to 460