Claude
Skills
Sign in
Back

bio-machine-learning-prediction-explanation

Included with Lifetime
$97 forever

Explains ML predictions on omics data with SHAP, LIME, and permutation importance, handling the correlated-feature trap, the conditional-vs-interventional Shapley choice, and the attribution-is-not-causation boundary. Use when interpreting an omics classifier, debugging shortcut/batch learning, or deciding whether an attribution ranking can be trusted as biology. For validated feature selection see machine-learning/biomarker-discovery; explanations are not a selection method.

General

What this skill does


## Version Compatibility

Reference examples tested with: numpy 1.26+, pandas 2.2+, scikit-learn 1.4+, shap 0.44+, lime 0.2+.

Before using code patterns, verify installed versions match. If versions differ:
- Python: `pip show <package>` then `help(module.function)` to check signatures

Two shap drifts: the Explanation-object plotting API arrived ~0.36 (new-style `shap.plots.*` take an `Explanation`, legacy `shap.summary_plot`/`dependence_plot` take numpy arrays -- mixing them is the most common runtime error); and `TreeExplainer(..., feature_perturbation='auto')` became the default in 0.47 (was `interventional`), so providing or omitting `data=` silently changes the estimand. Always set `feature_perturbation` explicitly. If code throws ImportError, AttributeError, or TypeError, introspect the installed package and adapt rather than retrying.

# Model Interpretation for Omics Classifiers

**"Which genes drive my classifier?"** -> Compute attributions, but treat them as a description of the model (not biology), choose the Shapley conditioning deliberately, and aggregate over correlated gene modules before ranking.
- Tree models: `shap.TreeExplainer(model, data=background, feature_perturbation='interventional')`
- Model-agnostic local: `lime.lime_tabular.LimeTabularExplainer`
- Model-reliance screen: `sklearn.inspection.permutation_importance`

## The Single Most Important Modern Insight -- Attributions Explain the Model, Not Biology, and Under Correlation the Algorithm Chooses How to Split Credit

A feature attribution describes the function the model learned on this training distribution; it is not a measurement of biology. A high-SHAP gene can be a pure correlate of a batch, scanner, or library-prep signal the model exploited (DeGrave 2021 is the canonical proof). And because genes co-express in tight modules, the attribution algorithm has genuine freedom in how it splits credit among correlated genes -- the choice of conditioning (`tree_path_dependent`/conditional vs `interventional`/marginal) is not a cosmetic knob, it changes *which* genes get credit, and it is a live methodological controversy with no universally correct answer (Janzing 2020). The operational rule: SHAP/LIME rankings are a debugging and hypothesis-generation tool, never a validated biomarker-selection criterion, and within a co-expression module the ordering is not a finding.

## Method Taxonomy

| Method | What it estimates | Correlated-feature behavior | Cost | Best use |
|--------|-------------------|------------------------------|------|----------|
| TreeSHAP `tree_path_dependent` | Conditional Shapley via tree coverage; approximates E[f \| x_S] | Can give nonzero credit to a feature the model never uses (correlation leak); no background needed | Fast, exact for this estimand | Fast cohort summaries when conditional semantics are acceptable |
| TreeSHAP `interventional` | Marginal/do-operator Shapley; features replaced from a background | Zero credit to unused features even if correlated | Scales with background size (~100-1000) | "What the model actually uses"; most defensible default |
| KernelSHAP | Model-agnostic Shapley via masking; assumes independence | Masking lands off-manifold under correlation; corrupted | Expensive | Last resort for non-tree/non-net models |
| DeepSHAP / GradientSHAP | SHAP for nets via backprop relative to a background | Background-dependent under correlation | Moderate | Neural omics models |
| LinearExplainer | Exact Shapley for linear models | `interventional` vs `correlation_dependent` give different values | Cheap | Penalized linear models; choose the mode |
| LIME | Local sparse linear surrogate on perturbed samples | Off-manifold perturbations; unstable across seeds | Moderate | Eyeballing one local prediction, never global ranking |
| Permutation importance | Drop in score when a feature is shuffled | Shuffling A while correlated B intact zeros BOTH; extrapolates | n_repeats x n_features | Global screen on decorrelated features |
| Conditional permutation (Strobl) | Importance permuting within correlated strata | Fairer among correlated predictors | Higher | RF importance under correlation |

## The Correlated-Feature / Conditional-vs-Marginal Problem (the core)

When Shapley values "drop" a feature subset, they replace it by some distribution, and two incompatible choices exist:

- **Conditional / observational** (`tree_path_dependent`): dropped features drawn from `p(x_dropped | x_S)`. A feature the model *never uses* can still receive nonzero attribution purely because it is correlated with a used feature. So **high SHAP does not mean the model relies on this gene.**
- **Marginal / interventional** (`interventional`): dropped features drawn from the marginal `p(x_dropped)`, i.e. `do(x_dropped = background)`. Features the model genuinely ignores get **exactly zero**, even if correlated. Janzing 2020 argues this is the principled "drop" for attribution; it is why modern SHAP added and (pre-0.47) defaulted to it.

There is no free lunch: every method either extrapolates off-manifold (interventional SHAP, unrestricted permutation -- Hooker 2021's "no free variable importance") or leaks credit through correlation (conditional SHAP). Decide which pathology the question can tolerate, and **aggregate attributions over co-expression modules before ranking** -- "gene A ranked above gene B" within a correlated module is governed by off-manifold value-function behavior, not biology.

## Decision Tree by Scenario

| Scenario | Recommended approach | Why |
|----------|---------------------|-----|
| "Which genes is my model actually keying on" (debug shortcuts) | TreeSHAP `interventional` with a representative background | Gives unused genes zero; reveals true reliance |
| "Which genes are informative about the outcome here" (descriptive) | TreeSHAP `tree_path_dependent`, but never call it model reliance | Conditional semantics answer the descriptive question |
| Ranking importance across correlated genes | Aggregate \|SHAP\| within co-expression clusters first | Within-module order is arbitrary |
| Penalized linear model | `LinearExplainer` (choose `interventional` vs `correlation_dependent`) | Or just read the coefficients -- the model is its own explanation |
| One local prediction, communication | LIME or SHAP waterfall, pinned seed, labeled model-internal | Local surrogate; not global, not reproducible across seeds |
| "I want to pick a biomarker panel" | -> machine-learning/biomarker-discovery | SHAP ranking is not validated selection (no FDR, no replication) |
| High-stakes clinical decision | Prefer an inherently interpretable model | Sparse linear/rule list is exact; avoids the conditional-vs-marginal ambiguity (Rudin 2019) |

## SHAP TreeExplainer (set the conditioning explicitly)

**Goal:** Attribute a tree model's predictions to features with a chosen, stated estimand.

**Approach:** Pass a background and `feature_perturbation='interventional'` for "what the model uses," or omit the background and use `tree_path_dependent` for the conditional/descriptive view. The default `'auto'` flips between them based on whether `data=` is given.

```python
import shap
import numpy as np

# Interventional ('what the model uses'): needs a background (~100-1000 rows).
background = shap.utils.sample(X_train, 200)
explainer = shap.TreeExplainer(model, data=background, feature_perturbation='interventional')
sv = explainer(X_test)                                  # modern Explanation object

# Aggregate over correlated modules BEFORE ranking (clusters = a precomputed gene->module map).
mean_abs = np.abs(sv.values).mean(axis=0)
module_importance = {}
for gene, m in zip(X_test.columns, mean_abs):
    module_importance[clusters[gene]] = module_importance.get(clusters[gene], 0) + m
```

## Attribution Is Not Causation, Mechanism, or a Validated Biomarker

Three layers of "not": (1) **not biology** -- the attribution describes the model, which may have expl

Related in General