xai4tsc 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- xai4tsc-1.0.0/LICENSE +21 -0
- xai4tsc-1.0.0/PKG-INFO +280 -0
- xai4tsc-1.0.0/README.md +244 -0
- xai4tsc-1.0.0/pyproject.toml +134 -0
- xai4tsc-1.0.0/src/xai4tsc/__init__.py +124 -0
- xai4tsc-1.0.0/src/xai4tsc/data/__init__.py +43 -0
- xai4tsc-1.0.0/src/xai4tsc/data/base.py +678 -0
- xai4tsc-1.0.0/src/xai4tsc/data/data_loaders.py +146 -0
- xai4tsc-1.0.0/src/xai4tsc/data/datasets.py +765 -0
- xai4tsc-1.0.0/src/xai4tsc/evaluation/__init__.py +31 -0
- xai4tsc-1.0.0/src/xai4tsc/evaluation/base.py +344 -0
- xai4tsc-1.0.0/src/xai4tsc/evaluation/evaluate.py +180 -0
- xai4tsc-1.0.0/src/xai4tsc/evaluation/frequency_evaluate.py +333 -0
- xai4tsc-1.0.0/src/xai4tsc/evaluation/timefrequency_auc.py +248 -0
- xai4tsc-1.0.0/src/xai4tsc/evaluation/timefrequency_perturbation.py +112 -0
- xai4tsc-1.0.0/src/xai4tsc/models/__init__.py +41 -0
- xai4tsc-1.0.0/src/xai4tsc/models/base.py +639 -0
- xai4tsc-1.0.0/src/xai4tsc/models/models.py +883 -0
- xai4tsc-1.0.0/src/xai4tsc/utils/__init__.py +45 -0
- xai4tsc-1.0.0/src/xai4tsc/utils/animation.py +194 -0
- xai4tsc-1.0.0/src/xai4tsc/utils/defaults.py +36 -0
- xai4tsc-1.0.0/src/xai4tsc/utils/fourier_transforms.py +443 -0
- xai4tsc-1.0.0/src/xai4tsc/utils/perturbation.py +148 -0
- xai4tsc-1.0.0/src/xai4tsc/utils/plot.py +599 -0
- xai4tsc-1.0.0/src/xai4tsc/utils/utils.py +153 -0
- xai4tsc-1.0.0/src/xai4tsc/xai/__init__.py +52 -0
- xai4tsc-1.0.0/src/xai4tsc/xai/_types.py +106 -0
- xai4tsc-1.0.0/src/xai4tsc/xai/base.py +238 -0
- xai4tsc-1.0.0/src/xai4tsc/xai/explain.py +446 -0
- xai4tsc-1.0.0/src/xai4tsc/xai/explanation_domains.py +130 -0
- xai4tsc-1.0.0/src/xai4tsc/xai/feature_attribution.py +520 -0
- xai4tsc-1.0.0/src/xai4tsc/xai/freqrise.py +240 -0
- xai4tsc-1.0.0/src/xai4tsc/xai/random_baseline.py +109 -0
- xai4tsc-1.0.0/src/xai4tsc/xai/wrappers.py +92 -0
xai4tsc-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 TimeXAI Junior Research Group
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
xai4tsc-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: xai4tsc
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: An evaluation framework for eXplainable AI (XAI) methods applied to Time Series Classification (TSC).
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Keywords: xai,explainable-ai,interpretability,time-series,time-series-classification,captum,quantus,pytorch
|
|
8
|
+
Author: TimeXAI Research Group
|
|
9
|
+
Maintainer: Louis Peter
|
|
10
|
+
Maintainer-email: Louis.Peter@kite.thm.de
|
|
11
|
+
Requires-Python: >=3.12,<3.14
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Dist: cachetools (>=6.1.0,<7.0.0)
|
|
21
|
+
Requires-Dist: captum (>=0.8.0,<0.9.0)
|
|
22
|
+
Requires-Dist: matplotlib (>=3.10.5,<4.0.0)
|
|
23
|
+
Requires-Dist: protobuf (==3.20.3)
|
|
24
|
+
Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
|
|
25
|
+
Requires-Dist: quantus[torch] (>=0.6.0,<0.7.0)
|
|
26
|
+
Requires-Dist: scikit-learn (>=1.6)
|
|
27
|
+
Requires-Dist: shap (>=0.48.0,<0.49.0)
|
|
28
|
+
Requires-Dist: sktime (>=0.40.1,<0.41.0)
|
|
29
|
+
Project-URL: Bug Tracker, https://github.com/TimeXAIgroup/XAI4TSC/issues
|
|
30
|
+
Project-URL: Changelog, https://github.com/TimeXAIgroup/XAI4TSC/releases
|
|
31
|
+
Project-URL: Documentation, https://timexaigroup.github.io/XAI4TSC/
|
|
32
|
+
Project-URL: Homepage, https://github.com/TimeXAIgroup/XAI4TSC
|
|
33
|
+
Project-URL: Repository, https://github.com/TimeXAIgroup/XAI4TSC
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# XAI4TSC
|
|
37
|
+
|
|
38
|
+
An evaluation framework for eXplainable AI (XAI) methods applied to Time Series Classification
|
|
39
|
+
(TSC), developed by the TimeXAI Research Group.
|
|
40
|
+
|
|
41
|
+
XAI4TSC has two independent use cases:
|
|
42
|
+
|
|
43
|
+
- **Standalone experiment runner** — clone the repo, write a YAML config, run experiments from
|
|
44
|
+
the command line.
|
|
45
|
+
- **Importable Python package** — `pip install -e .` and use the public API in your own code,
|
|
46
|
+
notebooks, or scripts.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Installation
|
|
51
|
+
|
|
52
|
+
### Standalone (experiment runner)
|
|
53
|
+
|
|
54
|
+
We use Poetry for package management inside a Conda environment:
|
|
55
|
+
|
|
56
|
+
1. Install the Conda environment from `environment.yml`:
|
|
57
|
+
```bash
|
|
58
|
+
conda env create # environment.yml is picked up automatically
|
|
59
|
+
```
|
|
60
|
+
2. Activate the environment:
|
|
61
|
+
```bash
|
|
62
|
+
conda activate xai4tsc
|
|
63
|
+
```
|
|
64
|
+
3. Create a local virtual environment:
|
|
65
|
+
```bash
|
|
66
|
+
python -m venv .venv
|
|
67
|
+
```
|
|
68
|
+
4. Activate the local environment:
|
|
69
|
+
```bash
|
|
70
|
+
source .venv/bin/activate
|
|
71
|
+
```
|
|
72
|
+
5. Confirm setup — check which `python` and `poetry` your shell uses:
|
|
73
|
+
```bash
|
|
74
|
+
which python # should point to the local venv
|
|
75
|
+
which poetry # should point to the conda environment
|
|
76
|
+
```
|
|
77
|
+
6. Install dependencies with Poetry:
|
|
78
|
+
```bash
|
|
79
|
+
poetry install # use --dry-run to preview safely
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Package only
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
pip install xai4tsc
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Or, for a local/editable install from a clone:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
pip install -e PATH/TO/REPOSITORY
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Project layout
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
xai4tsc/
|
|
100
|
+
├── experiment_runner/ # Standalone CLI — owns all config and orchestration logic
|
|
101
|
+
│ ├── main.py # Entry point: python -m experiment_runner.main --conf ...
|
|
102
|
+
│ ├── config.py # Config loading, validation, defaults
|
|
103
|
+
│ ├── cache.py # Runner-level split caching helpers
|
|
104
|
+
│ ├── explain.py # Runner adapter for xai4tsc.xai
|
|
105
|
+
│ ├── evaluate.py # Runner adapter for xai4tsc.evaluation
|
|
106
|
+
│ ├── log_setup.py # Logging setup for the standalone runner
|
|
107
|
+
│ └── configs/
|
|
108
|
+
│ ├── master.yaml # Annotated reference — all available options and defaults
|
|
109
|
+
│ ├── example.yaml # Go-to demo: synthetic data, time-domain explainers + metrics
|
|
110
|
+
│ ├── example_frequency.yaml # Frequency / time-frequency showcase (FreqRISE, freq metrics)
|
|
111
|
+
│ ├── ucr_benchmark.yaml # Full UCR sweep (LeNet + Integrated Gradients + Complexity)
|
|
112
|
+
│ └── uea_benchmark.yaml # Full UEA sweep (skips OOM-risky datasets)
|
|
113
|
+
│
|
|
114
|
+
├── src/xai4tsc/ # Importable package
|
|
115
|
+
│ ├── data/
|
|
116
|
+
│ │ ├── datasets.py # UcrUeaDataset, LocalDataset, SyntheticDataset
|
|
117
|
+
│ │ └── ... # base classes, data loaders
|
|
118
|
+
│ ├── models/ # ModelBase, built-in models, registry
|
|
119
|
+
│ ├── xai/ # Explainer ABC, generate_explanation(), built-in explainers
|
|
120
|
+
│ ├── evaluation/ # evaluate(), Quantus metric registry
|
|
121
|
+
│ └── utils/ # Shared utilities (dict_to_args, merge_dicts, rescale_array, plot)
|
|
122
|
+
│
|
|
123
|
+
└── tests/
|
|
124
|
+
├── conftest.py # Session-scoped fixtures (GunPoint download, split, model)
|
|
125
|
+
├── fixtures/
|
|
126
|
+
│ └── test_config.yaml # Minimal runner config for integration tests
|
|
127
|
+
├── unit/ # Fast, no-I/O tests (@pytest.mark.unit)
|
|
128
|
+
└── integration/ # Full pipeline tests (@pytest.mark.integration)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Running an experiment
|
|
134
|
+
|
|
135
|
+
Run the runner as a module from the repository root:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
python -m experiment_runner.main --conf experiment_runner/configs/example.yaml
|
|
139
|
+
python -m experiment_runner.main --conf experiment_runner/configs/example.yaml --debug
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Four ready-to-run configs ship under `experiment_runner/configs/`:
|
|
143
|
+
|
|
144
|
+
- `example.yaml` — the go-to demo (and the default when `--conf` is omitted):
|
|
145
|
+
the synthetic `freq_shapes` dataset, two models, and time-domain explainers
|
|
146
|
+
and metrics. Needs no download.
|
|
147
|
+
- `example_frequency.yaml` — the same dataset explained with FreqRISE in the
|
|
148
|
+
frequency and time-frequency domains, scored with the frequency metrics.
|
|
149
|
+
- `ucr_benchmark.yaml` / `uea_benchmark.yaml` — full-archive sweeps with a
|
|
150
|
+
minimal model/explainer/metric stack.
|
|
151
|
+
|
|
152
|
+
Edit or copy any of them to configure datasets, models, explainers, and metrics.
|
|
153
|
+
`master.yaml` contains annotated documentation for every available option.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Testing
|
|
158
|
+
|
|
159
|
+
The test suite uses two pytest markers to separate fast unit tests from slow
|
|
160
|
+
integration tests that require a live dataset and a training run.
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
# Unit tests only — fast, no I/O, no training
|
|
164
|
+
pytest -m unit
|
|
165
|
+
|
|
166
|
+
# Integration tests only — downloads GunPoint on first run, trains a model
|
|
167
|
+
pytest -m integration
|
|
168
|
+
|
|
169
|
+
# Full suite
|
|
170
|
+
pytest
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
GunPoint is downloaded automatically on the first integration run and cached in
|
|
174
|
+
`tests/cache/` (override with the `XAI4TSC_TEST_CACHE` environment variable).
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Using xai4tsc as a package
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
import xai4tsc
|
|
182
|
+
from xai4tsc.data import load_dataset, LocalDataset
|
|
183
|
+
from xai4tsc.models.models import load_model
|
|
184
|
+
from xai4tsc.xai.explain import generate_explanation
|
|
185
|
+
from xai4tsc.evaluation.evaluate import evaluate
|
|
186
|
+
|
|
187
|
+
# ── Load data (UCR download or local numpy files) ─────────────────────────────
|
|
188
|
+
ds = load_dataset("GunPoint") # UcrUeaDataset — downloads on first use
|
|
189
|
+
# ds = LocalDataset("/path/to/data", name="MyDataset") # local data.npy + labels.json
|
|
190
|
+
|
|
191
|
+
splits, encoder = ds.split(
|
|
192
|
+
train_split=0.8, val_split=0.1, random_state=42, encode="label"
|
|
193
|
+
)
|
|
194
|
+
train_data, train_labels, _ = splits[0]
|
|
195
|
+
test_data, test_labels, _ = splits[1]
|
|
196
|
+
|
|
197
|
+
# ── Train a model ─────────────────────────────────────────────────────────────
|
|
198
|
+
model = load_model(
|
|
199
|
+
{"model": "FCN", "init_params": {"in_channels": 1, "num_classes": 2}},
|
|
200
|
+
device="cpu",
|
|
201
|
+
)
|
|
202
|
+
model.train_model(
|
|
203
|
+
train_data, train_labels,
|
|
204
|
+
hyperparams={"epochs": 50, "batchsize": 32, "loss_func": "CrossEntropy",
|
|
205
|
+
"optimizer": "adam", "learn_rate": 0.001, "patience": 10},
|
|
206
|
+
save_path="results", # best checkpoint + training plots land here
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
# ── Generate explanations ─────────────────────────────────────────────────────
|
|
210
|
+
exp = generate_explanation(
|
|
211
|
+
method="integrated_gradients",
|
|
212
|
+
model=model,
|
|
213
|
+
data=test_data,
|
|
214
|
+
labels=test_labels,
|
|
215
|
+
encoder=encoder,
|
|
216
|
+
indices=[0, 1, 2],
|
|
217
|
+
device="cpu",
|
|
218
|
+
)
|
|
219
|
+
# exp.exp_values — numpy array, same shape as test_data[[0, 1, 2]]
|
|
220
|
+
|
|
221
|
+
# ── Evaluate ──────────────────────────────────────────────────────────────────
|
|
222
|
+
score = evaluate(
|
|
223
|
+
model=model,
|
|
224
|
+
metric="Complexity",
|
|
225
|
+
explanation=exp,
|
|
226
|
+
data=test_data[exp.indices],
|
|
227
|
+
labels=test_labels[exp.indices],
|
|
228
|
+
metric_class_params={"normalise": True, "abs": True, "disable_warnings": True},
|
|
229
|
+
device="cpu",
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
# ── Register a custom explainer ───────────────────────────────────────────────
|
|
233
|
+
from captum.attr import Saliency
|
|
234
|
+
from xai4tsc import GradientExplainer, register_explainer
|
|
235
|
+
|
|
236
|
+
class SaliencyExplainer(GradientExplainer):
|
|
237
|
+
def _get_captum_attribution(self, model):
|
|
238
|
+
return Saliency(model)
|
|
239
|
+
|
|
240
|
+
register_explainer("saliency", SaliencyExplainer)
|
|
241
|
+
exp2 = generate_explanation(
|
|
242
|
+
"saliency", model=model, data=test_data,
|
|
243
|
+
labels=test_labels, encoder=encoder, indices=[0], device="cpu",
|
|
244
|
+
)
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Built on
|
|
250
|
+
|
|
251
|
+
- **Computational backends:** PyTorch, scikit-learn
|
|
252
|
+
- **Explanation backend:** Captum
|
|
253
|
+
- **Evaluation backend:** Quantus
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Contributing
|
|
258
|
+
|
|
259
|
+
Contributions are welcome. See [`CONTRIBUTING.md`](CONTRIBUTING.md) for the
|
|
260
|
+
development setup, code style and quality gate, testing conventions, and the
|
|
261
|
+
pattern for adding a new model, explainer, or metric. By participating you agree
|
|
262
|
+
to the [Code of Conduct](CODE_OF_CONDUCT.md). The project is released under the
|
|
263
|
+
[MIT License](LICENSE).
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Disclaimer
|
|
268
|
+
|
|
269
|
+
XAI4TSC bundles **clean-room re-implementations** of models, explainers, and
|
|
270
|
+
metrics from the research literature, alongside thin wrappers around third-party
|
|
271
|
+
libraries (Captum, Quantus). These implementations have **not** been verified by
|
|
272
|
+
the original authors and may differ from the source papers or reference code in
|
|
273
|
+
ways that affect results.
|
|
274
|
+
|
|
275
|
+
Evaluation scores are an empirical, sometimes contested, proxy for explanation
|
|
276
|
+
quality — treat them as guidance, not ground truth. When using XAI4TSC for
|
|
277
|
+
research, cite the original papers, state which implementation you used, and,
|
|
278
|
+
where possible, validate against a reference implementation. See the
|
|
279
|
+
[full disclaimer](docs/source/disclaimer.md) in the documentation for details.
|
|
280
|
+
|
xai4tsc-1.0.0/README.md
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# XAI4TSC
|
|
2
|
+
|
|
3
|
+
An evaluation framework for eXplainable AI (XAI) methods applied to Time Series Classification
|
|
4
|
+
(TSC), developed by the TimeXAI Research Group.
|
|
5
|
+
|
|
6
|
+
XAI4TSC has two independent use cases:
|
|
7
|
+
|
|
8
|
+
- **Standalone experiment runner** — clone the repo, write a YAML config, run experiments from
|
|
9
|
+
the command line.
|
|
10
|
+
- **Importable Python package** — `pip install -e .` and use the public API in your own code,
|
|
11
|
+
notebooks, or scripts.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
### Standalone (experiment runner)
|
|
18
|
+
|
|
19
|
+
We use Poetry for package management inside a Conda environment:
|
|
20
|
+
|
|
21
|
+
1. Install the Conda environment from `environment.yml`:
|
|
22
|
+
```bash
|
|
23
|
+
conda env create # environment.yml is picked up automatically
|
|
24
|
+
```
|
|
25
|
+
2. Activate the environment:
|
|
26
|
+
```bash
|
|
27
|
+
conda activate xai4tsc
|
|
28
|
+
```
|
|
29
|
+
3. Create a local virtual environment:
|
|
30
|
+
```bash
|
|
31
|
+
python -m venv .venv
|
|
32
|
+
```
|
|
33
|
+
4. Activate the local environment:
|
|
34
|
+
```bash
|
|
35
|
+
source .venv/bin/activate
|
|
36
|
+
```
|
|
37
|
+
5. Confirm setup — check which `python` and `poetry` your shell uses:
|
|
38
|
+
```bash
|
|
39
|
+
which python # should point to the local venv
|
|
40
|
+
which poetry # should point to the conda environment
|
|
41
|
+
```
|
|
42
|
+
6. Install dependencies with Poetry:
|
|
43
|
+
```bash
|
|
44
|
+
poetry install # use --dry-run to preview safely
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Package only
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install xai4tsc
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Or, for a local/editable install from a clone:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pip install -e PATH/TO/REPOSITORY
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Project layout
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
xai4tsc/
|
|
65
|
+
├── experiment_runner/ # Standalone CLI — owns all config and orchestration logic
|
|
66
|
+
│ ├── main.py # Entry point: python -m experiment_runner.main --conf ...
|
|
67
|
+
│ ├── config.py # Config loading, validation, defaults
|
|
68
|
+
│ ├── cache.py # Runner-level split caching helpers
|
|
69
|
+
│ ├── explain.py # Runner adapter for xai4tsc.xai
|
|
70
|
+
│ ├── evaluate.py # Runner adapter for xai4tsc.evaluation
|
|
71
|
+
│ ├── log_setup.py # Logging setup for the standalone runner
|
|
72
|
+
│ └── configs/
|
|
73
|
+
│ ├── master.yaml # Annotated reference — all available options and defaults
|
|
74
|
+
│ ├── example.yaml # Go-to demo: synthetic data, time-domain explainers + metrics
|
|
75
|
+
│ ├── example_frequency.yaml # Frequency / time-frequency showcase (FreqRISE, freq metrics)
|
|
76
|
+
│ ├── ucr_benchmark.yaml # Full UCR sweep (LeNet + Integrated Gradients + Complexity)
|
|
77
|
+
│ └── uea_benchmark.yaml # Full UEA sweep (skips OOM-risky datasets)
|
|
78
|
+
│
|
|
79
|
+
├── src/xai4tsc/ # Importable package
|
|
80
|
+
│ ├── data/
|
|
81
|
+
│ │ ├── datasets.py # UcrUeaDataset, LocalDataset, SyntheticDataset
|
|
82
|
+
│ │ └── ... # base classes, data loaders
|
|
83
|
+
│ ├── models/ # ModelBase, built-in models, registry
|
|
84
|
+
│ ├── xai/ # Explainer ABC, generate_explanation(), built-in explainers
|
|
85
|
+
│ ├── evaluation/ # evaluate(), Quantus metric registry
|
|
86
|
+
│ └── utils/ # Shared utilities (dict_to_args, merge_dicts, rescale_array, plot)
|
|
87
|
+
│
|
|
88
|
+
└── tests/
|
|
89
|
+
├── conftest.py # Session-scoped fixtures (GunPoint download, split, model)
|
|
90
|
+
├── fixtures/
|
|
91
|
+
│ └── test_config.yaml # Minimal runner config for integration tests
|
|
92
|
+
├── unit/ # Fast, no-I/O tests (@pytest.mark.unit)
|
|
93
|
+
└── integration/ # Full pipeline tests (@pytest.mark.integration)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Running an experiment
|
|
99
|
+
|
|
100
|
+
Run the runner as a module from the repository root:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
python -m experiment_runner.main --conf experiment_runner/configs/example.yaml
|
|
104
|
+
python -m experiment_runner.main --conf experiment_runner/configs/example.yaml --debug
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Four ready-to-run configs ship under `experiment_runner/configs/`:
|
|
108
|
+
|
|
109
|
+
- `example.yaml` — the go-to demo (and the default when `--conf` is omitted):
|
|
110
|
+
the synthetic `freq_shapes` dataset, two models, and time-domain explainers
|
|
111
|
+
and metrics. Needs no download.
|
|
112
|
+
- `example_frequency.yaml` — the same dataset explained with FreqRISE in the
|
|
113
|
+
frequency and time-frequency domains, scored with the frequency metrics.
|
|
114
|
+
- `ucr_benchmark.yaml` / `uea_benchmark.yaml` — full-archive sweeps with a
|
|
115
|
+
minimal model/explainer/metric stack.
|
|
116
|
+
|
|
117
|
+
Edit or copy any of them to configure datasets, models, explainers, and metrics.
|
|
118
|
+
`master.yaml` contains annotated documentation for every available option.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Testing
|
|
123
|
+
|
|
124
|
+
The test suite uses two pytest markers to separate fast unit tests from slow
|
|
125
|
+
integration tests that require a live dataset and a training run.
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
# Unit tests only — fast, no I/O, no training
|
|
129
|
+
pytest -m unit
|
|
130
|
+
|
|
131
|
+
# Integration tests only — downloads GunPoint on first run, trains a model
|
|
132
|
+
pytest -m integration
|
|
133
|
+
|
|
134
|
+
# Full suite
|
|
135
|
+
pytest
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
GunPoint is downloaded automatically on the first integration run and cached in
|
|
139
|
+
`tests/cache/` (override with the `XAI4TSC_TEST_CACHE` environment variable).
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Using xai4tsc as a package
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
import xai4tsc
|
|
147
|
+
from xai4tsc.data import load_dataset, LocalDataset
|
|
148
|
+
from xai4tsc.models.models import load_model
|
|
149
|
+
from xai4tsc.xai.explain import generate_explanation
|
|
150
|
+
from xai4tsc.evaluation.evaluate import evaluate
|
|
151
|
+
|
|
152
|
+
# ── Load data (UCR download or local numpy files) ─────────────────────────────
|
|
153
|
+
ds = load_dataset("GunPoint") # UcrUeaDataset — downloads on first use
|
|
154
|
+
# ds = LocalDataset("/path/to/data", name="MyDataset") # local data.npy + labels.json
|
|
155
|
+
|
|
156
|
+
splits, encoder = ds.split(
|
|
157
|
+
train_split=0.8, val_split=0.1, random_state=42, encode="label"
|
|
158
|
+
)
|
|
159
|
+
train_data, train_labels, _ = splits[0]
|
|
160
|
+
test_data, test_labels, _ = splits[1]
|
|
161
|
+
|
|
162
|
+
# ── Train a model ─────────────────────────────────────────────────────────────
|
|
163
|
+
model = load_model(
|
|
164
|
+
{"model": "FCN", "init_params": {"in_channels": 1, "num_classes": 2}},
|
|
165
|
+
device="cpu",
|
|
166
|
+
)
|
|
167
|
+
model.train_model(
|
|
168
|
+
train_data, train_labels,
|
|
169
|
+
hyperparams={"epochs": 50, "batchsize": 32, "loss_func": "CrossEntropy",
|
|
170
|
+
"optimizer": "adam", "learn_rate": 0.001, "patience": 10},
|
|
171
|
+
save_path="results", # best checkpoint + training plots land here
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
# ── Generate explanations ─────────────────────────────────────────────────────
|
|
175
|
+
exp = generate_explanation(
|
|
176
|
+
method="integrated_gradients",
|
|
177
|
+
model=model,
|
|
178
|
+
data=test_data,
|
|
179
|
+
labels=test_labels,
|
|
180
|
+
encoder=encoder,
|
|
181
|
+
indices=[0, 1, 2],
|
|
182
|
+
device="cpu",
|
|
183
|
+
)
|
|
184
|
+
# exp.exp_values — numpy array, same shape as test_data[[0, 1, 2]]
|
|
185
|
+
|
|
186
|
+
# ── Evaluate ──────────────────────────────────────────────────────────────────
|
|
187
|
+
score = evaluate(
|
|
188
|
+
model=model,
|
|
189
|
+
metric="Complexity",
|
|
190
|
+
explanation=exp,
|
|
191
|
+
data=test_data[exp.indices],
|
|
192
|
+
labels=test_labels[exp.indices],
|
|
193
|
+
metric_class_params={"normalise": True, "abs": True, "disable_warnings": True},
|
|
194
|
+
device="cpu",
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
# ── Register a custom explainer ───────────────────────────────────────────────
|
|
198
|
+
from captum.attr import Saliency
|
|
199
|
+
from xai4tsc import GradientExplainer, register_explainer
|
|
200
|
+
|
|
201
|
+
class SaliencyExplainer(GradientExplainer):
|
|
202
|
+
def _get_captum_attribution(self, model):
|
|
203
|
+
return Saliency(model)
|
|
204
|
+
|
|
205
|
+
register_explainer("saliency", SaliencyExplainer)
|
|
206
|
+
exp2 = generate_explanation(
|
|
207
|
+
"saliency", model=model, data=test_data,
|
|
208
|
+
labels=test_labels, encoder=encoder, indices=[0], device="cpu",
|
|
209
|
+
)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Built on
|
|
215
|
+
|
|
216
|
+
- **Computational backends:** PyTorch, scikit-learn
|
|
217
|
+
- **Explanation backend:** Captum
|
|
218
|
+
- **Evaluation backend:** Quantus
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Contributing
|
|
223
|
+
|
|
224
|
+
Contributions are welcome. See [`CONTRIBUTING.md`](CONTRIBUTING.md) for the
|
|
225
|
+
development setup, code style and quality gate, testing conventions, and the
|
|
226
|
+
pattern for adding a new model, explainer, or metric. By participating you agree
|
|
227
|
+
to the [Code of Conduct](CODE_OF_CONDUCT.md). The project is released under the
|
|
228
|
+
[MIT License](LICENSE).
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Disclaimer
|
|
233
|
+
|
|
234
|
+
XAI4TSC bundles **clean-room re-implementations** of models, explainers, and
|
|
235
|
+
metrics from the research literature, alongside thin wrappers around third-party
|
|
236
|
+
libraries (Captum, Quantus). These implementations have **not** been verified by
|
|
237
|
+
the original authors and may differ from the source papers or reference code in
|
|
238
|
+
ways that affect results.
|
|
239
|
+
|
|
240
|
+
Evaluation scores are an empirical, sometimes contested, proxy for explanation
|
|
241
|
+
quality — treat them as guidance, not ground truth. When using XAI4TSC for
|
|
242
|
+
research, cite the original papers, state which implementation you used, and,
|
|
243
|
+
where possible, validate against a reference implementation. See the
|
|
244
|
+
[full disclaimer](docs/source/disclaimer.md) in the documentation for details.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "xai4tsc"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
description = "An evaluation framework for eXplainable AI (XAI) methods applied to Time Series Classification (TSC)."
|
|
5
|
+
authors = [
|
|
6
|
+
{name = "TimeXAI Research Group"}
|
|
7
|
+
]
|
|
8
|
+
maintainers = [
|
|
9
|
+
{name = "Louis Peter", email = "Louis.Peter@kite.thm.de"}
|
|
10
|
+
]
|
|
11
|
+
license = "MIT"
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.12,<3.14"
|
|
14
|
+
keywords = [
|
|
15
|
+
"xai",
|
|
16
|
+
"explainable-ai",
|
|
17
|
+
"interpretability",
|
|
18
|
+
"time-series",
|
|
19
|
+
"time-series-classification",
|
|
20
|
+
"captum",
|
|
21
|
+
"quantus",
|
|
22
|
+
"pytorch",
|
|
23
|
+
]
|
|
24
|
+
# The MIT license classifier is intentionally omitted: the SPDX `license`
|
|
25
|
+
# field above already declares it (PEP 639), and mixing the two is discouraged.
|
|
26
|
+
classifiers = [
|
|
27
|
+
"Development Status :: 5 - Production/Stable",
|
|
28
|
+
"Intended Audience :: Science/Research",
|
|
29
|
+
"Operating System :: OS Independent",
|
|
30
|
+
"Programming Language :: Python :: 3",
|
|
31
|
+
"Programming Language :: Python :: 3.12",
|
|
32
|
+
"Programming Language :: Python :: 3.13",
|
|
33
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
34
|
+
"Typing :: Typed",
|
|
35
|
+
]
|
|
36
|
+
dependencies = [
|
|
37
|
+
"sktime (>=0.40.1,<0.41.0)",
|
|
38
|
+
"pyyaml (>=6.0.2,<7.0.0)",
|
|
39
|
+
"shap (>=0.48.0,<0.49.0)",
|
|
40
|
+
"captum (>=0.8.0,<0.9.0)",
|
|
41
|
+
"cachetools (>=6.1.0,<7.0.0)",
|
|
42
|
+
"matplotlib (>=3.10.5,<4.0.0)",
|
|
43
|
+
"protobuf (==3.20.3)",
|
|
44
|
+
"quantus[torch] (>=0.6.0,<0.7.0)",
|
|
45
|
+
"scikit-learn (>=1.6)"
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
[project.urls]
|
|
49
|
+
Homepage = "https://github.com/TimeXAIgroup/XAI4TSC"
|
|
50
|
+
Repository = "https://github.com/TimeXAIgroup/XAI4TSC"
|
|
51
|
+
Documentation = "https://timexaigroup.github.io/XAI4TSC/"
|
|
52
|
+
"Bug Tracker" = "https://github.com/TimeXAIgroup/XAI4TSC/issues"
|
|
53
|
+
Changelog = "https://github.com/TimeXAIgroup/XAI4TSC/releases"
|
|
54
|
+
|
|
55
|
+
[build-system]
|
|
56
|
+
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
|
57
|
+
build-backend = "poetry.core.masonry.api"
|
|
58
|
+
|
|
59
|
+
[tool.poetry.group.dev.dependencies]
|
|
60
|
+
ruff = "^0.15.14"
|
|
61
|
+
pytest = "^9.0.3"
|
|
62
|
+
pytest-cov = "^7.0"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
[tool.poetry.group.docs.dependencies]
|
|
67
|
+
sphinx = "^9.1.0"
|
|
68
|
+
sphinx-autoapi = "^3.8.0"
|
|
69
|
+
sphinx-rtd-theme = "^3.1.0"
|
|
70
|
+
myst-parser = "^5.1.0"
|
|
71
|
+
|
|
72
|
+
[tool.pytest.ini_options]
|
|
73
|
+
testpaths = ["tests"]
|
|
74
|
+
addopts = "-v"
|
|
75
|
+
markers = [
|
|
76
|
+
"unit: fast tests with no I/O or training — run constantly during development",
|
|
77
|
+
"integration: full pipeline tests with real data and training — run explicitly",
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
# Coverage is opt-in: pass --cov on the command line so the default fast
|
|
81
|
+
# `pytest -m unit` run stays free of measurement overhead. See CLAUDE.md.
|
|
82
|
+
[tool.coverage.run]
|
|
83
|
+
source = ["src/xai4tsc", "experiment_runner"]
|
|
84
|
+
branch = true # also track which if/else branches are taken, not just lines
|
|
85
|
+
|
|
86
|
+
[tool.coverage.report]
|
|
87
|
+
show_missing = true
|
|
88
|
+
exclude_lines = [
|
|
89
|
+
"pragma: no cover",
|
|
90
|
+
"if TYPE_CHECKING:",
|
|
91
|
+
"raise NotImplementedError",
|
|
92
|
+
"if __name__ == .__main__.:",
|
|
93
|
+
"@(abc\\.)?abstractmethod",
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
[tool.ruff]
|
|
97
|
+
# Source folders, specified to ease detection and support GitHub workflow.
|
|
98
|
+
src = ["src", "experiment_runner"]
|
|
99
|
+
preview = false
|
|
100
|
+
indent-width = 4
|
|
101
|
+
# 88 is the ruff/Black default; it keeps two files readable side by side on a
|
|
102
|
+
# typical screen. Set explicitly so the limit is visible and intentional.
|
|
103
|
+
line-length = 88
|
|
104
|
+
lint.select = [
|
|
105
|
+
"E", # pycodestyle errors
|
|
106
|
+
"W", # pycodestyle warnings
|
|
107
|
+
"F", # pyflakes (real bugs: unused/undefined names)
|
|
108
|
+
"I", # isort import sorting
|
|
109
|
+
"UP", # pyupgrade (modern syntax)
|
|
110
|
+
"B", # flake8-bugbear (likely bugs)
|
|
111
|
+
"SIM", # flake8-simplify
|
|
112
|
+
"RUF", # ruff-specific rules
|
|
113
|
+
"D", # pydocstyle (numpy convention, see below)
|
|
114
|
+
"N", # pep8-naming
|
|
115
|
+
"ANN", # flake8-annotations (typed public API)
|
|
116
|
+
]
|
|
117
|
+
lint.extend-select = [
|
|
118
|
+
"D213", # enforce summary on second docstring line (counterpart of ignored D212)
|
|
119
|
+
"D417", # keep undocumented-param despite numpy convention
|
|
120
|
+
]
|
|
121
|
+
lint.ignore = [
|
|
122
|
+
"D203", # using D211: no blank line before class docstring
|
|
123
|
+
"D212", # using D213: summary on second docstring line
|
|
124
|
+
### specific rules
|
|
125
|
+
"D100",
|
|
126
|
+
"D104",
|
|
127
|
+
]
|
|
128
|
+
lint.pydocstyle.convention = "numpy"
|
|
129
|
+
|
|
130
|
+
[tool.ruff.lint.per-file-ignores]
|
|
131
|
+
"tests/**" = [
|
|
132
|
+
"ANN", # test functions need no type annotations
|
|
133
|
+
"D103", # test names are self-documenting; no docstrings required
|
|
134
|
+
]
|