Spaces:
Sleeping
Sleeping
| """ | |
| Evaluation script for AI Image Detection | |
| """ | |
| import os | |
| import json | |
| import yaml | |
| import numpy as np | |
| from pathlib import Path | |
| import torch | |
| import torch.nn as nn | |
| from torch.utils.data import DataLoader | |
| from sklearn.metrics import ( | |
| confusion_matrix, classification_report, roc_auc_score, roc_curve, | |
| precision_recall_curve, f1_score, accuracy_score | |
| ) | |
| import matplotlib.pyplot as plt | |
| import seaborn as sns | |
| from tqdm import tqdm | |
| from src.dataset import AIImageDataset, get_transforms | |
| from src.model import AIImageClassifier | |
| def load_config(config_path='config.yaml'): | |
| """Load configuration from YAML file""" | |
| with open(config_path, 'r') as f: | |
| config = yaml.safe_load(f) | |
| return config | |
| def evaluate_model(model, test_loader, device, class_names=['Natural', 'Synthetic']): | |
| """ | |
| Evaluate model on test set and compute metrics | |
| Returns: | |
| dict: Dictionary containing all metrics | |
| """ | |
| model.eval() | |
| all_preds = [] | |
| all_labels = [] | |
| all_probs = [] | |
| with torch.no_grad(): | |
| for images, labels in tqdm(test_loader, desc="Evaluating"): | |
| images = images.to(device) | |
| labels = labels.to(device) | |
| outputs = model(images) | |
| probs = torch.softmax(outputs, dim=1) | |
| _, predicted = torch.max(outputs.data, 1) | |
| all_preds.extend(predicted.cpu().numpy()) | |
| all_labels.extend(labels.cpu().numpy()) | |
| all_probs.extend(probs.cpu().numpy()) | |
| all_preds = np.array(all_preds) | |
| all_labels = np.array(all_labels) | |
| all_probs = np.array(all_probs) | |
| # Calculate metrics | |
| metrics = { | |
| 'accuracy': accuracy_score(all_labels, all_preds), | |
| 'f1_score': f1_score(all_labels, all_preds), | |
| 'roc_auc': roc_auc_score(all_labels, all_probs[:, 1]), | |
| } | |
| # Confusion matrix | |
| cm = confusion_matrix(all_labels, all_preds) | |
| metrics['confusion_matrix'] = cm.tolist() | |
| # Classification report | |
| report = classification_report(all_labels, all_preds, target_names=class_names, output_dict=True) | |
| metrics['classification_report'] = report | |
| # ROC curve data | |
| fpr, tpr, _ = roc_curve(all_labels, all_probs[:, 1]) | |
| metrics['roc_curve'] = {'fpr': fpr.tolist(), 'tpr': tpr.tolist()} | |
| # Precision-Recall curve | |
| precision, recall, _ = precision_recall_curve(all_labels, all_probs[:, 1]) | |
| metrics['pr_curve'] = {'precision': precision.tolist(), 'recall': recall.tolist()} | |
| return metrics, all_preds, all_labels, all_probs, cm | |
| def plot_confusion_matrix(cm, class_names, save_path=None): | |
| """Plot and save confusion matrix""" | |
| plt.figure(figsize=(8, 6)) | |
| sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', | |
| xticklabels=class_names, yticklabels=class_names) | |
| plt.title('Confusion Matrix') | |
| plt.ylabel('True Label') | |
| plt.xlabel('Predicted Label') | |
| plt.tight_layout() | |
| if save_path: | |
| plt.savefig(save_path, dpi=300, bbox_inches='tight') | |
| print(f"Confusion matrix saved to {save_path}") | |
| plt.close() | |
| def plot_roc_curve(fpr, tpr, roc_auc, save_path=None): | |
| """Plot and save ROC curve""" | |
| plt.figure(figsize=(8, 6)) | |
| plt.plot(fpr, tpr, color='darkorange', lw=2, label=f'ROC curve (AUC = {roc_auc:.2f})') | |
| plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--') | |
| plt.xlim([0.0, 1.0]) | |
| plt.ylim([0.0, 1.05]) | |
| plt.xlabel('False Positive Rate') | |
| plt.ylabel('True Positive Rate') | |
| plt.title('ROC Curve') | |
| plt.legend(loc="lower right") | |
| plt.tight_layout() | |
| if save_path: | |
| plt.savefig(save_path, dpi=300, bbox_inches='tight') | |
| print(f"ROC curve saved to {save_path}") | |
| plt.close() | |
| def plot_pr_curve(precision, recall, save_path=None): | |
| """Plot and save Precision-Recall curve""" | |
| plt.figure(figsize=(8, 6)) | |
| plt.plot(recall, precision, color='blue', lw=2) | |
| plt.xlabel('Recall') | |
| plt.ylabel('Precision') | |
| plt.title('Precision-Recall Curve') | |
| plt.xlim([0.0, 1.0]) | |
| plt.ylim([0.0, 1.05]) | |
| plt.tight_layout() | |
| if save_path: | |
| plt.savefig(save_path, dpi=300, bbox_inches='tight') | |
| print(f"PR curve saved to {save_path}") | |
| plt.close() | |
| def evaluate(config=None, config_path='config.yaml', model_path=None, results_output_path=None): | |
| """Main evaluation function""" | |
| if config is None: | |
| config = load_config(config_path) | |
| if results_output_path is None: | |
| results_output_path = config['output']['results_path'] | |
| os.makedirs(results_output_path, exist_ok=True) | |
| print("=== AI Image Detection - Evaluation ===") | |
| device = torch.device('cuda' if torch.cuda.is_available() and config['device'] == 'cuda' else 'cpu') | |
| print(f"Device: {device}") | |
| # Load model | |
| model = AIImageClassifier( | |
| model_name=config['model']['name'], | |
| num_classes=config['model']['num_classes'], | |
| pretrained=False, | |
| dropout=config['model']['dropout'] | |
| ) | |
| if model_path is None: | |
| model_path = f"{config['output']['checkpoint_path']}/best_model.pth" | |
| checkpoint = torch.load(model_path, map_location=device) | |
| if isinstance(checkpoint, dict) and 'model_state_dict' in checkpoint: | |
| model.load_state_dict(checkpoint['model_state_dict']) | |
| else: | |
| model.load_state_dict(checkpoint) | |
| model = model.to(device) | |
| print(f"Loaded model from {model_path}") | |
| # Load test data | |
| test_transform = get_transforms( | |
| image_size=config['preprocessing']['image_size'], | |
| mode='val', | |
| normalize_mean=config['preprocessing']['normalize_mean'], | |
| normalize_std=config['preprocessing']['normalize_std'] | |
| ) | |
| test_dataset = AIImageDataset(config['data']['test_path'], transform=test_transform) | |
| test_loader = DataLoader( | |
| test_dataset, | |
| batch_size=config['training']['batch_size'], | |
| shuffle=False, | |
| num_workers=config['num_workers'] | |
| ) | |
| # Evaluate | |
| metrics, preds, labels, probs, cm = evaluate_model(model, test_loader, device) | |
| # Print results | |
| print("\n=== Metrics ===") | |
| print(f"Accuracy: {metrics['accuracy']:.4f}") | |
| print(f"F1-Score: {metrics['f1_score']:.4f}") | |
| print(f"ROC-AUC: {metrics['roc_auc']:.4f}") | |
| print("\n" + classification_report(labels, preds, target_names=['Natural', 'Synthetic'])) | |
| # Save metrics | |
| results_json_path = f"{results_output_path}/metrics.json" | |
| with open(results_json_path, 'w') as f: | |
| json.dump({ | |
| 'accuracy': float(metrics['accuracy']), | |
| 'f1_score': float(metrics['f1_score']), | |
| 'roc_auc': float(metrics['roc_auc']), | |
| 'confusion_matrix': metrics['confusion_matrix'], | |
| 'classification_report': metrics['classification_report'] | |
| }, f, indent=2) | |
| print(f"\nMetrics saved to {results_json_path}") | |
| # Plot results | |
| plot_confusion_matrix(cm, ['Natural', 'Synthetic'], | |
| f"{results_output_path}/confusion_matrix.png") | |
| plot_roc_curve(metrics['roc_curve']['fpr'], metrics['roc_curve']['tpr'], | |
| metrics['roc_auc'], f"{results_output_path}/roc_curve.png") | |
| plot_pr_curve(metrics['pr_curve']['precision'], metrics['pr_curve']['recall'], | |
| f"{results_output_path}/pr_curve.png") | |
| print("\n=== Evaluation Complete ===") | |
| if __name__ == '__main__': | |
| import argparse | |
| parser = argparse.ArgumentParser(description='Evaluate AI Image Detection Model') | |
| parser.add_argument('--config', type=str, default='config.yaml', help='Path to config file') | |
| parser.add_argument('--model_path', type=str, default=None, help='Path to model checkpoint') | |
| parser.add_argument('--results_path', type=str, default=None, help='Path to save results') | |
| args = parser.parse_args() | |
| evaluate(config_path=args.config, model_path=args.model_path, results_output_path=args.results_path) | |