Files
2026-07-13 13:22:52 +08:00

118 lines
3.9 KiB
Python

import itertools as it
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from . import perturbation
def update(model, attributions, X, y, masker, sort_order, perturbation_method, scores):
metric = perturbation_method + " " + sort_order
sp = perturbation.SequentialPerturbation(model, masker, sort_order, perturbation_method)
xs, ys, auc = sp.model_score(attributions, X, y=y)
scores["metrics"].append(metric)
scores["values"][metric] = [xs, ys, auc]
def get_benchmark(model, attributions, X, y, masker, metrics):
# convert dataframes
if isinstance(X, (pd.Series, pd.DataFrame)):
X = X.values
if isinstance(masker, (pd.Series, pd.DataFrame)):
masker = masker.values
# record scores per metric
scores = {"metrics": list(), "values": dict()}
for sort_order, perturbation_method in list(it.product(metrics["sort_order"], metrics["perturbation"])):
update(model, attributions, X, y, masker, sort_order, perturbation_method, scores)
return scores
def get_metrics(benchmarks, selection):
# select metrics to plot using selection function
explainer_metrics = set()
for explainer in benchmarks:
scores = benchmarks[explainer]
if len(explainer_metrics) == 0:
explainer_metrics = set(scores["metrics"])
else:
explainer_metrics = selection(explainer_metrics, set(scores["metrics"]))
return list(explainer_metrics)
def trend_plot(benchmarks):
explainer_metrics = get_metrics(benchmarks, lambda x, y: x.union(y))
# plot all curves if metric exists
for metric in explainer_metrics:
plt.clf()
for explainer in benchmarks:
scores = benchmarks[explainer]
if metric in scores["values"]:
x, y, auc = scores["values"][metric]
plt.plot(x, y, label=f"{round(auc, 3)} - {explainer}")
if "keep" in metric:
xlabel = "Percent Unmasked"
if "remove" in metric:
xlabel = "Percent Masked"
plt.ylabel("Model Output")
plt.xlabel(xlabel)
plt.title(metric)
plt.legend()
plt.show()
def compare_plot(benchmarks):
explainer_metrics = get_metrics(benchmarks, lambda x, y: x.intersection(y))
explainers = list(benchmarks.keys())
num_explainers = len(explainers)
num_metrics = len(explainer_metrics)
# dummy start to evenly distribute explainers on the left
# can later be replaced by boolean metrics
aucs = dict()
for i in range(num_explainers):
explainer = explainers[i]
aucs[explainer] = [i / (num_explainers - 1)]
# normalize per metric
for metric in explainer_metrics:
max_auc, min_auc = -float("inf"), float("inf")
for explainer in explainers:
scores = benchmarks[explainer]
_, _, auc = scores["values"][metric]
min_auc = min(auc, min_auc)
max_auc = max(auc, max_auc)
for explainer in explainers:
scores = benchmarks[explainer]
_, _, auc = scores["values"][metric]
aucs[explainer].append((auc - min_auc) / (max_auc - min_auc))
# plot common curves
ax = plt.gca()
for explainer in explainers:
plt.plot(np.linspace(0, 1, len(explainer_metrics) + 1), aucs[explainer], "--o")
ax.tick_params(which="major", axis="both", labelsize=8)
ax.set_yticks([i / (num_explainers - 1) for i in range(0, num_explainers)])
ax.set_yticklabels(explainers, rotation=0)
ax.set_xticks(np.linspace(0, 1, num_metrics + 1))
ax.set_xticklabels([" "] + explainer_metrics, rotation=45, ha="right")
plt.grid(which="major", axis="x", linestyle="--")
plt.tight_layout()
plt.ylabel("Relative Performance of Each Explanation Method")
plt.xlabel("Evaluation Metrics")
plt.title("Explanation Method Performance Across Metrics")
plt.show()