evaluate.py 3.48 KiB
# SPDX-FileCopyrightText: Copyright © 2023 Idiap Research Institute <contact@idiap.ch>
#
# SPDX-License-Identifier: GPL-3.0-or-later
import pathlib
import typing
import click
from clapper.click import ResourceOption, verbosity_option
from clapper.logging import setup
from ...models.typing import SaliencyMapAlgorithm
from ..click import ConfigCommand
# avoids X11/graphical desktop requirement when creating plots
__import__("matplotlib").use("agg")
logger = setup(__name__.split(".")[0], format="%(levelname)s: %(message)s")
@click.command(
entry_point_group="ptbench.config",
cls=ConfigCommand,
epilog="""Examples:
1. Tabulates and generates plots for two saliency map algorithms:
.. code:: sh
ptbench saliency evaluate -vv -e gradcam path/to/gradcam-completeness.json path/to/gradcam-interpretability.json -e gradcam++ path/to/gradcam++-completeness.json path/to/gradcam++-interpretability.json
""",
)
@click.option(
"--entry",
"-e",
required=True,
multiple=True,
help=f"ENTRY is a triplet containing the algorithm name, the path to the "
f"scores issued from the completness analysis (``ptbench "
f"saliency-completness``) and scores issued from the interpretability "
f"analysis (``ptbench saliency-interpretability``), both in JSON format. "
f"Paths to score files must exist before the program is called. Valid values "
f"for saliency map algorithms are "
f"{'|'.join(typing.get_args(SaliencyMapAlgorithm))}",
type=(
click.Choice(
typing.get_args(SaliencyMapAlgorithm), case_sensitive=False
),
click.Path(
exists=True,
file_okay=True,
dir_okay=False,
path_type=pathlib.Path,
),
click.Path(
exists=True,
file_okay=True,
dir_okay=False,
path_type=pathlib.Path,
),
),
cls=ResourceOption,
)
@click.option(
"--output-folder",
"-o",
help="Path where to store the analysis result (created if does not exist)",
required=False,
default="results",
type=click.Path(file_okay=False, dir_okay=True, path_type=pathlib.Path),
cls=ResourceOption,
)
@verbosity_option(logger=logger, expose_value=False)
def evaluate(
entry,
output_folder,
**_, # ignored
) -> None:
"""Calculates summary statistics for a saliency map algorithm."""
import json
from matplotlib.backends.backend_pdf import PdfPages
from ...engine.saliency.evaluator import run, summary_table
summary = {
algo: run(algo, json.load(complet.open()), json.load(interp.open()))
for algo, complet, interp in entry
}
table = summary_table(summary, "rst")
click.echo(summary)
if output_folder is not None:
output_folder.mkdir(parents=True, exist_ok=True)
table_path = output_folder / "summary.rst"
logger.info(f"Saving summary table at `{table_path}`...")
with table_path.open("w") as f:
f.write(table)
figure_path = output_folder / "plots.pdf"
logger.info(f"Saving figures at `{figure_path}`...")
with PdfPages(figure_path) as pdf:
for dataset in summary.keys():
pdf.savefig(summary[dataset]["aopc-combined"]["plot"])
pdf.savefig(summary[dataset]["proportional-energy"]["plot"])
pdf.savefig(
summary[dataset]["road-weighted-proportional-energy"][
"plot"
]
)