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

Benchmarking Script #17

Merged
merged 53 commits into from
Feb 9, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
dfee44e
Initial benchmarking script
Dec 3, 2021
c83f076
Merge branch 'development' into feature/ashwin/benchmarking_tools
Dec 3, 2021
198f11e
Finish benchmarking script.
Dec 3, 2021
1e04cb5
Fix param initialization
Dec 3, 2021
65e3361
Add tqdm
Dec 6, 2021
581c5dc
Fix license issue + add license to callbacks init
Dec 9, 2021
855db66
Add OpenVINO throughput
Dec 10, 2021
6b78c02
Merge branch 'development' into feature/ashwin/benchmarking_tools
Dec 30, 2021
9f143ac
Move pull request template to .github root
Dec 30, 2021
9e8ead8
Refactor Benchmarking script.
Jan 3, 2022
07c1b3c
Add tests
Jan 5, 2022
7924781
First round of refactor
Jan 6, 2022
eca1b7b
🔀 Merge branch 'feature/refactor' into feature/ashwin/benchmarking_tools
Jan 20, 2022
11cab37
🔀 Merge branch 'development' into feature/refactor
Jan 20, 2022
8366c1b
🔀 Merge branch 'feature/refactor' into feature/ashwin/benchmarking_tools
Jan 20, 2022
2875d58
🔥 Remove merge artefacts
Jan 20, 2022
3bdec07
Merge branch 'development' into feature/refactor
Jan 26, 2022
e59a34d
🔨 fix merge issues
Jan 26, 2022
e3a156b
🚚 Move components to model
Jan 26, 2022
a70ee32
🔥 remove artifacts from merge
Jan 26, 2022
239a670
Rename logger name
Jan 26, 2022
bb68181
🔨 Add wandb to pyproject config
Jan 26, 2022
2a40d08
Address PR comments
ashwinvaidya17 Jan 31, 2022
398aaad
Fix black version
ashwinvaidya17 Jan 31, 2022
9933862
🔀 Merge branch 'feature/refactor' into feature/ashwin/benchmarking_tools
ashwinvaidya17 Feb 1, 2022
dab9bc0
🔥 remove duplicate files + revert auc changes
ashwinvaidya17 Feb 1, 2022
e4d5b50
Fix license + minor refactor
ashwinvaidya17 Feb 1, 2022
31c2f16
Fix imports
ashwinvaidya17 Feb 1, 2022
bb633c3
Merge branch 'feature/refactor' into feature/ashwin/benchmarking_tools
ashwinvaidya17 Feb 1, 2022
d7790d0
Merge branch 'feature/ashwin/benchmarking_tools' of github.com:openvi…
Feb 1, 2022
68718be
Fix imports + seed
Feb 1, 2022
5bab555
Address PR comment + refactor script
Feb 4, 2022
c9b708b
🩹 Minor fixes
Feb 4, 2022
1322401
add spawn to context
Feb 7, 2022
472d6c1
isort
samet-akcay Feb 7, 2022
b06b517
Properly import AnomalyModule
samet-akcay Feb 7, 2022
ade82c0
Fix circular import
samet-akcay Feb 7, 2022
70041b1
Import Inference dataset
samet-akcay Feb 7, 2022
53bd2b5
address pre-commit
samet-akcay Feb 7, 2022
442636f
Rebase development
samet-akcay Feb 7, 2022
1030543
Rebase development
samet-akcay Feb 7, 2022
1be3443
Log csv to wandb
Feb 8, 2022
85eda00
🔥 remove redundant files from merge
Feb 8, 2022
2d44977
🔨 Fix linting issues
Feb 8, 2022
22455dc
🔥 remove duplicate tests from development merge
Feb 8, 2022
35dffec
fix cyclic imports reported by pylint
samet-akcay Feb 8, 2022
f8227f3
absolute import of AnomalyModule
samet-akcay Feb 8, 2022
8f37fca
Moved anomaly module and dynamic module out of base.anomaly_models
samet-akcay Feb 8, 2022
4f9d6a5
import AnomalyModule and DynamicBufferModule from anomalib.models.com…
samet-akcay Feb 8, 2022
b7ca2fe
reduced coverag percentage
samet-akcay Feb 8, 2022
89ed5a7
🔀 Merge branch 'feature/refactor' into feature/ashwin/benchmarking_tools
Feb 8, 2022
0ce4e0f
pull development and resolve conflicts
samet-akcay Feb 8, 2022
16e8b39
Move metrics calculation to benchmark.py
Feb 8, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ instance/
.scrapy

# Sphinx documentation
docs/_build/
docs/build/
docs/source/_build/

# PyBuilder
.pybuilder/
Expand Down
14 changes: 14 additions & 0 deletions anomalib/core/callbacks/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
"""Callbacks for Anomalib models."""

# Copyright (C) 2020 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions
# and limitations under the License.

import os
from importlib import import_module
from typing import List, Union
Expand Down
16 changes: 14 additions & 2 deletions tools/benchmarking/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

These bash scripts will assist in measuring the training performance of the anomalib library.

The python script (`benchmark.py`) will assist in computing metrics for all the models in the repository.

## Usage
Run the train.sh with the same args as the tools/train.py. Refer to [`../README.md`](https://gitlab-icv.inn.intel.com/algo_rnd_team/anomaly/README.md) for those details.
Run the train.sh with the same args as the tools/train.py. Refer to [`../README.md`](https://github.com/openvinotoolkit/anomalib/blob/development/README.md) for those details.

Note: To collect memory read/write numbers, run the script with sudo priviledges. Otherwise, those values will be blank.
Note: To collect memory read/write numbers, run the script with sudo privileges. Otherwise, those values will be blank.

```
sudo -E ./train.sh # Train STFPM on MVTec leather
Expand All @@ -22,3 +24,13 @@ For post processing, run the post-process.sh script with the results directory y
```
./post-process.sh ./output/2021Aug31_2351
```

---

To use the python script, run it from the root directory.

```
python tools/benchmarking/benchmark.py
```

The output will be generated in results folder and a csv file for each model.
184 changes: 184 additions & 0 deletions tools/benchmarking/benchmark.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
"""Benchmark all the algorithms in the repo."""

# Copyright (C) 2020 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions
# and limitations under the License.


import io
import os
import re
from contextlib import redirect_stdout
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import Dict, List, Union

import pandas as pd
from omegaconf import DictConfig, ListConfig
from pytorch_lightning import Trainer, seed_everything
from pytorch_lightning.callbacks.base import Callback
from pytorch_lightning.callbacks.model_checkpoint import ModelCheckpoint
from tqdm import tqdm

from anomalib.config import get_configurable_parameters
from anomalib.core.callbacks import get_callbacks
from anomalib.data import get_datamodule
from anomalib.models import get_model

MODEL_LIST = ["padim", "dfkde", "dfm", "patchcore", "stfpm"]
SEED = 42

# Modify category list according to dataset
CATEGORY_LIST = [
"bottle",
"cable",
"capsule",
"carpet",
"grid",
"hazelnut",
"leather",
"metal_nut",
"pill",
"screw",
"tile",
"toothbrush",
"transistor",
"wood",
"zipper",
]


def update_callbacks(config: Union[DictConfig, ListConfig]) -> List[Callback]:
samet-akcay marked this conversation as resolved.
Show resolved Hide resolved
"""Disable callbacks in config.

Args:
config (Union[DictConfig, ListConfig]): Model config loaded from anomalib
"""
config.project.log_images_to = [] # disable visualizer callback

# disable openvino optimization
if "optimization" in config.keys():
config.pop("optimization")

# disable load model callback
if "weight_file" in config.model.keys():
config.model.pop("weight_file")

# disable save_to_csv. Metrics will be captured from stdout
if "save_to_csv" in config.project.keys():
config.project.pop("save_to_csv")

callbacks = get_callbacks(config)

# remove ModelCheckpoint callback
for index, callback in enumerate(callbacks):
if isinstance(callback, ModelCheckpoint):
callbacks.pop(index)
break

return callbacks


def get_single_model_metrics(model_name: str, gpu_count: int, category: str) -> Dict:
"""Collects metrics for `model_name` and returns a dict of results.

Args:
model_name (str): Name of the model
gpu_count (int): Number of gpus. Use `gpu_count=0` for cpu
category (str): Category of the dataset

Returns:
Dict: Collection of all the metrics such as time taken, throughput and performance scores.
"""
config = get_configurable_parameters(model_name=model_name)
# Seed for reproducibility
seed_everything(42)

config.trainer.gpus = gpu_count
config.dataset.category = category

with TemporaryDirectory() as project_path:
config.project.path = project_path
datamodule = get_datamodule(config)
model = get_model(config)

callbacks = update_callbacks(config)

trainer = Trainer(**config.trainer, logger=None, callbacks=callbacks)

stdout = io.StringIO()
with redirect_stdout(stdout):
trainer.fit(model=model, datamodule=datamodule)

# get training time
captured_output = stdout.getvalue()
pattern = re.compile(r"Training took (\d*.\d*) seconds")
search_result = pattern.search(captured_output)
training_time = float("nan")
if search_result is not None:
training_time = float(search_result.group(1))
samet-akcay marked this conversation as resolved.
Show resolved Hide resolved

# Creating new variable is faster according to https://stackoverflow.com/a/4330829
stdout = io.StringIO()
with redirect_stdout(stdout):
trainer.test(model=model, datamodule=datamodule)

captured_output = stdout.getvalue()

# get testing time
pattern = re.compile(r"Testing took (\d*.\d*) seconds")
search_result = pattern.search(captured_output)
testing_time = float("nan")
if search_result is not None:
testing_time = float(search_result.group(1))

pattern = re.compile(r"Throughput\s?:\s?(\d+.\d+)")
search_result = pattern.search(captured_output)
throughput = float("nan")
if search_result is not None:
throughput = float(search_result.group(1))

# Get metrics
pattern = re.compile(r"\s?[\"'](\w+)[\"']:\s?(\d+.\d+)")
metrics = re.findall(pattern, captured_output)
samet-akcay marked this conversation as resolved.
Show resolved Hide resolved

# arrange the data
data = {
"Training Time (s)": training_time,
"Testing Time (s)": testing_time,
"Inference Throughput (fps)": throughput,
samet-akcay marked this conversation as resolved.
Show resolved Hide resolved
}
for key, val in metrics:
data[key] = float(val)

return data


def sweep():
"""Go over all models, categories, and devices and collect metrics."""
for model_name in MODEL_LIST:
metrics_list = []
for category in tqdm(CATEGORY_LIST, desc=f"{model_name}:"):
for gpu_count in range(0, 2):
model_metrics = get_single_model_metrics(model_name, gpu_count, category)
samet-akcay marked this conversation as resolved.
Show resolved Hide resolved
model_metrics["Device"] = "CPU" if gpu_count == 0 else "GPU"
model_metrics["Category"] = category
metrics_list.append(model_metrics)
metrics_df = pd.DataFrame(metrics_list)
result_path = Path(f"results/{model_name}.csv")
os.makedirs(result_path.parent, exist_ok=True)
metrics_df.to_csv(result_path)


if __name__ == "__main__":
sweep()