Skip to content

πŸ—‘οΈ Remove numba #2313

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

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,7 @@ test = [
"coverage[toml]",
"tox",
]
extra = ["numba>=0.58.1"]
full = ["anomalib[core,openvino,loggers,notebooks,extra]"]
full = ["anomalib[core,openvino,loggers,notebooks]"]
dev = ["anomalib[full,docs,test]"]

[project.scripts]
Expand Down
3 changes: 1 addition & 2 deletions src/anomalib/metrics/per_image/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# SPDX-License-Identifier: Apache-2.0

from .binclf_curve import per_image_binclf_curve, per_image_fpr, per_image_tpr
from .binclf_curve_numpy import BinclfAlgorithm, BinclfThreshsChoice
from .binclf_curve_numpy import BinclfThreshsChoice
from .pimo import AUPIMO, PIMO, AUPIMOResult, PIMOResult, aupimo_scores, pimo_curves
from .utils import (
compare_models_pairwise_ttest_rel,
Expand All @@ -20,7 +20,6 @@

__all__ = [
# constants
"BinclfAlgorithm",
"BinclfThreshsChoice",
"StatsOutliersPolicy",
"StatsRepeatedPolicy",
Expand Down
115 changes: 0 additions & 115 deletions src/anomalib/metrics/per_image/_binclf_curve_numba.py

This file was deleted.

4 changes: 2 additions & 2 deletions src/anomalib/metrics/per_image/_validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ def is_masks(masks: ndarray) -> None:
if masks.dtype.kind == "b":
pass

elif masks.dtype.kind in ("i", "u"):
elif masks.dtype.kind in {"i", "u"}:
masks_unique_vals = np.unique(masks)
if np.any((masks_unique_vals != 0) & (masks_unique_vals != 1)):
msg = (
Expand Down Expand Up @@ -243,7 +243,7 @@ def is_images_classes(images_classes: ndarray) -> None:

if images_classes.dtype.kind == "b":
pass
elif images_classes.dtype.kind in ("i", "u"):
elif images_classes.dtype.kind in {"i", "u"}:
unique_vals = np.unique(images_classes)
if np.any((unique_vals != 0) & (unique_vals != 1)):
msg = (
Expand Down
5 changes: 1 addition & 4 deletions src/anomalib/metrics/per_image/binclf_curve.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from torch import Tensor

from . import _validate, binclf_curve_numpy
from .binclf_curve_numpy import BinclfAlgorithm, BinclfThreshsChoice
from .binclf_curve_numpy import BinclfThreshsChoice

# =========================================== ARGS VALIDATION ===========================================

Expand All @@ -47,7 +47,6 @@ def _validate_is_binclf_curves(binclf_curves: Tensor, valid_threshs: Tensor | No
def per_image_binclf_curve(
anomaly_maps: Tensor,
masks: Tensor,
algorithm: BinclfAlgorithm | str = BinclfAlgorithm.NUMBA,
threshs_choice: BinclfThreshsChoice | str = BinclfThreshsChoice.MINMAX_LINSPACE,
threshs_given: Tensor | None = None,
num_threshs: int | None = None,
Expand All @@ -59,7 +58,6 @@ def per_image_binclf_curve(
Args:
anomaly_maps (Tensor): Anomaly score maps of shape (N, H, W [, D, ...])
masks (Tensor): Binary ground truth masks of shape (N, H, W [, D, ...])
algorithm (str, optional): Algorithm to use. Defaults to ALGORITHM_NUMBA.
threshs_choice (str, optional): Sequence of thresholds to use. Defaults to THRESH_SEQUENCE_MINMAX_LINSPACE.
return_result_object (bool, optional): Whether to return a `PerImageBinClfCurveResult` object. Defaults to True.

Expand Down Expand Up @@ -115,7 +113,6 @@ def per_image_binclf_curve(
threshs_array, binclf_curves_array = binclf_curve_numpy.per_image_binclf_curve(
anomaly_maps=anomaly_maps_array,
masks=masks_array,
algorithm=algorithm,
threshs_choice=threshs_choice,
threshs_given=threshs_given_array,
num_threshs=num_threshs,
Expand Down
49 changes: 8 additions & 41 deletions src/anomalib/metrics/per_image/binclf_curve_numpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,31 +20,13 @@
import numpy as np
from numpy import ndarray

try:
import numba # noqa: F401
except ImportError:
HAS_NUMBA = False
else:
HAS_NUMBA = True


if HAS_NUMBA:
from . import _binclf_curve_numba

from . import _validate

logger = logging.getLogger(__name__)

# =========================================== CONSTANTS ===========================================


class BinclfAlgorithm(Enum):
"""Algorithm to use (relates to the low-level implementation)."""

PYTHON: str = "python"
NUMBA: str = "numba"


class BinclfThreshsChoice(Enum):
"""Sequence of thresholds to use."""

Expand Down Expand Up @@ -95,8 +77,8 @@ def _validate_is_gts_batch(gts_batch: ndarray) -> None:
# =========================================== PYTHON VERSION ===========================================


def _binclf_one_curve_python(scores: ndarray, gts: ndarray, threshs: ndarray) -> ndarray:
"""One binary classification matrix at each threshold (PYTHON implementation).
def _binclf_one_curve(scores: ndarray, gts: ndarray, threshs: ndarray) -> ndarray:
"""One binary classification matrix at each threshold.

In the case where the thresholds are given (i.e. not considering all possible thresholds based on the scores),
this weird-looking function is faster than the two options in `torchmetrics` on the CPU:
Expand Down Expand Up @@ -167,10 +149,10 @@ def score_less_than_thresh(score: float, thresh: float) -> bool:
).transpose(0, 2, 1)


_binclf_multiple_curves_python = np.vectorize(_binclf_one_curve_python, signature="(n),(n),(k)->(k,2,2)")
_binclf_multiple_curves_python.__doc__ = """
Multiple binary classification matrix at each threshold (PYTHON implementation).
vectorized version of `_binclf_one_curve_python` (see above)
_binclf_multiple_curves = np.vectorize(_binclf_one_curve, signature="(n),(n),(k)->(k,2,2)")
_binclf_multiple_curves.__doc__ = """
Multiple binary classification matrix at each threshold.
vectorized version of `_binclf_one_curve` (see above)
"""

# =========================================== INTERFACE ===========================================
Expand All @@ -180,7 +162,6 @@ def binclf_multiple_curves(
scores_batch: ndarray,
Copy link
Contributor

Choose a reason for hiding this comment

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

this function was like a demux for the two versions (python, numba), but without numba it becomes kind of redundant with the one that calls it (there are the validations tho)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah I plan to simplify this with several PRs

gts_batch: ndarray,
threshs: ndarray,
algorithm: BinclfAlgorithm | str = BinclfAlgorithm.NUMBA.value,
) -> ndarray:
"""Multiple binary classification matrix (per-instance scope) at each threshold (shared).

Expand Down Expand Up @@ -220,23 +201,12 @@ def binclf_multiple_curves(

Thresholds are sorted in ascending order.
"""
algorithm = BinclfAlgorithm(algorithm)
_validate_is_scores_batch(scores_batch)
_validate_is_gts_batch(gts_batch)
_validate.is_same_shape(scores_batch, gts_batch)
_validate.is_threshs(threshs)

if algorithm == BinclfAlgorithm.NUMBA:
if HAS_NUMBA:
return _binclf_curve_numba.binclf_multiple_curves_numba(scores_batch, gts_batch, threshs)

logger.warning(
f"Algorithm '{BinclfAlgorithm.NUMBA.value}' was selected, but Numba is not installed. "
f"Falling back to '{BinclfAlgorithm.PYTHON.value}' implementation.",
"Notice that the performance will be slower. Consider installing Numba for faster computation.",
)

return _binclf_multiple_curves_python(scores_batch, gts_batch, threshs)
return _binclf_multiple_curves(scores_batch, gts_batch, threshs)


# ========================================= PER-IMAGE BINCLF CURVE =========================================
Expand All @@ -258,7 +228,6 @@ def _get_threshs_minmax_linspace(anomaly_maps: ndarray, num_threshs: int) -> nda
def per_image_binclf_curve(
anomaly_maps: ndarray,
masks: ndarray,
algorithm: BinclfAlgorithm | str = BinclfAlgorithm.NUMBA.value,
threshs_choice: BinclfThreshsChoice | str = BinclfThreshsChoice.MINMAX_LINSPACE.value,
threshs_given: ndarray | None = None,
num_threshs: int | None = None,
Expand All @@ -268,7 +237,6 @@ def per_image_binclf_curve(
Args:
anomaly_maps (ndarray): Anomaly score maps of shape (N, H, W)
masks (ndarray): Binary ground truth masks of shape (N, H, W)
algorithm (str, optional): Algorithm to use. Defaults to ALGORITHM_NUMBA.
threshs_choice (str, optional): Sequence of thresholds to use. Defaults to THRESH_SEQUENCE_MINMAX_LINSPACE.
#
# `threshs_choice`-dependent arguments
Expand Down Expand Up @@ -308,7 +276,6 @@ def per_image_binclf_curve(

Thresholds are sorted in ascending order.
"""
BinclfAlgorithm(algorithm)
threshs_choice = BinclfThreshsChoice(threshs_choice)
_validate.is_anomaly_maps(anomaly_maps)
_validate.is_masks(masks)
Expand Down Expand Up @@ -350,7 +317,7 @@ def per_image_binclf_curve(
scores_batch = anomaly_maps.reshape(anomaly_maps.shape[0], -1)
gts_batch = masks.reshape(masks.shape[0], -1).astype(bool) # make sure it is boolean

binclf_curves = binclf_multiple_curves(scores_batch, gts_batch, threshs, algorithm=algorithm)
binclf_curves = binclf_multiple_curves(scores_batch, gts_batch, threshs)

num_images = anomaly_maps.shape[0]

Expand Down
Loading
Loading