From afdf3f432b0b4102bb5da37fcbd985f38c9f2dc9 Mon Sep 17 00:00:00 2001 From: Tim Laibacher <tim.laibacher@idiap.ch> Date: Mon, 29 Apr 2019 18:00:53 +0200 Subject: [PATCH] Add metrics compare cli --- bob/ip/binseg/engine/__init__.py | 3 ++ bob/ip/binseg/engine/inferencer.py | 3 +- bob/ip/binseg/script/binseg.py | 28 ++++++++++++++- bob/ip/binseg/utils/click.py | 49 +++++++++++++++++++++++++ bob/ip/binseg/utils/plot.py | 55 ++++++++++++++++++++++++++++- conda/meta.yaml | 4 +++ metrics.pkl | 0 precision_recall_comparison.pdf | Bin 0 -> 18677 bytes requirements.txt | 3 +- setup.py | 1 + 10 files changed, 141 insertions(+), 5 deletions(-) create mode 100644 bob/ip/binseg/engine/__init__.py create mode 100644 bob/ip/binseg/utils/click.py delete mode 100644 metrics.pkl create mode 100644 precision_recall_comparison.pdf diff --git a/bob/ip/binseg/engine/__init__.py b/bob/ip/binseg/engine/__init__.py new file mode 100644 index 00000000..2ca5e07c --- /dev/null +++ b/bob/ip/binseg/engine/__init__.py @@ -0,0 +1,3 @@ +# see https://docs.python.org/3/library/pkgutil.html +from pkgutil import extend_path +__path__ = extend_path(__path__, __name__) \ No newline at end of file diff --git a/bob/ip/binseg/engine/inferencer.py b/bob/ip/binseg/engine/inferencer.py index 650583fc..c8166a47 100644 --- a/bob/ip/binseg/engine/inferencer.py +++ b/bob/ip/binseg/engine/inferencer.py @@ -160,7 +160,6 @@ def do_inference( logger.info("Saving average over all input images: {}".format(metrics_file)) avg_metrics = df_metrics.groupby('threshold').mean() - avg_metrics["model_name"] = model.name avg_metrics.to_csv(metrics_path) avg_metrics["f1_score"] = 2* avg_metrics["precision"]*avg_metrics["recall"]/ \ @@ -175,7 +174,7 @@ def do_inference( np_avg_metrics = avg_metrics.to_numpy().T fig_name = "precision_recall.pdf".format(model.name) logger.info("saving {}".format(fig_name)) - fig = precision_recall_f1iso([np_avg_metrics[0]],[np_avg_metrics[1]], np_avg_metrics[-1]) + fig = precision_recall_f1iso([np_avg_metrics[0]],[np_avg_metrics[1]], [model.name,None]) fig_filename = os.path.join(results_subfolder, fig_name) fig.savefig(fig_filename) diff --git a/bob/ip/binseg/script/binseg.py b/bob/ip/binseg/script/binseg.py index 0db77074..d57406f7 100644 --- a/bob/ip/binseg/script/binseg.py +++ b/bob/ip/binseg/script/binseg.py @@ -25,6 +25,8 @@ from bob.ip.binseg.utils.checkpointer import DetectronCheckpointer from torch.utils.data import DataLoader from bob.ip.binseg.engine.trainer import do_train from bob.ip.binseg.engine.inferencer import do_inference +from bob.ip.binseg.utils.plot import plot_overview +from bob.ip.binseg.utils.click import OptionEatAll logger = logging.getLogger(__name__) @@ -275,4 +277,28 @@ def testcheckpoints(model # checkpointer, load last model in dir checkpointer = DetectronCheckpointer(model, save_dir = output_subfolder, save_to_disk=False) checkpointer.load(checkpoint) - do_inference(model, data_loader, device, output_subfolder) \ No newline at end of file + do_inference(model, data_loader, device, output_subfolder) + +# Plot comparison +@binseg.command(entry_point_group='bob.ip.binseg.config', cls=ConfigCommand) +@click.option( + '--output-path-list', + '-l', + required=True, + help='Pass all output paths as arguments', + cls=OptionEatAll, + ) +@click.option( + '--output-path', + '-o', + required=True, + ) +@verbosity_option(cls=ResourceOption) +def compare(output_path_list, output_path,**kwargs): + """ Compares multiple metrics files that are stored in the format mymodel/results/Metrics.csv """ + logger.debug("Output paths: {}".format(output_path_list)) + logger.info('Plotting precision vs recall curves for {}'.format(output_path_list)) + fig = plot_overview(output_path_list) + fig_filename = os.path.join(output_path, 'precision_recall_comparison.pdf') + logger.info('saving {}'.format(fig_filename)) + fig.savefig(fig_filename) diff --git a/bob/ip/binseg/utils/click.py b/bob/ip/binseg/utils/click.py new file mode 100644 index 00000000..fa358287 --- /dev/null +++ b/bob/ip/binseg/utils/click.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# https://stackoverflow.com/questions/48391777/nargs-equivalent-for-options-in-click + +import click + +class OptionEatAll(click.Option): + + def __init__(self, *args, **kwargs): + self.save_other_options = kwargs.pop('save_other_options', True) + nargs = kwargs.pop('nargs', -1) + assert nargs == -1, 'nargs, if set, must be -1 not {}'.format(nargs) + super(OptionEatAll, self).__init__(*args, **kwargs) + self._previous_parser_process = None + self._eat_all_parser = None + + def add_to_parser(self, parser, ctx): + + def parser_process(value, state): + # method to hook to the parser.process + done = False + value = [value] + if self.save_other_options: + # grab everything up to the next option + while state.rargs and not done: + for prefix in self._eat_all_parser.prefixes: + if state.rargs[0].startswith(prefix): + done = True + if not done: + value.append(state.rargs.pop(0)) + else: + # grab everything remaining + value += state.rargs + state.rargs[:] = [] + value = tuple(value) + + # call the actual process + self._previous_parser_process(value, state) + + retval = super(OptionEatAll, self).add_to_parser(parser, ctx) + for name in self.opts: + our_parser = parser._long_opt.get(name) or parser._short_opt.get(name) + if our_parser: + self._eat_all_parser = our_parser + self._previous_parser_process = our_parser.process + our_parser.process = parser_process + break + return retval \ No newline at end of file diff --git a/bob/ip/binseg/utils/plot.py b/bob/ip/binseg/utils/plot.py index 8dbdb1f7..98c9adef 100644 --- a/bob/ip/binseg/utils/plot.py +++ b/bob/ip/binseg/utils/plot.py @@ -5,6 +5,8 @@ # author_email='andre.anjos@idiap.ch', import numpy as np +import os +import csv def precision_recall_f1iso(precision, recall, names, title=None, human_perf_bsds500=False): '''Creates a precision-recall plot of the given data. @@ -115,4 +117,55 @@ def loss_curve(df): ax1.set_xlabel('epoch') plt.tight_layout() fig = ax1.get_figure() - return fig \ No newline at end of file + return fig + + +def read_metricscsv(file): + """ + Read precision and recall from csv file + + Arguments + --------- + file: str + path to file + + Returns + ------- + precision : :py:class:`np.ndarray` + recall : :py:class:`np.ndarray` + """ + with open (file, "r") as infile: + metricsreader = csv.reader(infile) + # skip header row + next(metricsreader) + precision = [] + recall = [] + for row in metricsreader: + precision.append(float(row[1])) + recall.append(float(row[2])) + return np.array(precision), np.array(recall) + + +def plot_overview(outputfolders): + """ + Plots comparison chart of all trained models + Arguments + --------- + outputfolder : list + list containing output paths of all evaluated models (e.g. ['output/model1', 'output/model2']) + + Returns + ------- + fig : matplotlib.figure.Figure + """ + precisions = [] + recalls = [] + names = [] + for folder in outputfolders: + metrics_path = os.path.join(folder,'results/Metrics.csv') + pr, re = read_metricscsv(metrics_path) + precisions.append(pr) + recalls.append(re) + names.append(folder) + fig = precision_recall_f1iso(precisions,recalls,names) + return fig diff --git a/conda/meta.yaml b/conda/meta.yaml index b85fcc20..1e569ac4 100644 --- a/conda/meta.yaml +++ b/conda/meta.yaml @@ -6,6 +6,7 @@ package: version: {{ environ.get('BOB_PACKAGE_VERSION', '0.0.1') }} build: + skip: true # [not linux] number: {{ environ.get('BOB_BUILD_NUMBER', 0) }} run_exports: - {{ pin_subpackage(name) }} @@ -38,6 +39,7 @@ requirements: - {{ pin_compatible('numpy') }} - pandas - matplotlib + - tqdm - bob.db.drive - bob.db.stare - bob.db.chasedb1 @@ -56,6 +58,8 @@ test: # test commands ("script" entry-points) from your package here - bob binseg --help - bob binseg train --help + - bob binseg test --help + - bob binseg compare --help - nosetests --with-coverage --cover-package={{ name }} -sv {{ name }} - sphinx-build -aEW {{ project_dir }}/doc {{ project_dir }}/sphinx - sphinx-build -aEb doctest {{ project_dir }}/doc sphinx diff --git a/metrics.pkl b/metrics.pkl deleted file mode 100644 index e69de29b..00000000 diff --git a/precision_recall_comparison.pdf b/precision_recall_comparison.pdf new file mode 100644 index 0000000000000000000000000000000000000000..1568e9c1a33e6421a2fd77af3bb6d720f1af156d GIT binary patch literal 18677 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 Tbm@QqV=Yd^&8?($p6LGoqvm7{ literal 0 HcmV?d00001 diff --git a/requirements.txt b/requirements.txt index ca8e6293..d2f0e445 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,4 +13,5 @@ bob.db.iostar torch torchvision pandas -matplotlib \ No newline at end of file +matplotlib +tqdm \ No newline at end of file diff --git a/setup.py b/setup.py index b3423a5d..8c8bb0bf 100644 --- a/setup.py +++ b/setup.py @@ -48,6 +48,7 @@ setup( 'bob.ip.binseg.cli': [ 'train = bob.ip.binseg.script.binseg:train', 'test = bob.ip.binseg.script.binseg:test', + 'compare = bob.bin.binseg.script.binseg:compare', 'testcheckpoints = bob.ip.binseg.script.binseg:testcheckpoints', ], -- GitLab