SpiceNet / code /evaluate.py
Noushad999's picture
Upload folder using huggingface_hub
1ea7ba6 verified
Raw
History Blame Contribute Delete
5.17 kB
"""
Full evaluation: Top-1/5 accuracy, F1, confusion matrix, inference time, Grad-CAM.
Usage:
python evaluate.py # evaluate best.pth (Phase 3 fusion)
python evaluate.py --ckpt outputs/checkpoints/p1_best.pth --mode image
python evaluate.py --gradcam # also generate Grad-CAM plots
"""
import sys, os
_base = "/mnt/d/SpiceNet" if os.path.exists("/mnt/d/SpiceNet") else "D:/SpiceNet"
sys.path.insert(0, _base)
import argparse
import torch
import config
from src.dataset import get_dataloaders, build_splits
from src.model import load_checkpoint
from src.utils import (
set_seed, compute_and_print_metrics, plot_confusion_matrix,
save_metrics, measure_inference_time, topk_accuracy,
)
from src.gradcam import visualize_gradcam
@torch.no_grad()
def predict(model, loader, device, mode="fusion"):
model.eval()
all_preds, all_labels = [], []
all_logits = []
for imgs, tex, col, labels in loader:
imgs, labels = imgs.to(device), labels.to(device)
if mode == "fusion":
tex, col = tex.to(device), col.to(device)
logits, _ = model.forward_fusion(imgs, tex, col)
else:
logits = model.forward_image(imgs)
all_logits.append(logits.cpu())
all_preds.extend(logits.argmax(1).cpu().tolist())
all_labels.extend(labels.cpu().tolist())
return all_labels, all_preds, torch.cat(all_logits, dim=0)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--ckpt", default=str(config.CHECKPOINT_DIR / "best.pth"))
parser.add_argument("--mode", default="fusion", choices=["fusion", "image"])
parser.add_argument("--gradcam", action="store_true")
parser.add_argument("--data_dir", type=str, default=None)
parser.add_argument("--cam_algo", default="hirescam",
choices=["hirescam", "gradcam++"],
help="Attribution algorithm for --gradcam")
args = parser.parse_args()
set_seed(config.RANDOM_SEED)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
from pathlib import Path
data_dir = Path(args.data_dir) if args.data_dir else config.DATA_DIR
multimodal = args.mode == "fusion"
print(f"Checkpoint : {args.ckpt}")
print(f"Mode : {args.mode}")
model, epoch, best_val_acc, _ = load_checkpoint(args.ckpt, device)
print(f"Trained for {epoch} epochs | best val acc: {best_val_acc:.4f}")
_, _, test_loader, x_te, y_te = get_dataloaders(
multimodal=multimodal, data_dir=data_dir)
# Inference time
infer_ms = measure_inference_time(model, test_loader, device)
# Predictions
print(f"\nInference on {len(test_loader.dataset)} test samples...")
y_true, y_pred, logits_all = predict(model, test_loader, device, mode=args.mode)
# Top-5
y_true_t = torch.tensor(y_true)
top5 = topk_accuracy(logits_all, y_true_t, k=min(5, config.NUM_CLASSES))
output_dir = config.OUTPUT_DIR
output_dir.mkdir(parents=True, exist_ok=True)
prefix = f"{args.mode}"
metrics = compute_and_print_metrics(
y_true, y_pred, config.CLASSES,
top5_acc=top5,
infer_ms=infer_ms,
)
save_metrics(metrics, output_dir / f"{prefix}_test_metrics.json")
plot_confusion_matrix(y_true, y_pred, config.CLASSES, output_dir, prefix=prefix)
# Grad-CAM
if args.gradcam:
import torch.nn.functional as F
probs = F.softmax(logits_all, dim=1)
max_p, _ = probs.max(dim=1)
# High-confidence correct samples — one per class when possible
per_class: dict[int, list] = {}
for path, true_lbl, pred, p in zip(x_te, y_te, y_pred, max_p.tolist()):
if pred != true_lbl or p < 0.90:
continue
per_class.setdefault(true_lbl, []).append((p, path, true_lbl))
# Pick top-1 confidence per class
chosen = []
for cls_idx in sorted(per_class.keys()):
entries = sorted(per_class[cls_idx], key=lambda t: t[0], reverse=True)
chosen.append((entries[0][1], entries[0][2]))
# Fill remaining slots with next-highest-confidence correct samples
if len(chosen) < 16:
extras = []
for cls_idx, entries in per_class.items():
for p, path, lbl in sorted(entries, key=lambda t: t[0], reverse=True)[1:]:
extras.append((p, path, lbl))
extras.sort(key=lambda t: t[0], reverse=True)
for _, path, lbl in extras:
if len(chosen) >= 16:
break
chosen.append((path, lbl))
paths, labels = zip(*chosen) if chosen else ([], [])
visualize_gradcam(
model, list(paths), list(labels), config.CLASSES,
device, output_dir / f"{prefix}_gradcam.png",
mode=args.mode,
algorithm=args.cam_algo,
)
print(f"\nAll outputs saved to {output_dir}/")
if __name__ == "__main__":
main()