-
Notifications
You must be signed in to change notification settings - Fork 708
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* 🚀 Anomalib Pipelines (#2005) * Add initial design Signed-off-by: Ashwin Vaidya <[email protected]> * Refactor + add to CLI Signed-off-by: Ashwin Vaidya <[email protected]> * Support grid search on class path Signed-off-by: Ashwin Vaidya <[email protected]> * redirect outputs Signed-off-by: Ashwin Vaidya <[email protected]> * design v2 Signed-off-by: Ashwin Vaidya <[email protected]> * remove commented code Signed-off-by: Ashwin Vaidya <[email protected]> * add dummy experiment Signed-off-by: Ashwin Vaidya <[email protected]> * add config Signed-off-by: Ashwin Vaidya <[email protected]> * Refactor Signed-off-by: Ashwin Vaidya <[email protected]> * Add tests Signed-off-by: Ashwin Vaidya <[email protected]> * Apply suggestions from code review Co-authored-by: Samet Akcay <[email protected]> * address pr comments Signed-off-by: Ashwin Vaidya <[email protected]> * Apply suggestions from code review Co-authored-by: Samet Akcay <[email protected]> * refactor Signed-off-by: Ashwin Vaidya <[email protected]> * Simplify argparse Signed-off-by: Ashwin Vaidya <[email protected]> * modify logger redirect Signed-off-by: Ashwin Vaidya <[email protected]> * update docstrings Signed-off-by: Ashwin Vaidya <[email protected]> --------- Signed-off-by: Ashwin Vaidya <[email protected]> Co-authored-by: Samet Akcay <[email protected]> * 🐞 Fix Rich Progress with Patchcore Training (#2062) Add safe track Signed-off-by: Ashwin Vaidya <[email protected]> * [Pipelines] 🔨 Intra-stage result passing (#2061) * Add initial design Signed-off-by: Ashwin Vaidya <[email protected]> * Refactor + add to CLI Signed-off-by: Ashwin Vaidya <[email protected]> * Support grid search on class path Signed-off-by: Ashwin Vaidya <[email protected]> * redirect outputs Signed-off-by: Ashwin Vaidya <[email protected]> * design v2 Signed-off-by: Ashwin Vaidya <[email protected]> * remove commented code Signed-off-by: Ashwin Vaidya <[email protected]> * add dummy experiment Signed-off-by: Ashwin Vaidya <[email protected]> * add config Signed-off-by: Ashwin Vaidya <[email protected]> * Refactor Signed-off-by: Ashwin Vaidya <[email protected]> * Add tests Signed-off-by: Ashwin Vaidya <[email protected]> * Apply suggestions from code review Co-authored-by: Samet Akcay <[email protected]> * address pr comments Signed-off-by: Ashwin Vaidya <[email protected]> * Apply suggestions from code review Co-authored-by: Samet Akcay <[email protected]> * refactor Signed-off-by: Ashwin Vaidya <[email protected]> * Simplify argparse Signed-off-by: Ashwin Vaidya <[email protected]> * modify logger redirect Signed-off-by: Ashwin Vaidya <[email protected]> * update docstrings Signed-off-by: Ashwin Vaidya <[email protected]> * Add proposal Signed-off-by: Ashwin Vaidya <[email protected]> --------- Signed-off-by: Ashwin Vaidya <[email protected]> Co-authored-by: Samet Akcay <[email protected]> * Update src/anomalib/pipelines/benchmark/job.py --------- Signed-off-by: Ashwin Vaidya <[email protected]> Co-authored-by: Samet Akcay <[email protected]>
- Loading branch information
1 parent
5ca1612
commit a4c5a2b
Showing
36 changed files
with
1,068 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
"""Subcommand for pipelines.""" | ||
|
||
# Copyright (C) 2024 Intel Corporation | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
|
||
import logging | ||
|
||
from jsonargparse import Namespace | ||
|
||
from anomalib.cli.utils.help_formatter import get_short_docstring | ||
from anomalib.utils.exceptions import try_import | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
if try_import("anomalib.pipelines"): | ||
from anomalib.pipelines import Benchmark | ||
from anomalib.pipelines.components.base import Pipeline | ||
|
||
PIPELINE_REGISTRY: dict[str, type[Pipeline]] | None = {"benchmark": Benchmark} | ||
else: | ||
PIPELINE_REGISTRY = None | ||
|
||
|
||
def pipeline_subcommands() -> dict[str, dict[str, str]]: | ||
"""Return subcommands for pipelines.""" | ||
if PIPELINE_REGISTRY is not None: | ||
return {name: {"description": get_short_docstring(pipeline)} for name, pipeline in PIPELINE_REGISTRY.items()} | ||
return {} | ||
|
||
|
||
def run_pipeline(args: Namespace) -> None: | ||
"""Run pipeline.""" | ||
logger.warning("This feature is experimental. It may change or be removed in the future.") | ||
if PIPELINE_REGISTRY is not None: | ||
subcommand = args.subcommand | ||
config = args[subcommand] | ||
PIPELINE_REGISTRY[subcommand]().run(config) | ||
else: | ||
msg = "Pipeline is not available" | ||
raise ValueError(msg) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
"""Pipelines for end-to-end usecases.""" | ||
|
||
# Copyright (C) 2024 Intel Corporation | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
from .benchmark import Benchmark | ||
|
||
__all__ = ["Benchmark"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
"""Benchmarking.""" | ||
|
||
# Copyright (C) 2024 Intel Corporation | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
from .pipeline import Benchmark | ||
|
||
__all__ = ["Benchmark"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
"""Benchmark job generator.""" | ||
|
||
# Copyright (C) 2024 Intel Corporation | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
from collections.abc import Generator | ||
|
||
from anomalib.data import get_datamodule | ||
from anomalib.models import get_model | ||
from anomalib.pipelines.components import JobGenerator | ||
from anomalib.pipelines.components.utils import get_iterator_from_grid_dict | ||
from anomalib.pipelines.types import PREV_STAGE_RESULT | ||
from anomalib.utils.logging import hide_output | ||
|
||
from .job import BenchmarkJob | ||
|
||
|
||
class BenchmarkJobGenerator(JobGenerator): | ||
"""Generate BenchmarkJob. | ||
Args: | ||
accelerator (str): The accelerator to use. | ||
""" | ||
|
||
def __init__(self, accelerator: str) -> None: | ||
self.accelerator = accelerator | ||
|
||
@property | ||
def job_class(self) -> type: | ||
"""Return the job class.""" | ||
return BenchmarkJob | ||
|
||
@hide_output | ||
def generate_jobs( | ||
self, | ||
args: dict, | ||
previous_stage_result: PREV_STAGE_RESULT, | ||
) -> Generator[BenchmarkJob, None, None]: | ||
"""Return iterator based on the arguments.""" | ||
del previous_stage_result # Not needed for this job | ||
for _container in get_iterator_from_grid_dict(args): | ||
yield BenchmarkJob( | ||
accelerator=self.accelerator, | ||
seed=_container["seed"], | ||
model=get_model(_container["model"]), | ||
datamodule=get_datamodule(_container["data"]), | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
"""Benchmarking job.""" | ||
|
||
# Copyright (C) 2024 Intel Corporation | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
import logging | ||
from datetime import datetime | ||
from pathlib import Path | ||
from tempfile import TemporaryDirectory | ||
from typing import Any | ||
|
||
import pandas as pd | ||
from lightning import seed_everything | ||
from rich.console import Console | ||
from rich.table import Table | ||
|
||
from anomalib.data import AnomalibDataModule | ||
from anomalib.engine import Engine | ||
from anomalib.models import AnomalyModule | ||
from anomalib.pipelines.components import Job | ||
from anomalib.utils.logging import hide_output | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class BenchmarkJob(Job): | ||
"""Benchmarking job. | ||
Args: | ||
accelerator (str): The accelerator to use. | ||
model (AnomalyModule): The model to use. | ||
datamodule (AnomalibDataModule): The data module to use. | ||
seed (int): The seed to use. | ||
""" | ||
|
||
name = "benchmark" | ||
|
||
def __init__(self, accelerator: str, model: AnomalyModule, datamodule: AnomalibDataModule, seed: int) -> None: | ||
super().__init__() | ||
self.accelerator = accelerator | ||
self.model = model | ||
self.datamodule = datamodule | ||
self.seed = seed | ||
|
||
@hide_output | ||
def run( | ||
self, | ||
task_id: int | None = None, | ||
) -> dict[str, Any]: | ||
"""Run the benchmark.""" | ||
devices: str | list[int] = "auto" | ||
if task_id is not None: | ||
devices = [task_id] | ||
logger.info(f"Running job {self.model.__class__.__name__} with device {task_id}") | ||
with TemporaryDirectory() as temp_dir: | ||
seed_everything(self.seed) | ||
engine = Engine( | ||
accelerator=self.accelerator, | ||
devices=devices, | ||
default_root_dir=temp_dir, | ||
) | ||
engine.fit(self.model, self.datamodule) | ||
test_results = engine.test(self.model, self.datamodule) | ||
# TODO(ashwinvaidya17): Restore throughput | ||
# https://github.com/openvinotoolkit/anomalib/issues/2054 | ||
output = { | ||
"seed": self.seed, | ||
"accelerator": self.accelerator, | ||
"model": self.model.__class__.__name__, | ||
"data": self.datamodule.__class__.__name__, | ||
"category": self.datamodule.category, | ||
**test_results[0], | ||
} | ||
logger.info(f"Completed with result {output}") | ||
return output | ||
|
||
@staticmethod | ||
def collect(results: list[dict[str, Any]]) -> pd.DataFrame: | ||
"""Gather the results returned from run.""" | ||
output: dict[str, Any] = {} | ||
for key in results[0]: | ||
output[key] = [] | ||
for result in results: | ||
for key, value in result.items(): | ||
output[key].append(value) | ||
return pd.DataFrame(output) | ||
|
||
@staticmethod | ||
def save(result: pd.DataFrame) -> None: | ||
"""Save the result to a csv file.""" | ||
BenchmarkJob._print_tabular_results(result) | ||
file_path = Path("runs") / BenchmarkJob.name / datetime.now().strftime("%Y-%m-%d-%H_%M_%S") / "results.csv" | ||
file_path.parent.mkdir(parents=True, exist_ok=True) | ||
result.to_csv(file_path, index=False) | ||
logger.info(f"Saved results to {file_path}") | ||
|
||
@staticmethod | ||
def _print_tabular_results(gathered_result: pd.DataFrame) -> None: | ||
"""Print the tabular results.""" | ||
if gathered_result is not None: | ||
console = Console() | ||
table = Table(title=f"{BenchmarkJob.name} Results", show_header=True, header_style="bold magenta") | ||
_results = gathered_result.to_dict("list") | ||
for column in _results: | ||
table.add_column(column) | ||
for row in zip(*_results.values(), strict=False): | ||
table.add_row(*[str(value) for value in row]) | ||
console.print(table) |
Oops, something went wrong.