quadra 2.7.0__py3-none-any.whl → 2.7.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
quadra/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "2.7.0"
1
+ __version__ = "2.7.1"
2
2
 
3
3
 
4
4
  def get_version():
@@ -19,3 +19,4 @@ task:
19
19
  model_path: ???
20
20
  use_training_threshold: false
21
21
  training_threshold_type: image
22
+ custom_normalized_threshold: null # Optional: Custom normalized threshold (e.g., 110 for 10% above training threshold)
quadra/tasks/anomaly.py CHANGED
@@ -354,6 +354,9 @@ class AnomalibEvaluation(Evaluation[AnomalyDataModule]):
354
354
  use_training_threshold: Whether to use the training threshold for the evaluation or use the one that
355
355
  maximizes the F1 score on the test set.
356
356
  device: Device to use for evaluation. If None, the device is automatically determined.
357
+ custom_normalized_threshold: Custom normalized threshold to use for evaluation. If provided, this threshold
358
+ will be used instead of the training threshold or the optimal F1 threshold. The threshold should be
359
+ in the normalized scale (e.g., 100 means the training threshold, 110 means 10% higher than training).
357
360
  """
358
361
 
359
362
  def __init__(
@@ -363,6 +366,7 @@ class AnomalibEvaluation(Evaluation[AnomalyDataModule]):
363
366
  use_training_threshold: bool = False,
364
367
  device: str | None = None,
365
368
  training_threshold_type: Literal["image", "pixel"] | None = None,
369
+ custom_normalized_threshold: float | None = None,
366
370
  ):
367
371
  super().__init__(config=config, model_path=model_path, device=device)
368
372
 
@@ -375,7 +379,20 @@ class AnomalibEvaluation(Evaluation[AnomalyDataModule]):
375
379
  log.warning("Using training threshold but no training threshold type is provided, defaulting to image")
376
380
  training_threshold_type = "image"
377
381
 
382
+ # Default to image threshold type if custom threshold is provided but type is not specified
383
+ if training_threshold_type is None and custom_normalized_threshold is not None:
384
+ log.warning("Using custom threshold but no training threshold type is provided, defaulting to image")
385
+ training_threshold_type = "image"
386
+
378
387
  self.training_threshold_type = training_threshold_type
388
+ self.custom_normalized_threshold = custom_normalized_threshold
389
+
390
+ if self.use_training_threshold and self.custom_normalized_threshold is not None:
391
+ self.use_training_threshold = False
392
+ log.warning("Ignoring use_training_threshold since custom_normalized_threshold is provided")
393
+
394
+ if custom_normalized_threshold is not None and custom_normalized_threshold <= 0:
395
+ raise ValueError("Custom normalized threshold must be greater than 0")
379
396
 
380
397
  def prepare(self) -> None:
381
398
  """Prepare the evaluation."""
@@ -421,11 +438,18 @@ class AnomalibEvaluation(Evaluation[AnomalyDataModule]):
421
438
 
422
439
  anomaly_scores = torch.cat(anomaly_scores)
423
440
  anomaly_maps = torch.cat(anomaly_maps)
441
+ training_threshold = float(self.model_data[f"{self.training_threshold_type}_threshold"])
424
442
 
425
443
  if any(x != -1 for x in image_labels):
426
- if self.use_training_threshold:
444
+ threshold = None
445
+ if self.custom_normalized_threshold is not None:
446
+ # normalized = (raw / training) * 100, so raw = (normalized * training) / 100
447
+ threshold = torch.tensor((self.custom_normalized_threshold * training_threshold) / 100.0)
448
+ elif self.use_training_threshold:
449
+ threshold = torch.tensor(training_threshold)
450
+
451
+ if threshold is not None:
427
452
  _image_labels = torch.tensor(image_labels)
428
- threshold = torch.tensor(float(self.model_data[f"{self.training_threshold_type}_threshold"]))
429
453
  known_labels = torch.where(_image_labels != -1)[0]
430
454
 
431
455
  _image_labels = _image_labels[known_labels]
@@ -438,9 +462,12 @@ class AnomalibEvaluation(Evaluation[AnomalyDataModule]):
438
462
  optimal_f1_score = optimal_f1.compute()
439
463
  threshold = optimal_f1.threshold
440
464
  else:
441
- log.warning("No ground truth available during evaluation, use training image threshold for reporting")
442
465
  optimal_f1_score = torch.tensor(0)
443
- threshold = torch.tensor(float(self.model_data["image_threshold"]))
466
+ if self.custom_normalized_threshold is None:
467
+ log.warning("No ground truth available during evaluation, use training image threshold for reporting")
468
+ threshold = torch.tensor(training_threshold)
469
+ else:
470
+ threshold = torch.tensor((self.custom_normalized_threshold * training_threshold) / 100.0)
444
471
 
445
472
  log.info("Computed F1 score: %s", optimal_f1_score.item())
446
473
  self.metadata["anomaly_scores"] = anomaly_scores
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quadra
3
- Version: 2.7.0
3
+ Version: 2.7.1
4
4
  Summary: Deep Learning experiment orchestration library
5
5
  License: Apache-2.0
6
6
  License-File: LICENSE
@@ -1,4 +1,4 @@
1
- quadra/__init__.py,sha256=6GiBpHwyZiEtMpxFsbhXJlAnmYsht8VrZM8V5h3MH-g,112
1
+ quadra/__init__.py,sha256=77nl9nFDUmAMNnqEFiQyLIKnrlY4eE1eiMYXuA1h8SA,112
2
2
  quadra/callbacks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  quadra/callbacks/anomalib.py,sha256=WLBEGhZA9HoP4Yh9UbbC2GzDOKYTkvU9EY1lkZcV7Fs,11971
4
4
  quadra/callbacks/lightning.py,sha256=qvtzDiv8ZUV7K11gKHKWCyo-a9XR_Jm_M-IEicTM1Yo,20242
@@ -56,7 +56,7 @@ quadra/configs/experiment/base/anomaly/csflow.yaml,sha256=h0fnpYQaN7WPEpHZVb_xpn
56
56
  quadra/configs/experiment/base/anomaly/draem.yaml,sha256=Q6lzy7tO5HJ5o4w5uxpGbCclwBvL9yKlgTr5FXun81M,1102
57
57
  quadra/configs/experiment/base/anomaly/efficient_ad.yaml,sha256=9RQovNcAJ_pJHm8xIuVh2NW3q1ufFEIUqWirj6HC5aY,1092
58
58
  quadra/configs/experiment/base/anomaly/fastflow.yaml,sha256=jN3TeJXPGT7_GOhu2EacpaCIOic-kPxT5I75PgBvoRU,1031
59
- quadra/configs/experiment/base/anomaly/inference.yaml,sha256=aLS3U0yC0Yb17BD1NSl-nhjJ9fcV6jDmxVQrrwZoMYI,446
59
+ quadra/configs/experiment/base/anomaly/inference.yaml,sha256=F-VXF_tIdUp7ZDbTRCH7fBKDmEQsavgj0PTbHE_Z360,567
60
60
  quadra/configs/experiment/base/anomaly/padim.yaml,sha256=5trGY5kL7gKzRTQuWT-LHYVPf4-q9J2I_-196noIZd4,829
61
61
  quadra/configs/experiment/base/anomaly/patchcore.yaml,sha256=a795iOcdH6kSoeA7ufChjBGWYdBn0ztRVtLBk1vpI3Q,841
62
62
  quadra/configs/experiment/base/classification/classification.yaml,sha256=MNWrzDPpTIBc5gfIyeZmEiLqeI-FPjXJt2APD4gY0gU,1290
@@ -248,7 +248,7 @@ quadra/schedulers/__init__.py,sha256=mQivr18c0j36hpV3Lm8nlyBVKFevWp8TtLuTfvI9kQc
248
248
  quadra/schedulers/base.py,sha256=T1EdrLOJ0i9MzWoLCkrNA0uypm7hJ-L6NFhjIXFB6NE,1462
249
249
  quadra/schedulers/warmup.py,sha256=chzzrK7OqqlicBCxiF4CqMYNrWu6nflIbRE-C86Jrw0,4962
250
250
  quadra/tasks/__init__.py,sha256=tmAfMoH0k3UC7r2pNrgbBa1Pfc3tpLl3IObFF6Z0eRE,820
251
- quadra/tasks/anomaly.py,sha256=40ciWnkle45Hl6SjY_4OHWdT2Mb1Qfoog60alZG-uQ0,25899
251
+ quadra/tasks/anomaly.py,sha256=Kv-Lh25E82BsycBPSXwx06fA_yQA3kSoeJfdek47-nM,27680
252
252
  quadra/tasks/base.py,sha256=piYlTFtvqH-4s4oEq4GczdAs_gL29UHAJGsOC5Sd3Bc,14187
253
253
  quadra/tasks/classification.py,sha256=_GQOPMGuOZ_uLA9jFhLEaJkW_Sid_WKHNn9ALXnGNmo,53407
254
254
  quadra/tasks/patch.py,sha256=nzo8o-ei7iF1Iarvd8-c08s0Rs_lPvVPDLAbkFMx-Qw,20251
@@ -293,8 +293,8 @@ quadra/utils/validator.py,sha256=wmVXycB90VNyAbKBUVncFCxK4nsYiOWJIY3ISXwxYCY,463
293
293
  quadra/utils/visualization.py,sha256=yYm7lPziUOlybxigZ2qTycNewb67Q80H4hjQGWUh788,16094
294
294
  quadra/utils/vit_explainability.py,sha256=Gh6BHaDEzWxOjJp1aqvCxLt9Rb8TXd5uKXOAx7-acUk,13351
295
295
  hydra_plugins/quadra_searchpath_plugin.py,sha256=AAn4TzR87zUK7nwSsK-KoqALiPtfQ8FvX3fgZPTGIJ0,1189
296
- quadra-2.7.0.dist-info/METADATA,sha256=wUVgExV2_fkLDND5oEZs_oGHVSh6riCbbZMOUbexYfc,17612
297
- quadra-2.7.0.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
298
- quadra-2.7.0.dist-info/entry_points.txt,sha256=sRYonBZyx-sAJeWcQNQoVQIU5lm02cnCQt6b15k0WHU,43
299
- quadra-2.7.0.dist-info/licenses/LICENSE,sha256=8cTbQtcWa02YJoSpMeV_gxj3jpMTkxvl-w3WJ5gV_QE,11342
300
- quadra-2.7.0.dist-info/RECORD,,
296
+ quadra-2.7.1.dist-info/METADATA,sha256=rq3319yhEWXyGTACbXBr6xbGdLdBuJ1KIn_CfpDQcio,17612
297
+ quadra-2.7.1.dist-info/WHEEL,sha256=kJCRJT_g0adfAJzTx2GUMmS80rTJIVHRCfG0DQgLq3o,88
298
+ quadra-2.7.1.dist-info/entry_points.txt,sha256=sRYonBZyx-sAJeWcQNQoVQIU5lm02cnCQt6b15k0WHU,43
299
+ quadra-2.7.1.dist-info/licenses/LICENSE,sha256=8cTbQtcWa02YJoSpMeV_gxj3jpMTkxvl-w3WJ5gV_QE,11342
300
+ quadra-2.7.1.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.3.0
2
+ Generator: poetry-core 2.3.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any