omyfish / src /evaluate.py
fenghebonjour's picture
Upload folder using huggingface_hub
46bcc01 verified
Raw
History Blame Contribute Delete
2.92 kB
import argparse
from pathlib import Path
from typing import Optional
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
import torch
import yaml
from sklearn.metrics import classification_report, confusion_matrix
from tqdm import tqdm
def evaluate(config_path: str = "configs/config.yaml", checkpoint: str = "checkpoints/best.pt"):
with open(config_path) as f:
config = yaml.safe_load(f)
device = "cuda" if torch.cuda.is_available() else "cpu"
from src.dataset import build_dataloaders
from src.model import build_model
_, val_loader, classes = build_dataloaders(config)
config["model"]["num_classes"] = len(classes)
model = build_model(config).to(device)
ckpt = torch.load(checkpoint, map_location=device, weights_only=False)
model.load_state_dict(ckpt["model_state_dict"])
model.eval()
all_preds, all_labels = [], []
with torch.no_grad():
for images, labels in tqdm(val_loader, desc="Evaluating"):
preds = model(images.to(device)).argmax(1).cpu().numpy()
all_preds.extend(preds)
all_labels.extend(labels.numpy())
all_preds, all_labels = np.array(all_preds), np.array(all_labels)
print(classification_report(all_labels, all_preds, target_names=classes, zero_division=0))
out_dir = Path(config["paths"]["outputs"])
out_dir.mkdir(parents=True, exist_ok=True)
n = len(classes)
cm = confusion_matrix(all_labels, all_preds)
fig, ax = plt.subplots(figsize=(max(10, n // 2), max(8, n // 2)))
sns.heatmap(cm, annot=n <= 30, fmt="d", xticklabels=classes, yticklabels=classes, ax=ax, cmap="Blues")
ax.set_xlabel("Predicted")
ax.set_ylabel("True")
ax.set_title("Confusion Matrix")
plt.tight_layout()
cm_path = out_dir / "confusion_matrix.png"
plt.savefig(cm_path, dpi=150)
print(f"Confusion matrix → {cm_path}")
plt.close()
def gradcam_heatmap(
model: torch.nn.Module,
image_tensor: torch.Tensor,
target_class: Optional[int] = None,
) -> np.ndarray:
"""
Grad-CAM heatmap for a single image tensor (1, C, H, W).
Works for EfficientNet/ResNet backbones; ViT needs a different target layer.
"""
from pytorch_grad_cam import GradCAM
from pytorch_grad_cam.utils.model_targets import ClassifierOutputTarget
children = list(model.backbone.children())
target_layers = [children[-1]]
targets = [ClassifierOutputTarget(target_class)] if target_class is not None else None
with GradCAM(model=model, target_layers=target_layers) as cam:
return cam(input_tensor=image_tensor, targets=targets)[0]
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--config", default="configs/config.yaml")
parser.add_argument("--checkpoint", default="checkpoints/best.pt")
args = parser.parse_args()
evaluate(args.config, args.checkpoint)