S-Vetrivel commited on
Commit
f07936b
·
1 Parent(s): b1df80d

Upgrade to specialized deepfake detection model and optimize audio processing

Browse files
Files changed (4) hide show
  1. app/infer.py +79 -92
  2. test_user_b64.py +33 -0
  3. verify_inference_logic.py +38 -0
  4. verify_model.py +33 -0
app/infer.py CHANGED
@@ -4,7 +4,7 @@ import os
4
  import numpy as np
5
  import librosa
6
  import time
7
- from transformers import Wav2Vec2Model
8
  from dotenv import load_dotenv
9
 
10
  load_dotenv()
@@ -12,112 +12,97 @@ load_dotenv()
12
  class VoiceClassifier:
13
  def __init__(self):
14
  self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
15
- print(f"Loading Wav2Vec2 model on {self.device}...")
16
 
17
- # Load Pretrained Wav2Vec2-XLS-R (Multilingual: 53 languages)
18
- self.encoder = Wav2Vec2Model.from_pretrained("facebook/wav2vec2-large-xlsr-53")
19
- self.encoder.to(self.device)
20
- self.encoder.eval()
21
 
22
- # Freeze weights
23
- for param in self.encoder.parameters():
24
- param.requires_grad = False
25
-
26
- # Linear Classifier (1024 embedding + 1 pitch var)
27
- # XLS-R-53 base outputs 1024 dimension features
28
- self.classifier = nn.Linear(1024 + 1, 1).to(self.device)
29
- # Initialize with dummy weights acting as a threshold for now
30
- # Logic: High pitch variance -> Human (negative logit?), Low -> AI (positive?)
31
- # For now we'll rely on training or manual setting.
32
- # Let's set a bias that assumes Human (low prob AI) unless proven otherwise.
33
- nn.init.constant_(self.classifier.bias, -1.0)
34
- nn.init.normal_(self.classifier.weight, mean=0.0, std=0.01)
35
-
36
- print("Model loaded successfully.")
37
-
38
- def extract_features(self, waveform: torch.Tensor):
39
- """
40
- waveform: [1, T] Tensor at 16kHz
41
- Returns: feature_vector [1, 769]
42
- """
43
- waveform = waveform.to(self.device)
44
-
45
- t0 = time.time()
46
- # 1. Wav2Vec2 Embedding
47
- with torch.no_grad():
48
- outputs = self.encoder(waveform)
49
- # last_hidden_state: [1, Sequence, 768]
50
- hidden_states = outputs.last_hidden_state
51
- # Mean Pooling -> [1, 768]
52
- embedding = torch.mean(hidden_states, dim=1)
53
-
54
- t1 = time.time()
55
- print(f"DEBUG: Wav2Vec2 Embedding took {t1 - t0:.3f}s")
56
-
57
- # 2. Pitch Variance
58
- # Move to CPU for numpy/librosa ops
59
- wav_np = waveform.squeeze().cpu().numpy()
60
-
61
- # Use librosa for pitch tracking (fast approximation)
62
- # fmin/fmax for human speech range
63
- f0, voiced_flag, voiced_probs = librosa.pyin(
64
- wav_np,
65
- fmin=librosa.note_to_hz('C2'),
66
- fmax=librosa.note_to_hz('C7'),
67
- sr=16000,
68
- frame_length=2048
69
- )
70
-
71
- # Filter NaNs
72
- f0 = f0[~np.isnan(f0)]
73
-
74
- if len(f0) > 0:
75
- pitch_std = np.std(f0)
76
- # Normalize? Let's just keep raw for now, or log scale
77
- pitch_var = pitch_std
78
- else:
79
- pitch_var = 0.0
80
-
81
- t2 = time.time()
82
- print(f"DEBUG: Pitch Detection (librosa.pyin) took {t2 - t1:.3f}s")
83
-
84
- # Combine
85
- pitch_feature = torch.tensor([[pitch_var]], device=self.device, dtype=torch.float32)
86
-
87
- # Concatenate [1, 768] + [1, 1] -> [1, 769]
88
- features = torch.cat((embedding, pitch_feature), dim=1)
89
- return features, pitch_var
90
 
91
  def predict(self, waveform: torch.Tensor):
92
- if self.encoder is None:
93
  return {"error": "Model not loaded"}
94
 
95
  try:
96
- features, pitch_var = self.extract_features(waveform)
 
 
 
 
 
 
 
 
 
 
97
 
 
 
 
 
 
98
  with torch.no_grad():
99
- logits = self.classifier(features)
100
- prob_ai = torch.sigmoid(logits).item()
 
 
 
 
 
 
 
101
 
102
- # Explainability & Heuristic Classification
103
- # Validated Threshold: Pitch Variance < 20.0 usually indicates AI/Robotic speech
104
- THRESHOLD_PITCH = 20.0
 
 
 
 
 
 
 
 
 
 
 
105
 
106
- if pitch_var < THRESHOLD_PITCH:
 
 
 
 
 
107
  prediction = "AI_GENERATED"
108
- # High confidence if pitch is very low (e.g., 0-5)
109
- # confidence mapped from 20->0 as 0.7->0.99
110
- confidence = 0.7 + (0.29 * (1.0 - (pitch_var / THRESHOLD_PITCH)))
111
- explanation = "Unnatural pitch consistency and robotic speech patterns detected."
112
  else:
113
  prediction = "HUMAN"
114
- # Confidence based on how far above threshold
115
- confidence = 0.7 + min(0.25, (pitch_var - THRESHOLD_PITCH) / 50.0)
116
- explanation = "High pitch variance and natural prosody detected."
 
 
 
 
 
 
 
 
 
 
 
117
 
118
- # Override prob_ai for consistency in response
119
- prob_ai = 0.9 if prediction == "AI_GENERATED" else 0.1
120
-
121
  return {
122
  "prediction": prediction,
123
  "probability_ai": float(f"{prob_ai:.4f}"),
@@ -130,4 +115,6 @@ class VoiceClassifier:
130
 
131
  except Exception as e:
132
  print(f"Prediction Error: {e}")
 
 
133
  return {"error": str(e)}
 
4
  import numpy as np
5
  import librosa
6
  import time
7
+ from transformers import AutoModelForAudioClassification, Wav2Vec2FeatureExtractor
8
  from dotenv import load_dotenv
9
 
10
  load_dotenv()
 
12
  class VoiceClassifier:
13
  def __init__(self):
14
  self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
15
+ print(f"Loading Deepfake Detection model on {self.device}...")
16
 
17
+ # Load Fine-Tuned Deepfake Detection Model
18
+ self.model_name = "mo-thecreator/Deepfake-audio-detection"
 
 
19
 
20
+ try:
21
+ self.feature_extractor = Wav2Vec2FeatureExtractor.from_pretrained(self.model_name)
22
+ self.model = AutoModelForAudioClassification.from_pretrained(self.model_name)
23
+ self.model.to(self.device)
24
+ self.model.eval()
25
+ print(f"Model {self.model_name} loaded successfully.")
26
+ # Labels: {0: 'fake', 1: 'real'} usually for this model
27
+ print(f"Labels: {self.model.config.id2label}")
28
+ except Exception as e:
29
+ print(f"Error loading model: {e}")
30
+ self.model = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
  def predict(self, waveform: torch.Tensor):
33
+ if self.model is None:
34
  return {"error": "Model not loaded"}
35
 
36
  try:
37
+ # 1. Preprocess Audio
38
+ # Waveform is [1, T] Tensor. Convert to numpy [T]
39
+ wav_np = waveform.squeeze().cpu().numpy()
40
+
41
+ # Ensure we send it as a list or numpy array to the extractor
42
+ inputs = self.feature_extractor(
43
+ wav_np,
44
+ sampling_rate=16000,
45
+ return_tensors="pt",
46
+ padding=True
47
+ )
48
 
49
+ inputs = {k: v.to(self.device) for k, v in inputs.items()}
50
+
51
+ t0 = time.time()
52
+
53
+ # 2. Model Inference
54
  with torch.no_grad():
55
+ outputs = self.model(**inputs)
56
+ logits = outputs.logits
57
+ probs = torch.softmax(logits, dim=-1)
58
+
59
+ # Logic for this specific model:
60
+ # Label 0: 'fake' (AI)
61
+ # Label 1: 'real' (Human)
62
+ prob_fake = probs[0][0].item()
63
+ prob_real = probs[0][1].item()
64
 
65
+ t1 = time.time()
66
+ print(f"DEBUG: Inference took {t1 - t0:.3f}s. probs: {probs}")
67
+
68
+ # 3. Pitch Analysis (for explanation)
69
+ # Use librosa for pitch tracking (fast approximation)
70
+ f0, voiced_flag, voiced_probs = librosa.pyin(
71
+ wav_np,
72
+ fmin=librosa.note_to_hz('C2'),
73
+ fmax=librosa.note_to_hz('C7'),
74
+ sr=16000,
75
+ frame_length=2048
76
+ )
77
+ f0 = f0[~np.isnan(f0)]
78
+ pitch_var = np.std(f0) if len(f0) > 0 else 0.0
79
 
80
+ t2 = time.time()
81
+ print(f"DEBUG: Pitch Detection took {t2 - t1:.3f}s. Variance: {pitch_var}")
82
+
83
+ # 4. Final Classification Logic
84
+ # Deepfake model is the authority
85
+ if prob_fake > prob_real:
86
  prediction = "AI_GENERATED"
87
+ confidence = prob_fake
88
+ prob_ai = prob_fake
 
 
89
  else:
90
  prediction = "HUMAN"
91
+ confidence = prob_real
92
+ prob_ai = prob_fake
93
+
94
+ # Construct Explanation
95
+ if prediction == "AI_GENERATED":
96
+ if pitch_var < 20.0:
97
+ explanation = f"Deepfake model reported {confidence*100:.1f}% confidence. Detected unnatural pitch consistency (Variance: {pitch_var:.1f})."
98
+ else:
99
+ explanation = f"Deepfake model reported {confidence*100:.1f}% confidence. Detected digital artifacts characteristic of AI synthesis."
100
+ else:
101
+ if pitch_var > 20.0:
102
+ explanation = f"Deepfake model reported {confidence*100:.1f}% confidence. Natural prosody and high pitch variance detected."
103
+ else:
104
+ explanation = f"Deepfake model reported {confidence*100:.1f}% confidence. Audio classified as human despite low pitch variance."
105
 
 
 
 
106
  return {
107
  "prediction": prediction,
108
  "probability_ai": float(f"{prob_ai:.4f}"),
 
115
 
116
  except Exception as e:
117
  print(f"Prediction Error: {e}")
118
+ import traceback
119
+ traceback.print_exc()
120
  return {"error": str(e)}
test_user_b64.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import requests
3
+ import json
4
+
5
+ def test_provided_b64():
6
+ # Use the first part of the string provided by the user (it was truncated in the prompt view but I'll use the one I have)
7
+ # Actually, I'll just use a small script that reads it from a file if I could,
8
+ # but I'll just paste the one I have from the prompt if it fits.
9
+ # If the string is too long, I'll just use the first 100kb of the user request.
10
+
11
+ b64_str = "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"
12
+ url = "http://127.0.0.1:7860/api/voice-detection"
13
+ headers = {
14
+ "x-api-key": "test-key-123",
15
+ "Content-Type": "application/json"
16
+ }
17
+ payload = {
18
+ "language": "English",
19
+ "audioFormat": "mp3",
20
+ "audioBase64": b64_str
21
+ }
22
+
23
+ print(f"📡 POSTing specific test case to {url}...")
24
+ try:
25
+ response = requests.post(url, headers=headers, json=payload)
26
+ print(f"📥 Status Code: {response.status_code}")
27
+ print("📄 Response JSON:")
28
+ print(response.json())
29
+ except Exception as e:
30
+ print(f"❌ ERROR: {e}")
31
+
32
+ if __name__ == "__main__":
33
+ test_provided_b64()
verify_inference_logic.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import os
3
+ import sys
4
+ import numpy as np
5
+ import io
6
+ from pydub import AudioSegment
7
+
8
+ # Ensure app is in path
9
+ sys.path.append(os.getcwd())
10
+
11
+ from app.infer import VoiceClassifier
12
+ from app.audio import process_audio
13
+
14
+ def verify():
15
+ classifier = VoiceClassifier()
16
+
17
+ # Generate a valid sine wave MP3 in memory
18
+ sr = 44100
19
+ t = np.linspace(0, 1, sr, endpoint=False)
20
+ x = 0.5 * np.sin(2 * np.pi * 440 * t)
21
+ x_int = (x * 32767).astype(np.int16)
22
+ audio = AudioSegment(x_int.tobytes(), frame_rate=sr, sample_width=2, channels=1)
23
+
24
+ mp3_io = io.BytesIO()
25
+ audio.export(mp3_io, format="mp3")
26
+ mp3_bytes = mp3_io.getvalue()
27
+
28
+ print(f"Generated test MP3 bytes: {len(mp3_bytes)} bytes")
29
+
30
+ # Process audio raw bytes
31
+ waveform = process_audio(mp3_bytes)
32
+
33
+ # Predict
34
+ result = classifier.predict(waveform)
35
+ print("Result:", result)
36
+
37
+ if __name__ == "__main__":
38
+ verify()
verify_model.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import torch
3
+ from transformers import AutoModelForAudioClassification, Wav2Vec2FeatureExtractor
4
+ import numpy as np
5
+
6
+ def verify_model():
7
+ model_name = "mo-thecreator/Deepfake-audio-detection"
8
+ print(f"Loading {model_name}...")
9
+ try:
10
+ feature_extractor = Wav2Vec2FeatureExtractor.from_pretrained(model_name)
11
+ model = AutoModelForAudioClassification.from_pretrained(model_name)
12
+ print("Model loaded successfully!")
13
+
14
+ print("Labels:", model.config.id2label)
15
+
16
+ # Create dummy audio (1 second of silence/noise)
17
+ # 16000 Hz
18
+ dummy_audio = np.random.uniform(-1, 1, 16000)
19
+
20
+ inputs = feature_extractor(dummy_audio, sampling_rate=16000, return_tensors="pt")
21
+
22
+ with torch.no_grad():
23
+ logits = model(**inputs).logits
24
+
25
+ print("Logits:", logits)
26
+ predicted_class_id = torch.argmax(logits, dim=-1).item()
27
+ print("Predicted Label:", model.config.id2label[predicted_class_id])
28
+
29
+ except Exception as e:
30
+ print(f"Failed to load/run model: {e}")
31
+
32
+ if __name__ == "__main__":
33
+ verify_model()