Spaces:
Running on Zero
Running on Zero
Download src/explain.py from AyoPrince/AuralGuard: direct link, hf CLI and curl.
- Browser
- Download file 8.27 kB
-
https://huggingface.co/spaces/AyoPrince/AuralGuard/resolve/main/src/explain.py
- Command line
-
hf download hf://spaces/AyoPrince/AuralGuard/src/explain.py
-
curl -L -o explain.py https://huggingface.co/spaces/AyoPrince/AuralGuard/resolve/main/src/explain.py
8.27 kB
| from __future__ import annotations | |
| from typing import Any, Dict, List | |
| from .evidence import build_evidence_packet | |
| from .labels import ID_TO_EXPLANATION | |
| def decision_from_probability( | |
| fake_probability: float, | |
| real_threshold: float = 0.35, | |
| fake_threshold: float = 0.85, | |
| suspicious_segments: List[Dict[str, float]] | None = None, | |
| suspicious_window_threshold: float = 0.75, | |
| ) -> str: | |
| """Safer decision gate for real-world speech. | |
| Old/simple logic: fake_prob >= 0.65 => likely fake. | |
| New logic: fake_prob must be very high, otherwise suspicious/human review. | |
| This reduces false accusations on real accented/interview/noisy speech. | |
| """ | |
| suspicious_segments = suspicious_segments or [] | |
| if fake_probability < real_threshold: | |
| return "likely real" | |
| # If probability is not very high, avoid a hard fake decision. | |
| if fake_probability < fake_threshold: | |
| return "suspicious / human review" | |
| # If very high probability, still check if there is some consistent evidence. | |
| strong_windows = [ | |
| s for s in suspicious_segments | |
| if float(s.get("fake_score", 0.0)) >= suspicious_window_threshold | |
| ] | |
| # If no localization evidence is available, allow likely fake based on high utterance score. | |
| if not suspicious_segments: | |
| return "likely fake" | |
| # If localization evidence exists and at least one strong window agrees, likely fake. | |
| if strong_windows: | |
| return "likely fake" | |
| # High utterance score but weak/unclear window evidence: safer to review. | |
| return "suspicious / human review" | |
| def beginner_friendly_explanation( | |
| fake_prob: float, | |
| attack_type: str, | |
| suspicious_segments: List[Dict[str, float]] | None = None, | |
| ) -> str: | |
| suspicious_segments = suspicious_segments or [] | |
| if fake_prob < 0.35: | |
| return ( | |
| "The model thinks this audio is likely real because the fake score is low. " | |
| "The speech patterns look closer to real human speech examples than to synthetic or manipulated speech." | |
| ) | |
| if fake_prob < 0.85: | |
| return ( | |
| "The model is not fully sure. The fake score is not low, but it is also not safe enough to call the audio fake. " | |
| "This should be treated as suspicious and reviewed by a human, especially if the clip contains an accent, interview speech, noise, or recording-quality differences." | |
| ) | |
| if attack_type == "tts_vc": | |
| reason = ( | |
| "The model thinks this audio may be fake because it contains patterns similar to text-to-speech " | |
| "or voice-conversion examples from training." | |
| ) | |
| elif attack_type == "codec": | |
| reason = ( | |
| "The model thinks this audio may be fake because it contains patterns often linked to codec-based generated speech." | |
| ) | |
| elif attack_type == "partial": | |
| reason = ( | |
| "The model thinks only part of the audio may be manipulated because suspicious evidence appears in specific time regions." | |
| ) | |
| elif attack_type == "bonafide": | |
| reason = ( | |
| "The fake score is high, but the attack-type head predicts bonafide speech. " | |
| "This conflict means the safest decision is human review rather than automatic accusation." | |
| ) | |
| else: | |
| reason = "The model found strong fake-like patterns in the audio." | |
| if suspicious_segments: | |
| top = max(suspicious_segments, key=lambda x: float(x.get("fake_score", 0.0))) | |
| time_reason = ( | |
| f" The strongest suspicious part is around {float(top['start']):.1f}s to {float(top['end']):.1f}s, " | |
| f"with a fake score of {float(top['fake_score']):.2f}." | |
| ) | |
| else: | |
| time_reason = " The evidence appears to affect the whole clip rather than one clear small region." | |
| return reason + time_reason | |
| def explanation_text( | |
| fake_probability: float, | |
| attack_type: str, | |
| explanation_id: int, | |
| suspicious_segments: List[Dict[str, float]] | None = None, | |
| ) -> List[str]: | |
| suspicious_segments = suspicious_segments or [] | |
| explanation_name = ID_TO_EXPLANATION.get(int(explanation_id), "unknown") | |
| reasons: List[str] = [] | |
| if fake_probability >= 0.85: | |
| reasons.append("The shared AASIST-based representation produced very strong utterance-level fake evidence.") | |
| elif fake_probability >= 0.65: | |
| reasons.append("The shared AASIST-based representation produced moderate-to-strong fake evidence, but this is kept in the human-review range for real-world speech.") | |
| elif fake_probability >= 0.35: | |
| reasons.append("The fake probability is in the uncertain range, so the audio should be treated as suspicious rather than automatically fake.") | |
| else: | |
| reasons.append("The full-audio fake probability is low, so no strong utterance-level fake evidence was detected.") | |
| if attack_type == "codec": | |
| reasons.append("The attack-type head predicts a codec-based fake category; this is treated as one clue, not standalone codec source tracing.") | |
| elif attack_type == "partial": | |
| reasons.append("The attack-type head predicts localized or partially manipulated speech.") | |
| elif attack_type == "tts_vc": | |
| reasons.append("The attack-type head predicts a text-to-speech or voice-conversion style spoof.") | |
| elif attack_type == "bonafide": | |
| reasons.append("The attack-type head predicts bonafide speech.") | |
| if suspicious_segments: | |
| best = max(suspicious_segments, key=lambda x: float(x.get("fake_score", 0.0))) | |
| reasons.append( | |
| f"Sliding-window inference found suspicious evidence, with the strongest region at " | |
| f"{best['start']}s–{best['end']}s and window fake score {float(best['fake_score']):.2f}." | |
| ) | |
| else: | |
| reasons.append("Sliding-window inference did not find a window above the suspicious threshold.") | |
| if explanation_name == "codec_based_artifact": | |
| reasons.append("The explanation head groups the evidence as codec-related artifacts.") | |
| elif explanation_name == "localized_partial_manipulation": | |
| reasons.append("The explanation head groups the evidence as localized partial manipulation.") | |
| elif explanation_name == "global_synthetic_artifact": | |
| reasons.append("The explanation head groups the evidence as global synthetic speech artifacts.") | |
| elif explanation_name == "no_strong_fake_evidence": | |
| reasons.append("The explanation head predicts the no-strong-fake-evidence category.") | |
| return reasons | |
| def build_report( | |
| fake_probability: float, | |
| attack_type: str, | |
| explanation_id: int, | |
| suspicious_segments: List[Dict[str, float]], | |
| real_threshold: float = 0.35, | |
| fake_threshold: float = 0.85, | |
| suspicious_window_threshold: float = 0.75, | |
| ) -> Dict[str, Any]: | |
| decision = decision_from_probability( | |
| fake_probability, | |
| real_threshold=real_threshold, | |
| fake_threshold=fake_threshold, | |
| suspicious_segments=suspicious_segments, | |
| suspicious_window_threshold=suspicious_window_threshold, | |
| ) | |
| explanation_category = ID_TO_EXPLANATION.get(int(explanation_id), "unknown") | |
| evidence = build_evidence_packet( | |
| fake_probability=fake_probability, | |
| attack_type=attack_type, | |
| explanation_category=explanation_category, | |
| suspicious_segments=suspicious_segments, | |
| thresholds={ | |
| "real": real_threshold, | |
| "fake": fake_threshold, | |
| "suspicious_window": suspicious_window_threshold, | |
| }, | |
| ) | |
| return { | |
| "decision": decision, | |
| "fake_probability": round(float(fake_probability), 4), | |
| "attack_type": attack_type, | |
| "suspicious_segments": suspicious_segments, | |
| "explanation_category": explanation_category, | |
| "explanation": explanation_text(fake_probability, attack_type, explanation_id, suspicious_segments), | |
| "beginner_explanation": beginner_friendly_explanation(fake_probability, attack_type, suspicious_segments), | |
| "evidence": evidence, | |
| } | |