proofyx / utils /explainability.py
abrar6024's picture
Merge upstream/main: reconcile async-DB/FastAPI/React fork with ML methodology branch
31e6e38
Raw
History Blame Contribute Delete
2.85 kB
"""
Explainability utilities for deepfake detection.
Provides structured risk explanations with evidence summaries
for image, video, audio, and multimodal analysis.
"""
def explain_risk(score, model_scores=None):
"""
Generate structured risk explanation for image/video analysis.
Args:
score: Final risk score (0.0 - 1.0).
model_scores: Optional dict of per-model scores for evidence.
Returns:
str with detailed risk explanation.
"""
if score >= 0.60:
level = "AI-GENERATED"
desc = "Strong indicators of AI generation or manipulation"
else:
level = "AUTHENTIC"
desc = "No significant manipulation indicators"
explanation = f"{level}{desc}"
if model_scores:
evidence = []
if model_scores.get("vit_prob", 0) > 0.6:
evidence.append("ViT detected deepfake patterns")
if model_scores.get("face_prob", 0) > 0.6:
evidence.append("facial manipulation artifacts found")
if model_scores.get("forensic_prob", 0) > 0.5:
evidence.append("noise/ELA inconsistency detected")
if model_scores.get("frequency_prob", 0) > 0.5:
evidence.append("frequency-domain anomalies")
if model_scores.get("eff_prob", 0) > 0.6:
evidence.append("EfficientNet flagged AI generation")
if model_scores.get("dino_prob", 0) > 0.6:
evidence.append("DINOv2 detected synthetic features")
if model_scores.get("clip_prob", 0) > 0.6:
evidence.append("CLIP vision-language model detected manipulation signatures")
if evidence:
explanation += f". Evidence: {'; '.join(evidence)}"
return explanation
def explain_audio_risk(fake_prob):
"""Explain audio deepfake risk level."""
if fake_prob >= 0.60:
return "AI-GENERATED — AI-generated speech detected (voice cloning / TTS)"
else:
return "AUTHENTIC — Audio appears authentic"
def explain_multimodal(modality_scores, final_score):
"""
Generate explanation for multimodal fusion result.
Args:
modality_scores: dict with image/video/audio scores.
final_score: fused risk score.
Returns:
str explanation.
"""
active = {k: v for k, v in modality_scores.items() if v is not None}
if not active:
return "No modalities analyzed"
parts = []
for mod, score in active.items():
if score >= 60:
parts.append(f"{mod}: AI-generated ({score}%)")
else:
parts.append(f"{mod}: authentic ({score}%)")
if final_score >= 0.60:
verdict = "Strong evidence of manipulation across modalities"
else:
verdict = "Content appears authentic across analyzed modalities"
return f"{verdict}. Per-modality: {'; '.join(parts)}"