import os import torch import torch.nn as nn from torchvision import transforms, models import medmnist from medmnist import INFO from torch.utils.data import DataLoader from sklearn.metrics import classification_report, confusion_matrix, ConfusionMatrixDisplay, roc_curve, auc import matplotlib.pyplot as plt import numpy as np def main(): # 1. Hardware Setup (Hardware Agnostic) device = torch.device("cuda" if torch.cuda.is_available() else "cpu") print(f"Evaluating on: {device}") # Streaming from the secondary NVMe dataset_root = r"C:\Users\USER\Downloads\MedMNIST_Data" data_flag = 'pneumoniamnist' info = INFO[data_flag] DataClass = getattr(medmnist, info['python_class']) # 2. Strict Validation Preprocessing val_transform = transforms.Compose([ transforms.Grayscale(num_output_channels=3), transforms.ToTensor(), transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]) ]) # 3. Load the Validation Dataset print("Loading Validation Data...") val_dataset = DataClass(split='val', transform=val_transform, download=False, size=224, root=dataset_root) val_loader = DataLoader(dataset=val_dataset, batch_size=32, shuffle=False, num_workers=0) # 4. Reconstruct and Load the Model print("Rebuilding ResNet50 Architecture...") model = models.resnet50() num_ftrs = model.fc.in_features model.fc = nn.Linear(num_ftrs, 2) #Put Model Path here weights_path = r"C:\Users\Brian ooi\Documents\code\CVPR\CVPRAssignment\baseline_resnet50.pth" model.load_state_dict(torch.load(weights_path, map_location=device, weights_only=True)) model = model.to(device) model.eval() all_predictions = [] all_true_labels = [] all_probabilities = [] # Raw probabilities for the ROC curve print("Running Inference...") with torch.no_grad(): for images, labels in val_loader: images = images.to(device) labels = labels.to(device).squeeze().long() outputs = model(images) # Apply softmax to get percentages (0.0 to 1.0) instead of raw logits probabilities = torch.softmax(outputs, dim=1) _, predicted = torch.max(outputs.data, 1) all_predictions.extend(predicted.cpu().numpy()) all_true_labels.extend(labels.cpu().numpy()) # Save the probability specifically for the "Pneumonia (1)" class all_probabilities.extend(probabilities[:, 1].cpu().numpy()) # 5. The Clinical Metrics (Sensitivity & Specificity) cm = confusion_matrix(all_true_labels, all_predictions) tn, fp, fn, tp = cm.ravel() sensitivity = tp / (tp + fn) specificity = tn / (tn + fp) print("\n" + "="*50) print("CLINICAL PERFORMANCE METRICS") print("="*50) print(f"Sensitivity (Recall for Pneumonia): {sensitivity:.4f}") print(f"Specificity (Recall for Normal): {specificity:.4f}") print("="*50) # 6. Generate the ROC Curve print("Generating ROC Curve Window...") fpr, tpr, thresholds = roc_curve(all_true_labels, all_probabilities) roc_auc = auc(fpr, tpr) fig, ax = plt.subplots(figsize=(8, 6)) ax.plot(fpr, tpr, color='darkorange', lw=2, label=f'ROC curve (AUC = {roc_auc:.4f})') ax.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--', label='Random Guessing') ax.set_xlim([0.0, 1.0]) ax.set_ylim([0.0, 1.05]) ax.set_xlabel('False Positive Rate (1 - Specificity)', fontweight='bold') ax.set_ylabel('True Positive Rate (Sensitivity)', fontweight='bold') ax.set_title('Receiver Operating Characteristic (ROC) - Baseline ResNet50 (No Augmentation)', fontweight='bold') ax.legend(loc="lower right") # Save the ROC curve to your NVMe roc_path = os.path.join(dataset_root, 'roc_curve.png') plt.savefig(roc_path, dpi=300) # 7. Generate and Save the Confusion Matrix Grid print("Generating Confusion Matrix Window...") disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=["Normal (0)", "Pneumonia (1)"]) disp.plot(cmap=plt.cm.Blues) plt.title('Baseline ResNet50 (No Augmentation) - PneumoniaMNIST', fontweight='bold') plt.savefig(os.path.join(dataset_root, 'baseline_confusion_matrix.png'), dpi=300) plt.show() if __name__ == '__main__': main()