Skip to content

Commit 6e1d870

Browse files
authored
Metrics redesign (#2326)
* refactor metrics callback * add Evaluator class * remove unused import * add AnomalibMetric base class * cleanup * add create_anomalib_metric function * use new AnomalibMetric class * verify keys when updating metric * use anomalib PR curve for f1max * replace evaluator with metrics arg * revert removing exportable transform method * fix unit tests * add kwargs to lightning models * fix pre-commit issues * remove kwargs key from update config * add Evaluator to metrics init * set default evaluator for image-only models * fix cfa tests * fix model tests * update changelog * remove tests of deprecated callback modules * fix engine tests * fix 201 notebook * fix 600 notebook * add default evaluator to fastflow * revert cfa changes and fix pred score computation * simplify evaluator argument * validate metrics in evaluator * remove unused import * fix pred_label shape validation * fix method name * fix pred_label validator * update validator tests * fix method name in fastflow * add AnomalibMetric functionality to PIMO * add optional default fields and implement for pimo * update aupimo notebooks * use explicit args in lightning model signature * remove kwargs from evaluator * add _resolve_evaluator method * fix config upgrade test * do not force pypi install in mlflow notebook * do not force pypi install in mlflow notebook
1 parent 95115f9 commit 6e1d870

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+737
-1520
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
2929

3030
### Changed
3131

32+
- Implement new design for metrics by @djdameln https://github.com/openvinotoolkit/anomalib/pull/2326
3233
- Set permissions for github workflows by @djdameln in https://github.com/openvinotoolkit/anomalib/pull/2127
3334
- Update timm requirement from <=1.0.3,>=0.5.4 to >=0.5.4,<=1.0.7 by @dependabot in https://github.com/openvinotoolkit/anomalib/pull/2151
3435
- 🚀 Use gh actions runners for pre-commit checks by @ashwinvaidya17 in https://github.com/openvinotoolkit/anomalib/pull/2160

notebooks/200_models/201_fastflow.ipynb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,6 @@
233233
"source": [
234234
"engine = Engine(\n",
235235
" callbacks=callbacks,\n",
236-
" pixel_metrics=\"AUROC\",\n",
237236
" accelerator=\"auto\", # \\<\"cpu\", \"gpu\", \"tpu\", \"ipu\", \"hpu\", \"auto\">,\n",
238237
" devices=1,\n",
239238
" logger=False,\n",

notebooks/600_loggers/601_mlflow_logging.ipynb

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"metadata": {},
3939
"outputs": [],
4040
"source": [
41-
"%pip install -qU anomalib"
41+
"%pip install anomalib"
4242
]
4343
},
4444
{
@@ -56,7 +56,7 @@
5656
"metadata": {},
5757
"outputs": [],
5858
"source": [
59-
"%pip install -qU anomalib[loggers]"
59+
"%pip install anomalib[loggers]"
6060
]
6161
},
6262
{
@@ -307,7 +307,6 @@
307307
"\n",
308308
"engine = Engine(\n",
309309
" callbacks=callbacks,\n",
310-
" pixel_metrics=\"AUROC\",\n",
311310
" accelerator=\"auto\",\n",
312311
" devices=1,\n",
313312
" logger=mlflow_logger, # Logger is set here\n",

notebooks/700_metrics/701a_aupimo.ipynb

Lines changed: 47 additions & 176 deletions
Large diffs are not rendered by default.

notebooks/700_metrics/701b_aupimo_advanced_i.ipynb

Lines changed: 47 additions & 279 deletions
Large diffs are not rendered by default.

notebooks/700_metrics/701c_aupimo_advanced_ii.ipynb

Lines changed: 29 additions & 145 deletions
Large diffs are not rendered by default.

src/anomalib/callbacks/metrics.py

Lines changed: 0 additions & 185 deletions
This file was deleted.

src/anomalib/cli/cli.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,6 @@ def add_arguments_to_parser(parser: ArgumentParser) -> None:
148148
``Engine`` class should be reflected manually.
149149
"""
150150
parser.add_argument("--task", type=TaskType | str, default=TaskType.SEGMENTATION)
151-
parser.add_argument("--metrics.image", type=list[str] | str | None, default=None)
152-
parser.add_argument("--metrics.pixel", type=list[str] | str | None, default=None, required=False)
153151
parser.add_argument("--logging.log_graph", type=bool, help="Log the model to the logger", default=False)
154152
if hasattr(parser, "subcommand") and parser.subcommand not in {"export", "predict"}:
155153
parser.link_arguments("task", "data.init_args.task")
@@ -323,8 +321,6 @@ def instantiate_engine(self) -> None:
323321

324322
engine_args = {
325323
"task": self._get(self.config_init, "task"),
326-
"image_metrics": self._get(self.config_init, "metrics.image"),
327-
"pixel_metrics": self._get(self.config_init, "metrics.pixel"),
328324
}
329325
trainer_config = {**self._get(self.config_init, "trainer", default={}), **engine_args}
330326
key = "callbacks"

src/anomalib/data/validators/torch/image.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -585,10 +585,16 @@ def validate_pred_label(pred_label: torch.Tensor | None) -> torch.Tensor | None:
585585
if pred_label.ndim > 2:
586586
msg = f"Predicted label must be 1-dimensional or 2-dimensional, got shape {pred_label.shape}."
587587
raise ValueError(msg)
588-
if pred_label.ndim == 2 and pred_label.shape[1] != 1:
589-
msg = f"Predicted label with 2 dimensions must have shape [N, 1], got shape {pred_label.shape}."
590-
raise ValueError(msg)
591-
588+
if pred_label.ndim == 2:
589+
if pred_label.shape[0] == 1:
590+
pred_label = pred_label.squeeze(0)
591+
elif pred_label.shape[1] == 1:
592+
pred_label = pred_label.squeeze(1)
593+
else:
594+
msg = (
595+
f"Predicted label with 2 dimensions must have shape [N, 1] or [1, N], got shape {pred_label.shape}."
596+
)
597+
raise ValueError(msg)
592598
return pred_label.to(torch.bool)
593599

594600
@staticmethod

src/anomalib/engine/engine.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
from anomalib import LearningType, TaskType
2020
from anomalib.callbacks.checkpoint import ModelCheckpoint
21-
from anomalib.callbacks.metrics import _MetricsCallback
2221
from anomalib.callbacks.timer import TimerCallback
2322
from anomalib.data import AnomalibDataModule, AnomalibDataset, PredictDataset
2423
from anomalib.deploy import CompressionType, ExportType
@@ -116,8 +115,6 @@ def __init__(
116115
self,
117116
callbacks: list[Callback] | None = None,
118117
task: TaskType | str = TaskType.SEGMENTATION,
119-
image_metrics: list[str] | str | dict[str, dict[str, Any]] | None = None,
120-
pixel_metrics: list[str] | str | dict[str, dict[str, Any]] | None = None,
121118
logger: Logger | Iterable[Logger] | bool | None = None,
122119
default_root_dir: str | Path = "results",
123120
**kwargs,
@@ -137,12 +134,6 @@ def __init__(
137134
)
138135

139136
self.task = TaskType(task)
140-
self.image_metric_names = image_metrics if image_metrics else ["AUROC", "F1Max"]
141-
142-
# pixel metrics are only used for segmentation tasks.
143-
self.pixel_metric_names = None
144-
if self.task == TaskType.SEGMENTATION:
145-
self.pixel_metric_names = pixel_metrics if pixel_metrics is not None else ["AUROC", "F1Max"]
146137

147138
self._trainer: Trainer | None = None
148139

@@ -375,7 +366,8 @@ def _setup_anomalib_callbacks(self, model: AnomalyModule) -> None:
375366
_callbacks.append(model.post_processor)
376367

377368
# Add the metrics callback.
378-
_callbacks.append(_MetricsCallback(self.task, self.image_metric_names, self.pixel_metric_names))
369+
if isinstance(model.evaluator, Callback):
370+
_callbacks.append(model.evaluator)
379371

380372
# Add the image visualizer callback if it is passed by the user.
381373
if not any(isinstance(callback, ImageVisualizer) for callback in self._cache.args["callbacks"]):

0 commit comments

Comments
 (0)