Download scripts/evaluate.py from erfanasgari21/omnial: direct link, hf CLI and curl.
- Browser
- Download file 4.65 kB
-
https://huggingface.co/erfanasgari21/omnial/resolve/main/scripts/evaluate.py
- Command line
-
hf download hf://erfanasgari21/omnial/scripts/evaluate.py
-
curl -L -o evaluate.py https://huggingface.co/erfanasgari21/omnial/resolve/main/scripts/evaluate.py
4.65 kB
| """ | |
| evaluate.py | |
| ============ | |
| توابع ماژولار ارزیابی روی مجموعه تست (Image-AUROC, Pixel-AUROC, Pixel-AP). | |
| """ | |
| import os | |
| import sys | |
| import numpy as np | |
| import torch | |
| import torch.nn.functional as F | |
| from tqdm.auto import tqdm | |
| sys.path.append(os.path.join(os.path.dirname(__file__), "..")) | |
| from data.data_loader import list_test_images, load_and_resize, load_mask | |
| from utils.metrics import anomaly_score_from_seg, compute_image_auroc, compute_pixel_auroc, compute_pixel_ap | |
| def evaluate_category(model, root: str, category: str, size, device: str, dataset_name: str = "mvtec", | |
| smooth_kernel: int = 7, smooth_sigma: float = 1.5): | |
| """ | |
| Run model on one class test set. Returns image/pixel scores and labels. | |
| """ | |
| model.eval() | |
| test_items = list_test_images(root, category, dataset_name) | |
| image_scores, image_labels = [], [] | |
| pixel_scores, pixel_labels = [], [] | |
| for img_path, defect_type, mask_path in test_items: | |
| img = load_and_resize(img_path, size) | |
| img_t = torch.from_numpy(img.transpose(2, 0, 1)).float().unsqueeze(0).to(device) | |
| # Eval in FP32 — AMP leftovers in weights can still NaN; sanitize scores below | |
| with torch.amp.autocast("cuda", enabled=False): | |
| out = model(img_t) | |
| seg_map = anomaly_score_from_seg( | |
| out["seg_logits"].float(), smooth_kernel=smooth_kernel, smooth_sigma=smooth_sigma | |
| )[0].cpu().numpy() # (H,W) | |
| seg_map = np.nan_to_num(seg_map, nan=0.0, posinf=1.0, neginf=0.0) | |
| label = 0 if defect_type == "good" else 1 | |
| image_scores.append(float(np.max(seg_map))) | |
| image_labels.append(label) | |
| if mask_path is not None: | |
| gt = load_mask(mask_path, size) | |
| else: | |
| gt = np.zeros(size, dtype=np.float32) | |
| pixel_scores.append(seg_map) | |
| pixel_labels.append(gt) | |
| return { | |
| "image_scores": np.asarray(image_scores, dtype=np.float64), | |
| "image_labels": np.asarray(image_labels), | |
| "pixel_scores": np.stack(pixel_scores, axis=0), | |
| "pixel_labels": np.stack(pixel_labels, axis=0), | |
| } | |
| def evaluate_dataset(model, cfg: dict, dataset_name: str = "mvtec", device: str = "cuda", | |
| verbose: bool = True): | |
| """ | |
| Full eval over all classes. Returns dict with per-category metrics + "average". | |
| Set verbose=False to silence per-category prints (training loop uses this). | |
| """ | |
| if dataset_name == "mvtec": | |
| root = cfg["data"]["mvtec_root"] | |
| categories = cfg["data"]["mvtec_categories"] | |
| size = tuple(cfg["data"]["mvtec_size"]) | |
| else: | |
| root = cfg["data"]["visa_root"] | |
| categories = cfg["data"]["visa_categories"] | |
| size = tuple(cfg["data"]["visa_size"]) | |
| smooth_k = cfg["model"].get("anomaly_smooth_kernel", 7) | |
| smooth_s = cfg["model"].get("anomaly_smooth_sigma", 1.5) | |
| results = {} | |
| cat_iter = categories if verbose else tqdm(categories, desc="eval", leave=False, dynamic_ncols=True) | |
| for cat in cat_iter: | |
| try: | |
| res = evaluate_category( | |
| model, root, cat, size, device, dataset_name, | |
| smooth_kernel=smooth_k, smooth_sigma=smooth_s, | |
| ) | |
| except Exception as e: | |
| print(f"[WARN] {cat}: eval failed ({e}); skipping.") | |
| continue | |
| if len(np.unique(res["image_labels"])) < 2: | |
| if verbose: | |
| print(f"[WARN] {cat}: need both normal/anomaly labels for AUROC.") | |
| continue | |
| i_auroc = compute_image_auroc(res["image_scores"], res["image_labels"]) | |
| p_auroc = compute_pixel_auroc(res["pixel_scores"], res["pixel_labels"]) | |
| p_ap = compute_pixel_ap(res["pixel_scores"], res["pixel_labels"]) | |
| if not np.isfinite(i_auroc) or not np.isfinite(p_auroc): | |
| print(f"[WARN] {cat}: non-finite metrics (I={i_auroc}, P={p_auroc}); skipping.") | |
| continue | |
| results[cat] = {"I-AUROC": i_auroc, "P-AUROC": p_auroc, "P-AP": p_ap} | |
| if verbose: | |
| print(f"{cat:15s} | I-AUROC={i_auroc:.3f} P-AUROC={p_auroc:.3f} P-AP={p_ap:.3f}") | |
| if results: | |
| avg = {k: float(np.mean([v[k] for v in results.values()])) for k in ["I-AUROC", "P-AUROC", "P-AP"]} | |
| results["average"] = avg | |
| if verbose: | |
| print(f"{'AVERAGE':15s} | I-AUROC={avg['I-AUROC']:.3f} P-AUROC={avg['P-AUROC']:.3f} P-AP={avg['P-AP']:.3f}") | |
| else: | |
| results["average"] = {"I-AUROC": 0.0, "P-AUROC": 0.0, "P-AP": 0.0} | |
| print("[WARN] No valid categories in eval — returning zero averages.") | |
| return results | |