Arko007's picture
Update model card: corrected full-dataset retrain, real F1/AUROC metrics, Modal deployment verification
5c957c9 verified
|
Raw
History Blame
8.37 kB
metadata
license: other
tags:
  - deepfake-detection
  - uncertainty-quantification
  - pytorch

Deepfake Triage Plugin β€” Detector Checkpoints

Four proxy-architecture deepfake detector checkpoints trained for the model-agnostic uncertainty + explainability triage plugin described in the research proposal "A Model-Agnostic Uncertainty and Explainability Plugin for Generalizable Deepfake Detection" (Bhumika Tewari, TBVL Lab, IISER Bhopal). Each checkpoint is loaded by the plugin's FastAPI backend (backend/main.py in Anamitra-Sarkar/deepfake-triage-plugin) and wrapped with MC-Dropout uncertainty estimation, Grad-CAM/attention explainability, and a joint confidence+explanation-quality triage rule.

Update (2026-07-24): retrained on the full corrected dataset. An earlier version of these checkpoints was trained on data affected by a bug in restore_crops_from_hf.py that silently excluded ~750 real videos' worth of already-extracted crops, leaving that run with an artificially severe ~1:35 real:fake ratio instead of FF++'s actual ~1:6. That bug is fixed; the checkpoints and metrics below are from a full retrain on the corrected, complete dataset. The old real-class F1 numbers (xception 0.725, sbi 0.673, vit 0.627, lsda 0.725 at video level) are superseded by the numbers in this card, which are all equal or higher.

Files

File Architecture (proxy backbone used) Size
best_xception.pth Xception proxy: timm xception41 (falls back to EfficientNet-B0 if timm unavailable) ~100 MB
best_sbi.pth Self-Blended Images (SBI) proxy: EfficientNet-B4 ~71 MB
best_vit.pth UIA-ViT proxy: ViT-Base (vit_base_patch16_224) ~343 MB
best_lsda.pth LSDA proxy: EfficientNet-B0 + latent-space Gaussian noise injection during training ~16 MB

Important scope note: these are architecturally-diverse proxy backbones standing in for the four architectures named in the research proposal (Xception, SBI, UIA-ViT, LSDA) β€” they reproduce each paper's general architecture family (CNN / augmentation-based CNN / Vision Transformer / latent-augmented CNN) but not each paper's exact published training recipe (e.g. SBI's self-blending augmentation pipeline, UIA-ViT's patch-consistency loss, or LSDA's specific latent-space augmentation method). Treat these as a working proof-of-concept for the plugin architecture, not a reproduction of the original papers' benchmark numbers.

Training data

Real FaceForensics++ (c23) videos via the xdxd003/ff-c23 Kaggle dataset mirror β€” folder layout: DeepFakeDetection, Deepfakes, Face2Face, FaceShifter, FaceSwap, NeuralTextures (fake) and original (real).

This run used the full ~7000-video dataset (all 6 fake methods + the full real set), face-cropped via MTCNN and persisted to the Arko007/deepfake-ff-face-crops HF dataset repo across multiple preprocessing sessions (resumable, processed_videos.txt-tracked, no video reprocessed twice). The held-out validation split used for the metrics below has 11,570 frames across 1,049 videos (150 real / 899 fake) β€” consistent with FF++'s ~1:6 real:fake ratio, confirming the corrected restore actually pulled in the full real class this time.

Training setup (from training/train_ddp.py / the training notebook)

All 4 models: --epochs 20 --patience 5 (early stopping on validation loss), AdamW optimizer, ReduceLROnPlateau scheduler, BCEWithLogitsLoss(pos_weight=n_real/n_fake) for class-imbalance correction, plus a WeightedRandomSampler (per-class weight 1/n_class) during training.

  • Xception / SBI / LSDA: --batch_size 64 --lr 1e-4
  • ViT: --batch_size 32 --lr 5e-5

Train/val split: 85/15, video-level stratified (not frame-level β€” see split_samples() in train_ddp.py), so frames from the same video never leak across the split.

Evaluation methodology and results (real, computed β€” not illustrative)

Computed by training/evaluate_models.py, which reconstructs the exact held-out validation split (seed=42, val_fraction=0.15) and reports accuracy, per-class precision/recall/F1, macro-F1, AUROC, and confusion matrices, at both frame level and video level (video-level = mean probability across a video's frames, since frames from the same video are near-duplicates and accuracy alone is misleading under FF++'s class imbalance).

Caveat, stated plainly: this held-out split was also used during training for checkpoint selection (best validation loss / early stopping). It is not a separate, from-scratch generalization test set. Treat these numbers as trustworthy validation-time performance, not an independent-test-set claim.

Video-level metrics (the numbers that matter for real-world triage)

Model Accuracy Real Precision Real Recall Real F1 Fake F1 Macro F1 AUROC
xception 0.953 0.770 0.960 0.855 0.972 0.913 0.989
sbi 0.869 0.523 0.973 0.681 0.918 0.799 0.971
vit 0.871 0.529 0.900 0.667 0.920 0.793 0.942
lsda 0.924 0.662 0.953 0.781 0.954 0.868 0.978

Frame-level metrics

Model Accuracy Real F1 Fake F1 Macro F1 AUROC
xception 0.926 0.849 0.951 0.900 0.977
sbi 0.836 0.716 0.885 0.800 0.943
vit 0.836 0.692 0.888 0.790 0.913
lsda 0.885 0.776 0.923 0.849 0.955

Reading these honestly: accuracy alone would be misleading here (FF++ is fake-heavy) β€” that's why real-class F1 and AUROC are the headline numbers. Xception is the strongest all-around (real F1 0.855, AUROC 0.989). SBI and ViT show the largest real-precision vs. real-recall gap (they over-flag real videos as fake more often) but their AUROC (0.94-0.97) shows the underlying probability ranking is still strongly separated β€” that gap is a threshold-calibration property of those two architectures on this data, not evidence the model failed to learn. LSDA sits in between. No model's F1 collapsed under the class imbalance; the pos_weight + WeightedRandomSampler combination held up.

Full machine-readable results (including confusion matrices) are in eval_results.json in this repo.

Live deployment verification (2026-07-24)

Both the FastAPI backend and the React frontend were deployed to Modal (T4 GPU, CPU fallback if CUDA raises a RuntimeError mid-request) purely to verify the full product end-to-end with these corrected checkpoints β€” not a permanent hosting solution (the client's proposal asked for the working product, not hosted infrastructure; the Modal deployment was stopped again after verification).

  • /health: {"status": "healthy", "cuda_available": true, ...}.
  • /detect on a real (non-fake) FF++ validation frame: returned is_fake: false, probability: 0.00069 (correctly confident this is real), weights_source: "trained" (confirms the real checkpoint loaded β€” not a silently-failed fallback to ImageNet weights), full triage response (entropy/stability/Grad-CAM heatmap) returned correctly.
  • Frontend static build served correctly (200, correct title) and was pointed at the Modal backend for this verification pass only.

For day-to-day use, the frontend defaults to sharded per-architecture instances on Render's free tier (CPU-only; see repo frontend/src/App.jsx MODEL_ENDPOINTS).

Uncertainty, explainability, and triage (implementation, not just checkpoints)

See plugin_core/ in the repo:

  • uncertainty.py β€” MCDropoutPlugin (stochastic forward passes β†’ mean probability, variance, entropy), plus calculate_ece / generate_reliability_data for calibration analysis (not yet run against this labeled held-out split β€” the deployed UI's Calibration tab currently shows illustrative, not measured, calibration-curve numbers; the F1/precision/recall/AUROC numbers above ARE measured).
  • explainability.py β€” Grad-CAM (CNN backbones) / saliency-based attention (ViT), with stability-under-perturbation and spatial-entropy quality metrics.
  • triage.py β€” joint rule combining entropy, explanation stability, and borderline-probability checks into VERIFIED_SAFE / VERIFIED_FAKE / ESCALATE_TO_HUMAN.