explainiverse 0.8.2__tar.gz → 0.8.3__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.
- {explainiverse-0.8.2 → explainiverse-0.8.3}/PKG-INFO +1 -1
- {explainiverse-0.8.2 → explainiverse-0.8.3}/pyproject.toml +1 -1
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/__init__.py +1 -1
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/evaluation/__init__.py +4 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/evaluation/faithfulness_extended.py +169 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/LICENSE +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/README.md +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/adapters/__init__.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/adapters/base_adapter.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/adapters/pytorch_adapter.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/adapters/sklearn_adapter.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/core/__init__.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/core/explainer.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/core/explanation.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/core/registry.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/engine/__init__.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/engine/suite.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/evaluation/_utils.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/evaluation/faithfulness.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/evaluation/metrics.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/evaluation/stability.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/__init__.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/attribution/__init__.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/attribution/lime_wrapper.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/attribution/shap_wrapper.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/attribution/treeshap_wrapper.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/counterfactual/__init__.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/counterfactual/dice_wrapper.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/example_based/__init__.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/example_based/protodash.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/global_explainers/__init__.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/global_explainers/ale.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/global_explainers/partial_dependence.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/global_explainers/permutation_importance.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/global_explainers/sage.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/gradient/__init__.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/gradient/deeplift.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/gradient/gradcam.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/gradient/integrated_gradients.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/gradient/lrp.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/gradient/saliency.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/gradient/smoothgrad.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/gradient/tcav.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/rule_based/__init__.py +0 -0
- {explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/rule_based/anchors_wrapper.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: explainiverse
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.3
|
|
4
4
|
Summary: Unified, extensible explainability framework supporting 18 XAI methods including LIME, SHAP, LRP, TCAV, GradCAM, and more
|
|
5
5
|
Home-page: https://github.com/jemsbhai/explainiverse
|
|
6
6
|
License: MIT
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "explainiverse"
|
|
3
|
-
version = "0.8.
|
|
3
|
+
version = "0.8.3"
|
|
4
4
|
description = "Unified, extensible explainability framework supporting 18 XAI methods including LIME, SHAP, LRP, TCAV, GradCAM, and more"
|
|
5
5
|
authors = ["Muntaser Syed <jemsbhai@gmail.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -41,6 +41,8 @@ from explainiverse.evaluation.faithfulness_extended import (
|
|
|
41
41
|
compute_batch_faithfulness_estimate,
|
|
42
42
|
compute_monotonicity,
|
|
43
43
|
compute_batch_monotonicity,
|
|
44
|
+
compute_monotonicity_nguyen,
|
|
45
|
+
compute_batch_monotonicity_nguyen,
|
|
44
46
|
)
|
|
45
47
|
|
|
46
48
|
__all__ = [
|
|
@@ -70,4 +72,6 @@ __all__ = [
|
|
|
70
72
|
"compute_batch_faithfulness_estimate",
|
|
71
73
|
"compute_monotonicity",
|
|
72
74
|
"compute_batch_monotonicity",
|
|
75
|
+
"compute_monotonicity_nguyen",
|
|
76
|
+
"compute_batch_monotonicity_nguyen",
|
|
73
77
|
]
|
{explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/evaluation/faithfulness_extended.py
RENAMED
|
@@ -256,6 +256,175 @@ def compute_batch_faithfulness_estimate(
|
|
|
256
256
|
}
|
|
257
257
|
|
|
258
258
|
|
|
259
|
+
# =============================================================================
|
|
260
|
+
# Metric 3: Monotonicity-Nguyen (Nguyen et al., 2020)
|
|
261
|
+
# =============================================================================
|
|
262
|
+
|
|
263
|
+
def compute_monotonicity_nguyen(
|
|
264
|
+
model,
|
|
265
|
+
instance: np.ndarray,
|
|
266
|
+
explanation: Explanation,
|
|
267
|
+
baseline: Union[str, float, np.ndarray, Callable] = "mean",
|
|
268
|
+
background_data: np.ndarray = None,
|
|
269
|
+
target_class: int = None,
|
|
270
|
+
use_absolute: bool = True,
|
|
271
|
+
) -> float:
|
|
272
|
+
"""
|
|
273
|
+
Compute Monotonicity Correlation (Nguyen et al., 2020).
|
|
274
|
+
|
|
275
|
+
Measures the Spearman rank correlation between attribution magnitudes
|
|
276
|
+
and the prediction changes when each feature is individually removed
|
|
277
|
+
(replaced with baseline). A faithful explanation should show that
|
|
278
|
+
features with higher attributions cause larger prediction changes
|
|
279
|
+
when removed.
|
|
280
|
+
|
|
281
|
+
Unlike Arya's Monotonicity (sequential feature addition), this metric
|
|
282
|
+
evaluates each feature independently and uses rank correlation to
|
|
283
|
+
measure agreement between attributed importance and actual impact.
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
model: Model adapter with predict/predict_proba method
|
|
287
|
+
instance: Input instance (1D array)
|
|
288
|
+
explanation: Explanation object with feature_attributions
|
|
289
|
+
baseline: Baseline for feature removal ("mean", "median", scalar, array, callable)
|
|
290
|
+
background_data: Reference data for computing baseline (required for "mean"/"median")
|
|
291
|
+
target_class: Target class index for probability (default: predicted class)
|
|
292
|
+
use_absolute: If True, use absolute attribution values (default: True)
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
Monotonicity correlation score (Spearman rho, -1 to 1, higher is better)
|
|
296
|
+
|
|
297
|
+
References:
|
|
298
|
+
Nguyen, A. P., & Martinez, M. R. (2020). Quantitative Evaluation of
|
|
299
|
+
Machine Learning Explanations: A Human-Grounded Benchmark.
|
|
300
|
+
arXiv:2010.07455.
|
|
301
|
+
"""
|
|
302
|
+
instance = np.asarray(instance).flatten()
|
|
303
|
+
n_features = len(instance)
|
|
304
|
+
|
|
305
|
+
# Get baseline values
|
|
306
|
+
baseline_values = compute_baseline_values(
|
|
307
|
+
baseline, background_data, n_features
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
# Extract attributions as array
|
|
311
|
+
attr_array = _extract_attribution_array(explanation, n_features)
|
|
312
|
+
|
|
313
|
+
# Determine target class
|
|
314
|
+
if target_class is None:
|
|
315
|
+
pred = get_prediction_value(model, instance.reshape(1, -1))
|
|
316
|
+
if isinstance(pred, np.ndarray) and pred.ndim > 0:
|
|
317
|
+
target_class = int(np.argmax(pred))
|
|
318
|
+
else:
|
|
319
|
+
target_class = 0
|
|
320
|
+
|
|
321
|
+
# Get original prediction for the target class
|
|
322
|
+
original_pred = get_prediction_value(model, instance.reshape(1, -1))
|
|
323
|
+
if isinstance(original_pred, np.ndarray) and original_pred.ndim > 0 and len(original_pred) > target_class:
|
|
324
|
+
original_value = original_pred[target_class]
|
|
325
|
+
else:
|
|
326
|
+
original_value = float(original_pred)
|
|
327
|
+
|
|
328
|
+
# Compute prediction change for each feature when removed
|
|
329
|
+
prediction_changes = []
|
|
330
|
+
attribution_values = []
|
|
331
|
+
|
|
332
|
+
for i in range(n_features):
|
|
333
|
+
# Create perturbed instance with feature i replaced by baseline
|
|
334
|
+
perturbed = instance.copy()
|
|
335
|
+
perturbed[i] = baseline_values[i]
|
|
336
|
+
|
|
337
|
+
# Get prediction for perturbed instance
|
|
338
|
+
perturbed_pred = get_prediction_value(model, perturbed.reshape(1, -1))
|
|
339
|
+
if isinstance(perturbed_pred, np.ndarray) and perturbed_pred.ndim > 0 and len(perturbed_pred) > target_class:
|
|
340
|
+
perturbed_value = perturbed_pred[target_class]
|
|
341
|
+
else:
|
|
342
|
+
perturbed_value = float(perturbed_pred)
|
|
343
|
+
|
|
344
|
+
# Prediction change (drop in confidence when feature is removed)
|
|
345
|
+
# Positive change means removing the feature decreased prediction
|
|
346
|
+
change = original_value - perturbed_value
|
|
347
|
+
prediction_changes.append(abs(change))
|
|
348
|
+
|
|
349
|
+
# Attribution value
|
|
350
|
+
if use_absolute:
|
|
351
|
+
attribution_values.append(abs(attr_array[i]))
|
|
352
|
+
else:
|
|
353
|
+
attribution_values.append(attr_array[i])
|
|
354
|
+
|
|
355
|
+
prediction_changes = np.array(prediction_changes)
|
|
356
|
+
attribution_values = np.array(attribution_values)
|
|
357
|
+
|
|
358
|
+
# Handle edge cases
|
|
359
|
+
if len(prediction_changes) < 2:
|
|
360
|
+
return 0.0
|
|
361
|
+
|
|
362
|
+
# Check for constant arrays (would cause division by zero in correlation)
|
|
363
|
+
if np.std(prediction_changes) < 1e-10 or np.std(attribution_values) < 1e-10:
|
|
364
|
+
# If both are constant, consider it perfect correlation
|
|
365
|
+
if np.std(prediction_changes) < 1e-10 and np.std(attribution_values) < 1e-10:
|
|
366
|
+
return 1.0
|
|
367
|
+
# If only one is constant, correlation is undefined
|
|
368
|
+
return 0.0
|
|
369
|
+
|
|
370
|
+
# Compute Spearman rank correlation
|
|
371
|
+
corr, _ = stats.spearmanr(attribution_values, prediction_changes)
|
|
372
|
+
|
|
373
|
+
return float(corr) if not np.isnan(corr) else 0.0
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def compute_batch_monotonicity_nguyen(
|
|
377
|
+
model,
|
|
378
|
+
X: np.ndarray,
|
|
379
|
+
explanations: List[Explanation],
|
|
380
|
+
baseline: Union[str, float, np.ndarray, Callable] = "mean",
|
|
381
|
+
max_samples: int = None,
|
|
382
|
+
use_absolute: bool = True,
|
|
383
|
+
) -> Dict[str, float]:
|
|
384
|
+
"""
|
|
385
|
+
Compute average Monotonicity-Nguyen over a batch of instances.
|
|
386
|
+
|
|
387
|
+
Args:
|
|
388
|
+
model: Model adapter
|
|
389
|
+
X: Input data (2D array)
|
|
390
|
+
explanations: List of Explanation objects (one per instance)
|
|
391
|
+
baseline: Baseline for feature removal
|
|
392
|
+
max_samples: Maximum number of samples to evaluate
|
|
393
|
+
use_absolute: If True, use absolute attribution values
|
|
394
|
+
|
|
395
|
+
Returns:
|
|
396
|
+
Dictionary with mean, std, min, max, and count of valid scores
|
|
397
|
+
"""
|
|
398
|
+
n_samples = len(explanations)
|
|
399
|
+
if max_samples:
|
|
400
|
+
n_samples = min(n_samples, max_samples)
|
|
401
|
+
|
|
402
|
+
scores = []
|
|
403
|
+
|
|
404
|
+
for i in range(n_samples):
|
|
405
|
+
try:
|
|
406
|
+
score = compute_monotonicity_nguyen(
|
|
407
|
+
model, X[i], explanations[i],
|
|
408
|
+
baseline=baseline, background_data=X,
|
|
409
|
+
use_absolute=use_absolute
|
|
410
|
+
)
|
|
411
|
+
if not np.isnan(score):
|
|
412
|
+
scores.append(score)
|
|
413
|
+
except Exception:
|
|
414
|
+
continue
|
|
415
|
+
|
|
416
|
+
if not scores:
|
|
417
|
+
return {"mean": 0.0, "std": 0.0, "min": 0.0, "max": 0.0, "n_samples": 0}
|
|
418
|
+
|
|
419
|
+
return {
|
|
420
|
+
"mean": float(np.mean(scores)),
|
|
421
|
+
"std": float(np.std(scores)),
|
|
422
|
+
"min": float(np.min(scores)),
|
|
423
|
+
"max": float(np.max(scores)),
|
|
424
|
+
"n_samples": len(scores),
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
|
|
259
428
|
# =============================================================================
|
|
260
429
|
# Metric 2: Monotonicity (Arya et al., 2019)
|
|
261
430
|
# =============================================================================
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/attribution/__init__.py
RENAMED
|
File without changes
|
{explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/attribution/lime_wrapper.py
RENAMED
|
File without changes
|
{explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/attribution/shap_wrapper.py
RENAMED
|
File without changes
|
|
File without changes
|
{explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/counterfactual/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/example_based/__init__.py
RENAMED
|
File without changes
|
{explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/example_based/protodash.py
RENAMED
|
File without changes
|
|
File without changes
|
{explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/global_explainers/ale.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/global_explainers/sage.py
RENAMED
|
File without changes
|
{explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/gradient/__init__.py
RENAMED
|
File without changes
|
{explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/gradient/deeplift.py
RENAMED
|
File without changes
|
{explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/gradient/gradcam.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/gradient/saliency.py
RENAMED
|
File without changes
|
{explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/gradient/smoothgrad.py
RENAMED
|
File without changes
|
|
File without changes
|
{explainiverse-0.8.2 → explainiverse-0.8.3}/src/explainiverse/explainers/rule_based/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|