explainiverse 0.8.2__py3-none-any.whl → 0.8.3__py3-none-any.whl

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/__init__.py CHANGED
@@ -34,7 +34,7 @@ from explainiverse.adapters.sklearn_adapter import SklearnAdapter
34
34
  from explainiverse.adapters import TORCH_AVAILABLE
35
35
  from explainiverse.engine.suite import ExplanationSuite
36
36
 
37
- __version__ = "0.8.2"
37
+ __version__ = "0.8.3"
38
38
 
39
39
  __all__ = [
40
40
  # Core
@@ -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
  ]
@@ -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
  # =============================================================================
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: explainiverse
3
- Version: 0.8.2
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,4 +1,4 @@
1
- explainiverse/__init__.py,sha256=icvNmaSq0DAERqIrU60N60KCIspHbtEWTi3kt_YXTUI,1694
1
+ explainiverse/__init__.py,sha256=kpQiMr5ii9_Bn_Q-VuxeqMGEucgWgeGYKmxn7AQoRLs,1694
2
2
  explainiverse/adapters/__init__.py,sha256=HcQGISyp-YQ4jEj2IYveX_c9X5otLcTNWRnVRRhzRik,781
3
3
  explainiverse/adapters/base_adapter.py,sha256=Nqt0GeDn_-PjTyJcZsE8dRTulavqFQsv8sMYWS_ps-M,603
4
4
  explainiverse/adapters/pytorch_adapter.py,sha256=DLQKJ7gB0foPwAmcrru7QdZnPRnhqDKpFCT-EaD3420,15612
@@ -9,10 +9,10 @@ explainiverse/core/explanation.py,sha256=498BbRYrNR-BOql78sENOsyWxgqLsBVZXn14lh-
9
9
  explainiverse/core/registry.py,sha256=6HttL27Ty4jYtugRf-EDIKPy80M8BfvUppAKwwGDyQ8,27207
10
10
  explainiverse/engine/__init__.py,sha256=1sZO8nH1mmwK2e-KUavBQm7zYDWUe27nyWoFy9tgsiA,197
11
11
  explainiverse/engine/suite.py,sha256=G-7OjESisSTaQ1FQrlPl4YydX13uz8Bb70hJZNlcl2M,8918
12
- explainiverse/evaluation/__init__.py,sha256=XFVnmwrRtHHhtxI_yOw_nsR67pJvH-IBO_lEUVI-eDE,1957
12
+ explainiverse/evaluation/__init__.py,sha256=n65EC3vfDfM9AU-tHvdlnTWEKGQzn3rq2mf6GwOMWh8,2105
13
13
  explainiverse/evaluation/_utils.py,sha256=ej7YOPZ90gVHuuIMj45EXHq9Jx3QG7lhaj5sk26hRpg,10519
14
14
  explainiverse/evaluation/faithfulness.py,sha256=_40afOW6vJ3dQguHlJySlgWqiJF_xIvN-uVA3nPKRvI,14841
15
- explainiverse/evaluation/faithfulness_extended.py,sha256=0zHcmINNA88EJcKOY04Z384S3QhBMo7W2m3lGNkUiNQ,14690
15
+ explainiverse/evaluation/faithfulness_extended.py,sha256=t1Iw4sAQ40X_agzTCLIyDvGwoS9knrmiLeCPdU4KpQg,21048
16
16
  explainiverse/evaluation/metrics.py,sha256=snNK9Ua1VzHDT6DlrhYL4m2MmRF3X15vuuVXiHbeicU,9944
17
17
  explainiverse/evaluation/stability.py,sha256=q2d3rpxpp0X1s6ADST1iZA4tzksLJpR0mYBnA_U5FIs,12090
18
18
  explainiverse/explainers/__init__.py,sha256=-ncRXbFKahH3bR0oXM2UQM4LtTdTlvdeprL6cHeqNBs,2549
@@ -39,7 +39,7 @@ explainiverse/explainers/gradient/smoothgrad.py,sha256=COIKZSFcApmMkA62M0AForHiY
39
39
  explainiverse/explainers/gradient/tcav.py,sha256=zc-8wMsc2ZOhUeSZNBJ6H6BPXlVMJ9DRcAMiL25wU9I,32242
40
40
  explainiverse/explainers/rule_based/__init__.py,sha256=gKzlFCAzwurAMLJcuYgal4XhDj1thteBGcaHWmN7iWk,243
41
41
  explainiverse/explainers/rule_based/anchors_wrapper.py,sha256=ML7W6aam-eMGZHy5ilol8qupZvNBJpYAFatEEPnuMyo,13254
42
- explainiverse-0.8.2.dist-info/LICENSE,sha256=28rbHe8rJgmUlRdxJACfq1Sj-MtCEhyHxkJedQd1ZYA,1070
43
- explainiverse-0.8.2.dist-info/METADATA,sha256=QSLwIr4RmoHpxqIfoarJX17alA-0esXfdNa1cemWu5s,23770
44
- explainiverse-0.8.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
45
- explainiverse-0.8.2.dist-info/RECORD,,
42
+ explainiverse-0.8.3.dist-info/LICENSE,sha256=28rbHe8rJgmUlRdxJACfq1Sj-MtCEhyHxkJedQd1ZYA,1070
43
+ explainiverse-0.8.3.dist-info/METADATA,sha256=2_B9YqcBdFLDjQYgqIxYpiKOMnx4bQGWdlgJHMTacLw,23770
44
+ explainiverse-0.8.3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
45
+ explainiverse-0.8.3.dist-info/RECORD,,