5050from torchmetrics import Metric
5151
5252from anomalib import TaskType
53- from anomalib .data import AnomalibDataModule
53+ from anomalib .data import AnomalibDataModule , ImageBatch
5454from anomalib .deploy .export import CompressionType , ExportType
5555
5656if TYPE_CHECKING :
@@ -179,6 +179,7 @@ def to_onnx(
179179 dynamic_axes = kwargs .pop ("dynamic_axes" , dynamic_axes ),
180180 input_names = kwargs .pop ("input_names" , ["input" ]),
181181 output_names = kwargs .pop ("output_names" , output_names ),
182+ dynamo = kwargs .pop ("dynamo" , False ), # Dynamo is changed to True by default in torch 2.9
182183 ** kwargs ,
183184 )
184185
@@ -193,6 +194,7 @@ def to_openvino(
193194 datamodule : AnomalibDataModule | None = None ,
194195 metric : Metric | None = None ,
195196 task : TaskType | None = None ,
197+ max_drop : float = 0.01 ,
196198 ov_kwargs : dict [str , Any ] | None = None ,
197199 onnx_kwargs : dict [str , Any ] | None = None ,
198200 ) -> Path :
@@ -209,9 +211,14 @@ def to_openvino(
209211 datamodule (AnomalibDataModule | None): DataModule for quantization.
210212 Required for ``INT8_PTQ`` and ``INT8_ACQ``. Defaults to ``None``
211213 metric (Metric | None): Metric for accuracy-aware quantization.
212- Required for ``INT8_ACQ``. Defaults to ``None``
214+ Used for ``INT8_ACQ``. If not provided, a default F1Score at image level
215+ will be used. Defaults to ``None``
213216 task (TaskType | None): Task type (classification/segmentation).
214217 Defaults to ``None``
218+ max_drop (float): Maximum acceptable accuracy drop during quantization.
219+ Only used for ``INT8_ACQ`` compression. Value should be between 0 and 1
220+ (e.g., 0.01 means 1% accuracy drop is acceptable).
221+ Defaults to ``0.01``
215222 ov_kwargs (dict[str, Any] | None): OpenVINO model optimizer arguments.
216223 Defaults to ``None``
217224 onnx_kwargs (dict[str, Any] | None): Additional arguments to pass to torch.onnx.export
@@ -257,7 +264,7 @@ def to_openvino(
257264
258265 model = ov .convert_model (model_path , ** (ov_kwargs or {}))
259266 if compression_type and compression_type != CompressionType .FP16 :
260- model = self ._compress_ov_model (model , compression_type , datamodule , metric , task )
267+ model = self ._compress_ov_model (model , compression_type , datamodule , metric , task , max_drop )
261268
262269 # fp16 compression is enabled by default
263270 compress_to_fp16 = compression_type == CompressionType .FP16
@@ -272,6 +279,7 @@ def _compress_ov_model(
272279 datamodule : AnomalibDataModule | None = None ,
273280 metric : Metric | None = None ,
274281 task : TaskType | None = None ,
282+ max_drop : float = 0.01 ,
275283 ) -> "CompiledModel" :
276284 """Compress OpenVINO model using NNCF.
277285
@@ -285,6 +293,8 @@ def _compress_ov_model(
285293 Required for ``INT8_ACQ``. Defaults to ``None``
286294 task (TaskType | None): Task type (classification/segmentation).
287295 Defaults to ``None``
296+ max_drop (float): Maximum acceptable accuracy drop during quantization.
297+ Only used for ``INT8_ACQ``. Defaults to ``0.01``
288298
289299 Returns:
290300 CompiledModel: Compressed OpenVINO model
@@ -304,7 +314,7 @@ def _compress_ov_model(
304314 elif compression_type == CompressionType .INT8_PTQ :
305315 model = self ._post_training_quantization_ov (model , datamodule )
306316 elif compression_type == CompressionType .INT8_ACQ :
307- model = self ._accuracy_control_quantization_ov (model , datamodule , metric , task )
317+ model = self ._accuracy_control_quantization_ov (model , datamodule , metric , task , max_drop )
308318 else :
309319 msg = f"Unrecognized compression type: { compression_type } "
310320 raise ValueError (msg )
@@ -356,6 +366,7 @@ def _accuracy_control_quantization_ov(
356366 datamodule : AnomalibDataModule | None = None ,
357367 metric : Metric | None = None ,
358368 task : TaskType | None = None ,
369+ max_drop : float = 0.01 ,
359370 ) -> "CompiledModel" :
360371 """Apply accuracy-aware quantization to OpenVINO model.
361372
@@ -366,15 +377,19 @@ def _accuracy_control_quantization_ov(
366377 Defaults to ``None``
367378 metric (Metric | None): Metric to measure accuracy during quantization.
368379 Higher values should indicate better performance.
380+ If not provided, defaults to F1Score at image level.
369381 Defaults to ``None``
370382 task (TaskType | None): Task type (classification/segmentation).
371383 Defaults to ``None``
384+ max_drop (float): Maximum acceptable accuracy drop during quantization.
385+ Value should be between 0 and 1 (e.g., 0.01 means 1% drop is acceptable).
386+ Defaults to ``0.01``
372387
373388 Returns:
374389 CompiledModel: Quantized OpenVINO model
375390
376391 Raises:
377- ValueError: If datamodule or metric is not provided
392+ ValueError: If datamodule is not provided, or if max_drop is out of valid range
378393 """
379394 import nncf
380395
@@ -386,9 +401,25 @@ def _accuracy_control_quantization_ov(
386401 # if task is not provided, use the task from the datamodule
387402 task = task or datamodule .task
388403
389- if metric is None :
390- msg = "Metric must be provided for OpenVINO INT8_ACQ compression"
404+ # Validate max_drop parameter
405+ if not 0 <= max_drop <= 1 :
406+ msg = f"max_drop must be between 0 and 1, got { max_drop } "
391407 raise ValueError (msg )
408+ if max_drop > 0.1 :
409+ logger .warning (
410+ f"max_drop={ max_drop } is a large value (>10%% accuracy drop). "
411+ "Typical values are in the 0.01-0.03 range (1-3%%)." ,
412+ )
413+
414+ # Set default metric if not provided
415+ if metric is None :
416+ from anomalib .metrics import F1Score
417+
418+ metric = F1Score (fields = ["pred_label" , "gt_label" ])
419+ logger .info (
420+ "No metric provided for INT8_ACQ quantization. "
421+ "Using default: F1Score at image level (fields=['pred_label', 'gt_label'])." ,
422+ )
392423
393424 model_input = model .input (0 )
394425
@@ -408,12 +439,28 @@ def _accuracy_control_quantization_ov(
408439 # validation function to evaluate the quality loss after quantization
409440 def val_fn (nncf_model : "CompiledModel" , validation_data : Iterable ) -> float :
410441 for batch in validation_data :
411- preds = torch .from_numpy (nncf_model (batch ["image" ])[0 ])
412- target = batch ["label" ] if task == TaskType .CLASSIFICATION else batch ["mask" ][:, None , :, :]
413- metric .update (preds , target )
442+ ov_model_output = nncf_model (batch ["image" ])
443+ result_batch = ImageBatch (
444+ image = batch ["image" ],
445+ # pred_score must be same size as gt_label for metrics like AUROC
446+ pred_score = torch .from_numpy (ov_model_output ["pred_score" ]).squeeze (),
447+ pred_label = torch .from_numpy (ov_model_output ["pred_label" ]).squeeze (),
448+ gt_label = batch ["gt_label" ],
449+ anomaly_map = torch .from_numpy (ov_model_output ["anomaly_map" ]),
450+ pred_mask = torch .from_numpy (ov_model_output ["pred_mask" ]),
451+ gt_mask = batch ["gt_mask" ][:, None , :, :], # Make shape the same format as pred_mask
452+ )
453+ metric .update (result_batch )
454+
414455 return metric .compute ()
415456
416- return nncf .quantize_with_accuracy_control (model , calibration_dataset , validation_dataset , val_fn )
457+ return nncf .quantize_with_accuracy_control (
458+ model ,
459+ calibration_dataset ,
460+ validation_dataset ,
461+ val_fn ,
462+ max_drop = max_drop ,
463+ )
417464
418465
419466def _create_export_root (export_root : str | Path , export_type : ExportType ) -> Path :
0 commit comments