> ## Documentation Index
> Fetch the complete documentation index at: https://docs.synthefy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Explainability

> Explain Nori's predictions with SHAP / Shapley values, feature interactions, partial dependence, and feature selection.

Understand *why* Nori made a prediction: which features drove it, how they
interact, and how the model responds across a feature's range. Because
`NoriRegressor` is a scikit-learn estimator, it plugs directly into
[shapiq](https://github.com/mmschlk/shapiq) — a fast SHAP implementation with
native Shapley-interaction support — and the rest of the sklearn
interpretability ecosystem. The helpers in `synthefy_nori.interpretability` are
thin convenience wrappers, so you can also use the underlying tools directly.

<Icon icon="github" /> [`examples/interpretability_regression.py`](https://github.com/Synthefy/synthefy-nori/blob/main/examples/interpretability_regression.py)

<Note>
  Needs the `interpretability` extra, which pulls in `shapiq`. It is covered by
  the recommended line in [Installation](/setup/installation).
</Note>

Nori is **regression-only**, so every method below explains the regression
predictive mean.

## Quickstart

`get_nori_imputation_explainer` builds a `shapiq.TabularExplainer` that removes
features by **imputation** against a background set. The training context stays
fixed across coalitions, so each coalition is a single `predict` call and the
cost is set by `budget`.

```python theme={null}
from synthefy_nori import NoriRegressor
from synthefy_nori.interpretability.shapiq import get_nori_imputation_explainer

model = NoriRegressor(model="nori-30m").fit(X_train, y_train)

# index="k-SII", max_order=2 captures pairwise interactions;
# use index="SV", max_order=1 for plain Shapley values.
explainer = get_nori_imputation_explainer(model, X_train, index="k-SII", max_order=2)
sv = explainer.explain(X_test[:1], budget=128)
print(sv)
sv.plot_waterfall()   # additive contribution waterfall
```

<Frame caption="SHAP waterfall — each feature's additive push from the baseline to one prediction (sklearn diabetes).">
  <img src="https://mintcdn.com/synthefy/TZmP-8nR-MmlZxHf/assets/nori-shap-waterfall.png?fit=max&auto=format&n=TZmP-8nR-MmlZxHf&q=85&s=10d7ac27ddc3ddf0317ef7a50f6346bb" alt="SHAP waterfall plot for a single Nori prediction" width="990" height="971" data-path="assets/nori-shap-waterfall.png" />
</Frame>

## Feature interactions

With `index="k-SII", max_order=2` the explainer also surfaces **pairwise
interactions**, which you can view as a network:

```python theme={null}
kv = explainer.explain(X_test[:1], budget=256)
kv.plot_network()   # interaction graph
```

<Frame caption="k-SII interaction network — node = a feature's main effect, edge = the strength/sign of each pairwise interaction.">
  <img src="https://mintcdn.com/synthefy/TZmP-8nR-MmlZxHf/assets/nori-shap-interactions.png?fit=max&auto=format&n=TZmP-8nR-MmlZxHf&q=85&s=80ec052805cbd81cba982bec0d65b64d" alt="SHAP k-SII interaction network" width="797" height="750" data-path="assets/nori-shap-interactions.png" />
</Frame>

## Partial dependence / ICE

A global view of how the prediction shifts across a feature's range:

```python theme={null}
from synthefy_nori.interpretability.pdp import partial_dependence_plots

# 1-D effects for features 0 and 2; use kind="individual" or "both" for ICE.
partial_dependence_plots(model, X_test, features=[0, 2], kind="average")
```

Pass `(i, j)` tuples in `features` for 2-D interaction surfaces. Returns an
sklearn `PartialDependenceDisplay`.

<Frame caption="Partial dependence — average effect of bmi and bp on the predicted target (sklearn diabetes).">
  <img src="https://mintcdn.com/synthefy/TZmP-8nR-MmlZxHf/assets/nori-pdp.png?fit=max&auto=format&n=TZmP-8nR-MmlZxHf&q=85&s=33bf96452d8794114dc10b4550393549" alt="Partial dependence plots for bmi and bp" width="903" height="402" data-path="assets/nori-pdp.png" />
</Frame>

## Feature selection

Find a minimal feature subset that preserves cross-validated performance
(sequential selection):

```python theme={null}
from synthefy_nori.interpretability.feature_selection import feature_selection

res = feature_selection(model, X_train, y_train, n_features_to_select=5, cv=3)
print(res.selected_indices, res.selected_score_mean)
```

`n_features_to_select` accepts an int, a fraction, or `"auto"` (with `tol`). The
result also reports baseline-vs-selected CV scores.

<Note>
  Sequential selection re-fits Nori in-context on every CV split, so keep it to a
  few thousand samples and a modest feature count.
</Note>

## Configuration

<AccordionGroup>
  <Accordion title="get_nori_imputation_explainer parameters" icon="sliders">
    | Parameter   | Default      | Description                                                                                                                 |
    | ----------- | ------------ | --------------------------------------------------------------------------------------------------------------------------- |
    | `index`     | `"k-SII"`    | `"SV"` for plain Shapley values, or `"k-SII"` for k-Shapley interactions.                                                   |
    | `max_order` | `2`          | Maximum interaction order. `1` = single-feature attributions, `2` = pairwise.                                               |
    | `imputer`   | `"baseline"` | `"baseline"` runs one forward per coalition (recommended); `"marginal"` / `"conditional"` are multi-sample and much slower. |

    **Budget** trades accuracy for cost. Start at `128` and raise only if the
    explanation looks noisy: `<10` features → 64–128; 10–20 → 128–512; 20+ →
    512–2048.
  </Accordion>
</AccordionGroup>

## Good to know

* These run on the **local Python package** (`synthefy-nori`), not the hosted
  API.
* TabPFN's fast Shapley path reuses a KV-cache across coalitions; Nori's public
  package runs one forward per coalition, which is correct and
  budget-controlled, just not cache-accelerated.

## Next steps

<CardGroup cols={2}>
  <Card title="Categorical & Ordinal Targets" icon="list-ol" href="/nori/categorical-targets">
    Predict labels on a discrete scale instead of a continuous estimate.
  </Card>

  <Card title="Embeddings" icon="vector-square" href="/nori/embeddings">
    Target-aware row vectors for probing, clustering, and search.
  </Card>
</CardGroup>
