"""Generate GradCAM heatmaps from the best checkpoint.""" import torch import torch.nn as nn import matplotlib.pyplot as plt import numpy as np from torchvision import models, transforms from PIL import Image import io from datasets import load_dataset from pytorch_grad_cam import GradCAM from pytorch_grad_cam.utils.image import show_cam_on_image import random import os CHECKPOINT = "checkpoints/resnet18_best.pt" # 1. Load checkpoint ckpt = torch.load(CHECKPOINT, map_location="cpu") class_names = ckpt["class_names"] print(f"Loaded checkpoint with test_acc={ckpt.get('test_acc', 'unknown')}") print(f"Classes: {class_names}") # 2. Load model model = models.resnet18(weights=None) model.fc = nn.Linear(model.fc.in_features, len(class_names)) model.load_state_dict(ckpt["model_state_dict"]) model.eval() # 3. Set up GradCAM on the last conv layer target_layers = [model.layer4[-1]] cam = GradCAM(model=model, target_layers=target_layers) # 4. Load dataset dataset = load_dataset("Jeneral/fer-2013") test = dataset["test"] # 5. Same transforms as training transform = transforms.Compose([ transforms.Grayscale(num_output_channels=3), transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229,0.224, 0.225]), ]) def decode_img(sample): data = sample["img_bytes"] return Image.open(io.BytesIO(data)) if isinstance(data, bytes) else data # 6. Find one good sample per emotion class samples_per_class = {} random.seed(42) indices = random.sample(range(len(test)), len(test)) for idx in indices: sample = test[idx] label = sample["labels"] if label not in samples_per_class: samples_per_class[label] = sample if len(samples_per_class) == len(class_names): break # 7. Generate GradCAM for each os.makedirs("gradcam_samples", exist_ok=True) fig, axes = plt.subplots(2, 7, figsize=(20, 6)) for i, (class_idx, sample) in enumerate(sorted(samples_per_class.items())): img = decode_img(sample).convert("RGB").resize((224, 224)) input_tensor = transform(img).unsqueeze(0) grayscale_cam = cam(input_tensor=input_tensor, targets=None)[0] rgb_img = np.array(img) / 255.0 cam_image = show_cam_on_image(rgb_img, grayscale_cam,use_rgb=True) axes[0, i].imshow(img) axes[0, i].set_title(f"Original: {class_names[class_idx]}") axes[0, i].axis("off") axes[1, i].imshow(cam_image) axes[1, i].set_title("GradCAM") axes[1, i].axis("off") plt.tight_layout() plt.savefig("gradcam_samples/all_classes.png", dpi=100) plt.close() print("✅ Saved gradcam_samples/all_classes.png")