Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

πŸš€ Anomalib Pipelines #2005

Conversation

ashwinvaidya17
Copy link
Collaborator

πŸ“ Description

  • Alternate design for pipeline

✨ Changes

Select what type of change your PR is:

  • 🐞 Bug fix (non-breaking change which fixes an issue)
  • πŸ”¨ Refactor (non-breaking change which refactors the code base)
  • πŸš€ New feature (non-breaking change which adds functionality)
  • πŸ’₯ Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • πŸ“š Documentation update
  • πŸ”’ Security update

βœ… Checklist

Before you submit your pull request, please make sure you have completed the following steps:

  • πŸ“‹ I have summarized my changes in the CHANGELOG and followed the guidelines for my type of change (skip for minor changes, documentation updates, and test enhancements).
  • πŸ“š I have made the necessary updates to the documentation (if applicable).
  • πŸ§ͺ I have written tests that support my changes and prove that my fix is effective or my feature works (if applicable).

For more information about code review checklists, see the Code Review Checklist.

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
@ashwinvaidya17
Copy link
Collaborator Author

Patchcore does not work with SerialRunner as rich progress bar clashes with corset subsampling's progress bar.

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
@ashwinvaidya17 ashwinvaidya17 marked this pull request as ready for review April 23, 2024 14:07
@samet-akcay samet-akcay changed the title πŸš€ Initial pipeline design v2 πŸš€ Anomalib Pipelines Apr 23, 2024
Copy link
Contributor

@samet-akcay samet-akcay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

took me some time, but managed to do my first round :)

It is looking good so far! I've got some minor comments initially. Will go for another round later

Comment on lines 13 to 15
PIPELINE_REGISTRY: dict[str, Orchestrator] | None = {
"benchmark": Benchmark(),
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this fit into a single line?

src/anomalib/data/__init__.py Show resolved Hide resolved
Comment on lines 23 to 26
from anomalib.pipelines.utils import (
dict_from_namespace,
hide_output,
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Single line?


@staticmethod
@abstractmethod
def get_iterator(args: Namespace | None = None) -> Iterator:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we find a more descriptive name? Does this only returns configs each time? If yes, wouldn't iterator be too generic?

result.to_csv(file_path, index=False)
self.logger.info(f"Saved results to {file_path}")

def _print_tabular_results(self, gathered_result: pd.DataFrame) -> None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this be used anywhere else? If so, maybe we could move this to a util function to keep this class cleaner?

src/anomalib/pipelines/components/actions/grid_search.py Outdated Show resolved Hide resolved
Comment on lines 15 to 24
log_file = "runs/pipeline.log"
Path(log_file).parent.mkdir(exist_ok=True, parents=True)
logger_file_handler = logging.FileHandler(log_file)
logging.getLogger().addHandler(logger_file_handler)
logging.getLogger().setLevel(logging.DEBUG)
warnings.filterwarnings("ignore")
for logger_name in ["lightning.pytorch", "lightning.fabric", "torchmetrics", "os"]:
logging.getLogger(logger_name).handlers = [logger_file_handler]
format_string = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
logging.basicConfig(format=format_string, level=logging.DEBUG)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to wrap this in a function, like setup_logging or something similar. It looks a bit messy this way

tests/integration/pipelines/__init__.py Outdated Show resolved Hide resolved
tests/integration/pipelines/test_benchmark.py Show resolved Hide resolved
@@ -0,0 +1,3 @@
# Anomalib Experimental

These are experimental utilities that are under development. These might change frequently or might even be dropped.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add this in a warning section

Co-authored-by: Samet Akcay <samet.akcay@intel.com>
ashwinvaidya17 and others added 3 commits May 1, 2024 13:39
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Co-authored-by: Samet Akcay <samet.akcay@intel.com>
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
@blaz-r
Copy link
Contributor

blaz-r commented May 3, 2024

I glanced over the design real quick, seems great πŸ˜„. I'll do a more thorough overview asap, mostly from the viewpoint of the tiled ensemble.

Copy link
Contributor

@djdameln djdameln left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I think we're getting there. I like the new Generator design which returns the job instance. My biggest concern with the current design is that the argument parsing is spread across multiple classes, which makes it a bit hard to follow. This may make it a bit intimidating for users to implement their own custom pipeline. Do you think we could simplify this in some way?

Since we want to encourage users to implement their own custom pipelines, we need to make sure that the functionality of the different components is very clear. I think it would be good to add a bit more detail to the docstrings of the classes (Pipeline, Job, Generator, Runner) including some examples to make it easier for the users to follow.

In line with this, I think it would be good to also add a guide to our documentation explaining step by step how to implement a new pipeline.



class ParallelRunner(Runner):
"""Run the job in parallel using a process pool."""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be a bit more descriptive and maybe show some examples

"""Pool execution error should be raised when one or more jobs fail in the pool."""


class ParallelRunner(Runner):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to tell the runner which devices to use? Or does it always distribute the jobs across all available GPUs?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parallel runner just creates an execution pool and passes the task_id to the job's run method. The job is responsible for using this task_id to use the appropriate device.

"""Generate BenchmarkJob."""

def __init__(self, accelerator: str) -> None:
self.accelerator = accelerator
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about the terminology here. We use this variable mainly to distinguish between cpu and gpu, but I'm not sure if cpu is technically considered to be an accelerator. Maybe device would be a more suitable name?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Originally this was called device. I think we discussed on changing this to accelerator to be inline with lightning's terminology. I have no preference here. So, I can rename this once we finalise the name.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -288,7 +294,7 @@ def instantiate_classes(self) -> None:
self.model = self._get(self.config_init, "model")
self._configure_optimizers_method_to_model()
self.instantiate_engine()
else:
elif self.config["subcommand"] != "pipeline":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer anomalib benchmark ... but if someone implements a custom pipeline then I feel they should be able to run it without making changes to the cli. In this case they might have to use anomalib pipeline cutom_pipeline?

src/anomalib/pipelines/benchmark/generator.py Outdated Show resolved Hide resolved
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be an idea to use warnings.warn in the entrypoint scripts to inform the user whenever they use these features?

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Copy link
Contributor

@samet-akcay samet-akcay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good to me. My only feedback is the lack of examples in the docstring and documentation. If you would like to add this in a follow-up PR, that is also fine.

@blaz-r
Copy link
Contributor

blaz-r commented May 14, 2024

I will check this thoroughly in a few hours and leave some feedback.

@blaz-r
Copy link
Contributor

blaz-r commented May 14, 2024

For some reason I keep getting rich.errors.LiveError: Only one live display may be active at once when trying to run the benchmark, although I'm only using Padim.

Comment on lines +58 to +68
for runner in runners:
try:
_args = args.get(runner.generator.job_class.name, None)
runner.run(_args)
except Exception: # noqa: PERF203 catch all exception and allow try-catch in loop
logger.exception("An error occurred when running the runner.")
print(
f"There were some errors when running [red]{runner.generator.job_class.name}[/red] with"
f" [green]{runner.__class__.__name__}[/green]."
f" Please check [magenta]{log_file}[/magenta] for more details.",
)
Copy link
Contributor

@blaz-r blaz-r May 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would this work if I wanted to take results from runner and use them as the input for the next one.
I see that a single runner collects the results at the end. But in case of tiled ensemble, training can be parallel, and then I'd have just a single serial runner that assembles the data back together. With this design I am not entirely sure how that would work.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then I guess this design falls short πŸ™‚ I'll have a look again at your code to see what changes need to be made.

Copy link
Contributor

@blaz-r blaz-r May 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. I think that having a pipeline, at least for me, would mean that you chain some elements and result of one is input for the next one. I also see no problem of using pipelines recursively (not a very deep recursion). So let's says in case of the tiled ensemble it'd look something like:
Training[Parallel]->merging[serial]->postprocessing(implemented as sub-pipeline consisting of serialRunners each doing one step like threshold, norm ...).
Tiled ensemble stores the data inside a class, that handles storage at different indices. Maybe this pattern of "storage" class can also be used in pipelines, so each runner returns that and it can be customized for each specific usecase.

@blaz-r
Copy link
Contributor

blaz-r commented May 14, 2024

I checked the code and the design is quite nice. I have just one question that I commented on the relevant part.

@ashwinvaidya17
Copy link
Collaborator Author

For some reason I keep getting rich.errors.LiveError: Only one live display may be active at once when trying to run the benchmark, although I'm only using Padim.

I'll have a look. Even patchcore breaks due to rich progress bar in kcenter-greedy. Maybe we shouldn't merge it till this issue is not solved

@ashwinvaidya17 ashwinvaidya17 changed the base branch from main to feature/pipelines May 15, 2024 09:11
@ashwinvaidya17 ashwinvaidya17 merged commit 5ff7f10 into openvinotoolkit:feature/pipelines May 15, 2024
5 of 7 checks passed
ashwinvaidya17 added a commit that referenced this pull request May 24, 2024
* πŸš€ Anomalib Pipelines  (#2005)

* Add initial design

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* Refactor + add to CLI

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* Support grid search on class path

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* redirect outputs

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* design v2

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* remove commented code

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* add dummy experiment

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* add config

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* Refactor

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* Add tests

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* Apply suggestions from code review

Co-authored-by: Samet Akcay <samet.akcay@intel.com>

* address pr comments

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* Apply suggestions from code review

Co-authored-by: Samet Akcay <samet.akcay@intel.com>

* refactor

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* Simplify argparse

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* modify logger redirect

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* update docstrings

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

---------

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Co-authored-by: Samet Akcay <samet.akcay@intel.com>

* 🐞 Fix Rich Progress with Patchcore Training (#2062)

Add safe track

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* [Pipelines] πŸ”¨ Intra-stage result passing (#2061)

* Add initial design

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* Refactor + add to CLI

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* Support grid search on class path

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* redirect outputs

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* design v2

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* remove commented code

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* add dummy experiment

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* add config

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* Refactor

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* Add tests

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* Apply suggestions from code review

Co-authored-by: Samet Akcay <samet.akcay@intel.com>

* address pr comments

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* Apply suggestions from code review

Co-authored-by: Samet Akcay <samet.akcay@intel.com>

* refactor

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* Simplify argparse

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* modify logger redirect

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* update docstrings

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

* Add proposal

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>

---------

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Co-authored-by: Samet Akcay <samet.akcay@intel.com>

* Update src/anomalib/pipelines/benchmark/job.py

---------

Signed-off-by: Ashwin Vaidya <ashwinnitinvaidya@gmail.com>
Co-authored-by: Samet Akcay <samet.akcay@intel.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants