---
myst:
html_meta:
description: "Use CBaseline backgrounds with TreeIG, UnifiedIG, and SHAP while preserving the appropriate rows, weights, and output scale."
---
# Integrated Gradients and SHAP integrations
CBaseline constructs the reference distribution; the attribution engine
explains predictions against it. Use calibrated weights when supported and
equal-weight selection when the interface accepts only a background matrix.
## Shared regression example
Run this setup before the TreeIG and SHAP sections. Install `treeig`,
`scikit-learn`, and `shap` in addition to CBaseline.
```python
import numpy as np
from sklearn.tree import DecisionTreeRegressor
from cbaseline import background
rng = np.random.default_rng(7)
X_train = rng.normal(size=(500, 3))
y_train = X_train[:, 0] ** 2 + X_train[:, 1] - 0.5 * X_train[:, 2]
model = DecisionTreeRegressor(max_depth=4, random_state=0).fit(X_train, y_train)
X_eval = X_train[:3]
f_train = model.predict(X_train)
f0 = float(f_train.mean())
```
## TreeIG: calibrated baseline distributions
[TreeIG](https://github.com/LudgerHentschel/treeig) accepts a CBaseline
`Background` directly and reads its rows and weights. This is the preferred
way to pass a calibrated distribution to its CPU interface.
```python
from treeig import TreeIG
wb = background(f_train, f0, X_train, weighting="calibrated")
ig = TreeIG(model, baseline=wb)
result = ig.explain(X_eval)
achieved = np.average(wb.predictions, axis=0, weights=wb.weights)
np.testing.assert_allclose(
result.values.sum(axis=1), ig.model_output(X_eval) - achieved, atol=1e-8
)
print("Reference gap:", achieved - f0)
print("Effective sample size:", wb.diagnostics["calibrated_ess"])
```
The equivalent explicit constructor is
`TreeIG(model, baseline=wb.rows, baseline_weights=wb.weights)`.
Pass the entire distribution through the engine to benefit from its baseline
batching. Averaging the feature rows into a single baseline generally changes
the output reference. See [TreeIG's baseline guide](https://ludgerhentschel.github.io/treeig/baselines.html).
For classification, use TreeIG's output conventions to obtain reference
scores; do not assume `model.predict` returns the scores TreeIG attributes.
Follow [choosing the reference prediction](reference-predictions.md) for
logits and class centering, and verify the output function on baseline rows.
## UnifiedIG and other Integrated Gradients engines
[UnifiedIG](https://ludgerhentschel.github.io/unifiedig/) accepts the same
CBaseline `Background` directly. Install `unifiedig` to run this section with
the shared regression setup and calibrated `wb` above:
```python
import unifiedig as uig
explanation = uig.Explainer(model, wb)(X_eval)
achieved = np.average(wb.predictions, axis=0, weights=wb.weights)
np.testing.assert_allclose(explanation.base_values, achieved, atol=1e-8)
np.testing.assert_allclose(
explanation.base_values + explanation.values.sum(axis=1),
model.predict(X_eval), atol=1e-8,
)
```
The background carries its aligned rows and weights through the wrapper to the
selected backend. See [UnifiedIG's baseline guide](https://ludgerhentschel.github.io/unifiedig/baselines.html)
and [model support](https://ludgerhentschel.github.io/unifiedig/supported-models.html)
when adapting this regression example to another model or output scale.
For an engine supporting only a single baseline at a time, the following
adapter makes the required averaging explicit. `attribute_one(X_eval, row)`
is a caller-supplied function returning an attribution array for one baseline;
it must explain the same output used to construct `wb`.
```python
def average_baseline_attributions(attribute_one, X_eval, wb):
total = None
weights = wb.weights / wb.weights.sum()
for row, weight in zip(wb.rows, weights):
if weight == 0:
continue
values = np.asarray(attribute_one(X_eval, row))
contribution = weight * values
total = contribution if total is None else total + contribution
return total
```
Prefer native weighted batching when available. This adapter illustrates the
weighted-averaging contract for other attribution engines. If a backend
only supports equal-weight distributions, construct `weighting="equal"`
instead. Numerical IG needs its own integration-accuracy checks in addition
to CBaseline's mean-neutrality check.
## SHAP: deterministic equal-weight backgrounds
SHAP is a first-class use case through its unweighted background interfaces.
Construct a fixed-size subset and explicitly retain every selected row in
the masker:
```python
import shap
bg = background(f_train, f0, X_train, weighting="equal", size=100)
masker = shap.maskers.Independent(bg.rows, max_samples=len(bg.rows))
explainer = shap.Explainer(model.predict, masker, algorithm="permutation", seed=0)
phi = explainer(X_eval)
achieved = model.predict(bg.rows).mean()
np.testing.assert_allclose(phi.base_values, achieved, atol=1e-8)
np.testing.assert_allclose(
phi.base_values + phi.values.sum(axis=1), model.predict(X_eval), atol=1e-8
)
print("Requested reference:", f0)
print("Achieved reference:", achieved)
print("Neutrality norm:", bg.diagnostics["selected_neutrality_norm"])
```
An independent masker can otherwise subsample a large background, changing
the selected reference. `max_samples=len(bg.rows)` preserves the CBaseline
construction. The fixed seed controls permutation sampling separately from
CBaseline's deterministic selection.
The total is `model.predict(X_eval) - achieved`, which differs from
`model.predict(X_eval) - f0` by the reported neutrality residual. The SHAP
base value is a useful independent check that the intended background and
output scale survived the integration.
For supported tree models, an interventional TreeExplainer can use the same
explicit masker:
```python
tree_explainer = shap.TreeExplainer(
model, data=masker, feature_perturbation="interventional", model_output="raw"
)
tree_phi = tree_explainer(X_eval)
np.testing.assert_allclose(tree_phi.base_values, achieved, atol=1e-8)
np.testing.assert_allclose(
tree_phi.base_values + tree_phi.values.sum(axis=1),
model.predict(X_eval), atol=1e-6,
)
```
This example uses a regression tree. For classifiers, `raw` means the
model-specific raw output; it is not universally a logit. Use a score callable
with the general explainer when you need explicit centered-logit control.
Tree-path-dependent SHAP uses a different reference mechanism and should not
be assumed to implement this empirical background distribution.
Passing `wb.rows` alone discards calibrated weights. Standard matrix-based
SHAP workflows do not use `wb.weights`, so use the equal-weight construction
unless an explicitly weighted interface is available. Resampling the weighted
background is an optional approximation, discussed in
[background constructions](backgrounds.md).