Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,18 @@ jobs:
with:
files: ./coverage.xml
fail_ci_if_error: false

docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
- name: Install
run: |
python -m pip install --upgrade pip
pip install -e ".[dev,docs]"
- name: Build docs (strict)
run: mkdocs build --strict
59 changes: 59 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Docs

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

jobs:
docs:
name: Build documentation
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[dev,docs]"

- name: Build documentation
run: mkdocs build --strict

deploy:
name: Deploy to GitHub Pages
needs: docs
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest

permissions:
contents: write

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[dev,docs]"

- name: Deploy documentation
run: mkdocs gh-deploy --force
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ env/
.coverage
htmlcov/
coverage.xml
site/

# Editors / OS
.vscode/
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ All notable changes to this project are documented here. The format follows
## [Unreleased]

### Added
- Documentation site powered by `mkdocs-material` and `mkdocstrings`, featuring getting started guides, mathematical overviews for all explainers, consensus documentation, an extending guide, and full API reference (#16).
- Automated GitHub Actions deployment workflow for GitHub Pages on push to `main` and strict doc build verification in CI (#16).
- `Explanation.to_markdown()`: render explanations as Markdown tables with headlines and agreement summaries for reports, pull requests, and notebooks (#1).

## [0.1.0] - 2026-09-21
Expand Down
86 changes: 15 additions & 71 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Documentation](https://img.shields.io/badge/docs-mkdocs--material-blue.svg)](https://adaumsilva.github.io/XAI-framework/)
[![CI](https://github.com/adaumsilva/XAI-framework/actions/workflows/ci.yml/badge.svg)](https://github.com/adaumsilva/XAI-framework/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/adaumsilva/XAI-framework/branch/main/graph/badge.svg)](https://codecov.io/gh/adaumsilva/XAI-framework)
[![GitHub issues](https://img.shields.io/github/issues/adaumsilva/XAI-framework)](https://github.com/adaumsilva/XAI-framework/issues)
Expand All @@ -10,6 +11,8 @@
[![GitHub contributors](https://img.shields.io/github/contributors/adaumsilva/XAI-framework)](https://github.com/adaumsilva/XAI-framework/graphs/contributors)
[![GitHub stars](https://img.shields.io/github/stars/adaumsilva/XAI-framework?style=social)](https://github.com/adaumsilva/XAI-framework/stargazers)

> 📖 **Full Documentation & API Reference:** [https://adaumsilva.github.io/XAI-framework/](https://adaumsilva.github.io/XAI-framework/)

**One call, several attribution methods, and a measure of how much they agree.**

XAI-Framework is a model-agnostic explainable-AI library with its **own native
Expand Down Expand Up @@ -129,90 +132,31 @@ explain(model, X, instance=0,
### Examples

- [Consensus Explanation Walkthrough](examples/01_consensus_walkthrough.ipynb) — demonstrates local consensus explanations, agreement between explanation methods, disagreement cases, and global explanations.
### Use the explainer classes directly

```python
from xai_framework import (
CoalitionExplainer, LocalSurrogateExplainer, PermutationExplainer, ConsensusExplainer,
)

coal = CoalitionExplainer(model, X_train, n_background=50) # exact/sampling chosen automatically
surr = LocalSurrogateExplainer(model, X_train, n_samples=5000, categorical_features=["sex"])
perm = PermutationExplainer(model, X_train, metric="accuracy")

coal.explain_instance(x)
coal.explain(X_test.head(20)) # batch
coal.explain_global(X_test)
perm.explain_global(X_test, y_test)

consensus = ConsensusExplainer(model, X_train, methods=(coal, surr))
consensus.explain_instance(x)
```
## Documentation

### How the coalition explainer scales
Detailed guides, mathematical formulations, and API specifications are available in the [Documentation Site](https://adaumsilva.github.io/XAI-framework/):

Cost per local explanation is `n_coalitions x n_background` model evaluations,
batched into a handful of `predict` calls:

| features | coalitions (auto) | result |
|---|---|---|
| <= 11 | all `2^n - 2` | exact Shapley values |
| 30 | 2 108 | sizes 1, 2, 28, 29 enumerated exactly; the rest importance-sampled |
| any, linear regressor | - | closed form, instant |

A 30-feature random forest explains locally in ~0.2 s and globally (100 rows) in a few
seconds on a laptop. Tune with `n_coalitions`, `n_background`, `n_global`.
| Guide | Description |
|---|---|
| [Getting Started](https://adaumsilva.github.io/XAI-framework/getting-started/) | Step-by-step tutorial on local and global explanations, formatting, and visualization. |
| [Methods & Mathematical Foundations](https://adaumsilva.github.io/XAI-framework/methods/) | Mathematical foundations for Coalition (Shapley), Local Surrogates, and Permutation Importance. |
| [Consensus & Agreement](https://adaumsilva.github.io/XAI-framework/methods/consensus/) | How multi-method explanations are normalized, combined, and diagnosed for agreement ($\rho$). |
| [Extending the Framework](https://adaumsilva.github.io/XAI-framework/extending/) | Guide on subclassing `BaseExplainer` and packaging custom explainers via entry points. |
| [API Reference](https://adaumsilva.github.io/XAI-framework/api/) | Full class, method, and function reference generated directly from code docstrings. |

### Works with
## Compatibility

* scikit-learn estimators and `Pipeline`s (anything with `predict_proba` / `predict`)
* XGBoost, LightGBM, CatBoost and any other library with the same interface
* plain callables `f(X) -> predictions` (probabilities or values)
* Plain callables `f(X) -> predictions` (probabilities or values)
* NumPy arrays and pandas DataFrames (column names become feature names)

Models fitted on DataFrames are handed DataFrames again, so you won't see
"X does not have valid feature names" warnings.

## The `Explanation` object

| Attribute / method | Meaning |
|---|---|
| `feature_names`, `values` | one attribution per feature; positive pushes towards the target class / higher value |
| `feature_values` | the explained instance (local only) |
| `prediction`, `base_value`, `target` | model output, reference value, class explained |
| `top(k)`, `ranks()`, `normalized()` | quick views |
| `to_text()`, `to_markdown()`, `to_dataframe()`, `to_dict()`, `plot()` | outputs |
| `metadata` | method-specific extras (surrogate `local_r2`, coalition `algorithm` / `exact`, permutation `importances_std`, ...) |

`ConsensusExplanation` adds `components`, `agreement`, `agreement_matrix` and
`agreement_level()` (`"high"` >= 0.8, `"moderate"` >= 0.5, `"low"`).

## Adding your own explainer

Subclass `BaseExplainer`, implement `_explain_instance` and/or `_explain_global`,
and register it:

```python
from xai_framework import BaseExplainer, register_explainer

@register_explainer("my_method")
class MyExplainer(BaseExplainer):
supports_global = False

def _explain_instance(self, x, target):
values = ... # one number per feature
return self._make_explanation(values, x, target, base_value=None)
```

It is now usable as `explain(model, X, instance=0, methods=["coalition", "my_method"])`.
Third-party packages can register through the `xai_framework.explainers` entry-point
group - see [CONTRIBUTING.md](CONTRIBUTING.md).

## Roadmap

See [ROADMAP.md](ROADMAP.md) and the issue tracker. Highlights: a fast exact path for
tree ensembles, rule-based (anchor) and counterfactual explainers, text and image
support, faithfulness metrics, a CLI and an HTML report, and a docs site.
support, faithfulness metrics, and standalone HTML reports.

## Contributing

Expand Down
5 changes: 5 additions & 0 deletions docs/api/auto.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Auto API (`xai_framework.auto`)

The `auto` module provides the high-level functional interface to XAI-Framework.

::: xai_framework.auto
9 changes: 9 additions & 0 deletions docs/api/explainers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Explainers API (`xai_framework.explainers`)

Explainer classes and consensus combination algorithms.

::: xai_framework.explainers.base
::: xai_framework.explainers.coalition
::: xai_framework.explainers.surrogate
::: xai_framework.explainers.permutation
::: xai_framework.explainers.consensus
5 changes: 5 additions & 0 deletions docs/api/explanation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Explanation API (`xai_framework.explanation`)

The data structures representing individual and consensus explanations.

::: xai_framework.explanation
13 changes: 13 additions & 0 deletions docs/api/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# API Reference

Complete documentation generated directly from docstrings across the XAI-Framework codebase.

---

## Core Modules

- **[`auto`](auto.md)**: Top-level convenience functions [`explain()`][xai_framework.auto.explain] and [`explain_global()`][xai_framework.auto.explain_global], plus explainer factory [`build_explainer()`][xai_framework.auto.build_explainer].
- **[`explainers`](explainers.md)**: Base classes and concrete explainer implementations ([`CoalitionExplainer`][xai_framework.explainers.coalition.CoalitionExplainer], [`LocalSurrogateExplainer`][xai_framework.explainers.surrogate.LocalSurrogateExplainer], [`PermutationExplainer`][xai_framework.explainers.permutation.PermutationExplainer], and [`ConsensusExplainer`][xai_framework.explainers.consensus.ConsensusExplainer]).
- **[`explanation`](explanation.md)**: Output containers [`Explanation`][xai_framework.explanation.Explanation] and [`ConsensusExplanation`][xai_framework.explanation.ConsensusExplanation].
- **[`model`](model.md)**: Standardized wrapper [`ModelAdapter`][xai_framework.model.ModelAdapter] providing uniform inference over estimators, pipelines, and callables.
- **[`registry`](registry.md)**: Explainer registry, dynamic discovery, and entry-point plugin mechanics.
5 changes: 5 additions & 0 deletions docs/api/model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Model Adapter API (`xai_framework.model`)

Model introspection, prediction wrapping, and format unification.

::: xai_framework.model
5 changes: 5 additions & 0 deletions docs/api/registry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Registry API (`xai_framework.registry`)

Dynamic explainer registration and entry point resolution.

::: xai_framework.registry
1 change: 1 addition & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--8<-- "CHANGELOG.md"
94 changes: 94 additions & 0 deletions docs/extending.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Extending XAI-Framework

XAI-Framework is designed to be easily extensible. You can register custom attribution algorithms internally or build third-party plugins that integrate seamlessly with the framework's consensus engine.

---

## The Explainer Contract

Every explainer inherits from [`BaseExplainer`][xai_framework.explainers.base.BaseExplainer] and produces a standardized [`Explanation`][xai_framework.explanation.Explanation] object.

### Step 1: Subclass `BaseExplainer`

Create a new class inheriting from `BaseExplainer`:

```python
from __future__ import annotations
import numpy as np
from xai_framework import BaseExplainer, register_explainer

@register_explainer("custom_gradient")
class CustomGradientExplainer(BaseExplainer):
"""Example custom explainer estimating gradients via finite differences."""

name = "custom_gradient"
supports_local = True
supports_global = False
requires_y = False

def _explain_instance(self, x: np.ndarray, target: int | None) -> Explanation:
# x is guaranteed to be a 1D float numpy array
# Use self._predict_scalar(..., target)[0] to evaluate scalar predictions
base_pred = float(self._predict_scalar(x.reshape(1, -1), target)[0])

eps = 1e-4
n_features = len(x)
attributions = np.zeros(n_features)

for i in range(n_features):
x_perturbed = x.copy()
x_perturbed[i] += eps
pred_perturbed = float(self._predict_scalar(x_perturbed.reshape(1, -1), target)[0])
attributions[i] = (pred_perturbed - base_pred) / eps

# Build standard Explanation instance using helper
return self._make_explanation(
values=attributions,
x=x,
target=target,
prediction=base_pred,
base_value=None,
step_size=eps,
)
```

---

## Core Guidelines & Invariants

When implementing a custom explainer, adhere to the following framework conventions:

1. **Model Communication via Helpers & `self.adapter`**:
Never call `model.predict()` or `model.predict_proba()` directly. Use `self._predict_scalar(X, target)` to obtain the scalar outcome being explained, or interact via `self.adapter` ([`ModelAdapter`][xai_framework.model.ModelAdapter]), which abstracts classification, regression, single-output functions, and feature name resolution.

2. **Strict Determinism via `self.rng`**:
Never use global random seeds (`np.random.seed` or `np.random.*`). Always use the instance-level random generator `self.rng` initialized with the user-provided `random_state`.

3. **Standard Sign Convention**:
Attributions must be formatted such that a positive value $\phi_i > 0$ means feature $i$ pushes the prediction towards the target outcome (higher value or higher probability for class `target`).

4. **Optional Dependencies & Lazy Imports**:
If your explainer relies on optional external libraries (such as PyTorch or XGBoost), override `is_available()` and perform imports lazily inside `__init__`.

---

## External Entry-Point Plugins

External Python packages can register custom explainers into XAI-Framework without modifying the `xai-framework` repository.

In your external package's `pyproject.toml`, declare entry points under `xai_framework.explainers`:

```toml
[project.entry-points."xai_framework.explainers"]
anchors = "my_xai_package.anchors:AnchorsExplainer"
integrated_gradients = "my_xai_package.ig:IntegratedGradientsExplainer"
```

Once installed in the environment, your explainer will be automatically detected and available to `explain()`:

```python
from xai_framework import explain

# Custom explainer is loaded via entry points automatically
exp = explain(model, X_train, instance=x_query, methods=["coalition", "anchors"])
```
Loading
Loading