UAIDE / video_bundle /diagnose_misclassification.py
ATS-27's picture
Upload folder using huggingface_hub
af980d7 verified
Raw
History Blame Contribute Delete
7.83 kB
"""
Diagnose misclassification issue by analyzing predicted probabilities,
finding optimal threshold, and showing false positive examples.
"""
import joblib
import numpy as np
import torch
from pathlib import Path
from sklearn.metrics import accuracy_score, roc_auc_score, classification_report, confusion_matrix, roc_curve
from torch.utils.data import DataLoader
from torchvision import transforms
import torchvision.transforms.functional as TF
import matplotlib.pyplot as plt
from train import DeepfakeFeatureFusion, ImageDataset
def pad_to_min_size(img, size):
w, h = img.size
pad_w = max(0, size - w)
pad_h = max(0, size - h)
if pad_w or pad_h:
left = pad_w // 2
right = pad_w - left
top = pad_h // 2
bottom = pad_h - top
img = TF.pad(img, [left, top, right, bottom], padding_mode='reflect')
return img
MODEL_INFO_PATH = Path('model_fusion_best.joblib_info.pkl')
DATASET = Path('DeepfakeVsReal/Dataset')
if not MODEL_INFO_PATH.exists():
raise FileNotFoundError(f'{MODEL_INFO_PATH} not found')
model_info = joblib.load(str(MODEL_INFO_PATH))
state_path = model_info.get('state_dict_path')
if state_path is None or not Path(state_path).exists():
raise FileNotFoundError(f'State dict not found: {state_path}')
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
clf = DeepfakeFeatureFusion()
clf.load_state_dict(torch.load(state_path, map_location='cpu'))
clf.to(device)
clf.eval()
transform = transforms.Compose([
transforms.Lambda(lambda img: pad_to_min_size(img, 224)),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])
print('=' * 80)
print('DEEPFAKE DETECTION DIAGNOSTIC')
print('=' * 80)
print(f'\n✓ Loaded fusion_improved model from {state_path}')
# Validation set
val_root = DATASET / 'Validation'
print(f'\nEvaluating on Validation set...')
real_val = val_root / 'Real'
fake_val = val_root / 'Fake'
real_files = sorted([str(x) for x in real_val.rglob('*.jpg')] + [str(x) for x in real_val.rglob('*.png')])
fake_files = sorted([str(x) for x in fake_val.rglob('*.jpg')] + [str(x) for x in fake_val.rglob('*.png')])
files = real_files + fake_files
labels = [0] * len(real_files) + [1] * len(fake_files)
if len(files) > 0:
dataset = ImageDataset(files, labels, transform=transform)
dataloader = DataLoader(dataset, batch_size=8, shuffle=False, num_workers=0)
all_probs, all_labels = [], []
with torch.no_grad():
for inputs, lbls in dataloader:
inputs = inputs.to(device)
outputs = clf(inputs)
probs = torch.softmax(outputs, dim=1)[:, 1].cpu().numpy()
all_probs.extend(probs.tolist())
all_labels.extend(lbls.tolist())
yv = np.array(all_labels)
probs_v = np.array(all_probs)
print(f' Total samples: {len(yv)}')
print(f' Real images: {sum(yv == 0)}')
print(f' Fake images: {sum(yv == 1)}')
# Current threshold (0.5)
preds_05 = (probs_v >= 0.5).astype(int)
acc_05 = accuracy_score(yv, preds_05)
cm_05 = confusion_matrix(yv, preds_05)
tn, fp, fn, tp = cm_05.ravel()
print(f'\n--- Current Threshold: 0.5 ---')
print(f' Accuracy: {acc_05:.4f}')
print(f' True Negatives (Real correctly as Real): {tn}')
print(f' False Positives (Real wrongly as Fake): {fp} ← PROBLEM')
print(f' False Negatives (Fake wrongly as Real): {fn}')
print(f' True Positives (Fake correctly as Fake): {tp}')
# Try to compute ROC AUC
try:
roc_auc = roc_auc_score(yv, probs_v)
print(f' ROC AUC: {roc_auc:.4f}')
except Exception as e:
print(f' ROC AUC: Error - {e}')
# Find best threshold to minimize false positives for real images
# (maximize specificity while maintaining reasonable sensitivity)
print(f'\n--- Threshold Sweep (Finding Optimal) ---')
thresholds = np.arange(0.1, 1.0, 0.05)
best_threshold = 0.5
best_fp_rate = 1.0
best_metrics = {}
for thresh in thresholds:
preds = (probs_v >= thresh).astype(int)
cm = confusion_matrix(yv, preds)
tn, fp, fn, tp = cm.ravel()
# False positive rate for real images (we want this LOW)
fp_rate = fp / (tn + fp) if (tn + fp) > 0 else 0
acc = accuracy_score(yv, preds)
status = "✓" if fp_rate < best_fp_rate else " "
print(f' {status} Threshold {thresh:.2f}: Acc={acc:.4f}, FP_Rate={fp_rate:.4f} (FP={fp}, TP={tp})')
if fp_rate < best_fp_rate:
best_fp_rate = fp_rate
best_threshold = thresh
best_metrics = {'tn': tn, 'fp': fp, 'fn': fn, 'tp': tp, 'fp_rate': fp_rate, 'acc': acc}
print(f'\n--- RECOMMENDATION ---')
print(f'Best threshold: {best_threshold:.2f}')
print(f' Expected metrics:')
print(f' False Positive Rate: {best_metrics["fp_rate"]:.4f}')
print(f' Accuracy: {best_metrics["acc"]:.4f}')
print(f' Real→Fake (FP): {best_metrics["fp"]}')
print(f' Fake→Real (FN): {best_metrics["fn"]}')
# Show detailed classification report at best threshold
preds_best = (probs_v >= best_threshold).astype(int)
print(f'\n--- Classification Report (Threshold {best_threshold:.2f}) ---')
print(classification_report(yv, preds_best, target_names=['Real', 'Fake']))
# Test set
test_root = DATASET / 'Test'
if test_root.exists():
print(f'\n{"=" * 80}')
print(f'Evaluating on Test set...')
real_test = test_root / 'Real'
fake_test = test_root / 'Fake'
real_t = sorted([str(x) for x in real_test.rglob('*.jpg')] + [str(x) for x in real_test.rglob('*.png')])
fake_t = sorted([str(x) for x in fake_test.rglob('*.jpg')] + [str(x) for x in fake_test.rglob('*.png')])
files_t = real_t + fake_t
labels_t = [0] * len(real_t) + [1] * len(fake_t)
if len(files_t) > 0:
ds_t = ImageDataset(files_t, labels_t, transform=transform)
dl_t = DataLoader(ds_t, batch_size=8, shuffle=False, num_workers=0)
all_probs_t, all_labels_t = [], []
with torch.no_grad():
for inputs, lbls in dl_t:
inputs = inputs.to(device)
outputs = clf(inputs)
probs = torch.softmax(outputs, dim=1)[:, 1].cpu().numpy()
all_probs_t.extend(probs.tolist())
all_labels_t.extend(lbls.tolist())
yt = np.array(all_labels_t)
probs_t = np.array(all_probs_t)
print(f' Total samples: {len(yt)}')
print(f' Real images: {sum(yt == 0)}')
print(f' Fake images: {sum(yt == 1)}')
# Test with best threshold
preds_best = (probs_t >= best_threshold).astype(int)
acc_best = accuracy_score(yt, preds_best)
cm_best = confusion_matrix(yt, preds_best)
tn, fp, fn, tp = cm_best.ravel()
print(f'\nTest Results (Threshold {best_threshold:.2f}):')
print(f' Accuracy: {acc_best:.4f}')
print(f' Real→Fake (False Positive): {fp}')
print(f' Fake→Real (False Negative): {fn}')
print(f'\n{classification_report(yt, preds_best, target_names=["Real", "Fake"])}')
print(f'\n{"=" * 80}')
print('NEXT STEPS:')
print('1. Update threshold in your prediction scripts from 0.5 to recommended value')
print('2. If false positives still too high, consider:')
print(' - Retraining with adjusted class weights')
print(' - Adding more diverse real image samples')
print(' - Checking for preprocessing mismatches')
print('=' * 80)