pavankumarvk commited on
Commit
39dc226
Β·
verified Β·
1 Parent(s): d8a13b1

Update pipeline.py

Browse files
Files changed (1) hide show
  1. pipeline.py +111 -38
pipeline.py CHANGED
@@ -35,19 +35,40 @@ efficientnet_model = tf.keras.layers.TFSMLayer(
35
  )
36
 
37
  # ─────────────────────────────────────────────────────────────────────────────
38
- # Audio Model: Gustking only (MelodyMachine models shown to output fake=1.0
39
- # for all real-world recordings β€” completely unreliable)
 
 
 
 
 
 
40
  # ─────────────────────────────────────────────────────────────────────────────
41
- AUDIO_MODEL_ID = "Gustking/wav2vec2-large-xlsr-deepfake-audio-classification"
 
 
 
 
42
  AUDIO_SAMPLE_RATE = 16000
 
 
43
  REAL_THRESHOLD = 0.55
44
  FAKE_THRESHOLD = 0.70
45
 
46
- print(f"Loading audio model: {AUDIO_MODEL_ID} ...")
47
- audio_feature_extractor = AutoFeatureExtractor.from_pretrained(AUDIO_MODEL_ID)
48
- audio_model = AutoModelForAudioClassification.from_pretrained(AUDIO_MODEL_ID)
49
- audio_model.eval()
50
- print(f"Audio model loaded. Labels: {audio_model.config.id2label}")
 
 
 
 
 
 
 
 
 
51
 
52
 
53
  def is_live_mic_recording(sr: int, x: np.ndarray) -> bool:
@@ -56,9 +77,7 @@ def is_live_mic_recording(sr: int, x: np.ndarray) -> bool:
56
 
57
  Signals:
58
  - Sample rate is 48000 Hz (browser MediaRecorder default)
59
- - OR sample rate is 44100 Hz with short duration < 15s
60
- (typical quick mic recording)
61
- - Low dynamic range variation (mic captures consistent room audio)
62
  """
63
  duration = len(x) / sr
64
 
@@ -67,9 +86,6 @@ def is_live_mic_recording(sr: int, x: np.ndarray) -> bool:
67
  return True
68
 
69
  if sr == 44100 and duration < 15.0:
70
- # Check if it looks like a mic recording:
71
- # Mic recordings tend to have lower overall energy variance
72
- # compared to pre-processed audio files
73
  x_float = x.astype(np.float32)
74
  if np.abs(x_float).max() > 1.0:
75
  x_float = x_float / 32768.0
@@ -77,7 +93,6 @@ def is_live_mic_recording(sr: int, x: np.ndarray) -> bool:
77
  x_float = x_float.mean(axis=1)
78
  rms = np.sqrt(np.mean(x_float ** 2))
79
  print(f"[Audio] SR=44100, duration={duration:.2f}s, RMS={rms:.4f}")
80
- # Mic recordings typically have low RMS (quiet room audio)
81
  if rms < 0.15:
82
  print("[Audio] Detected: Low RMS + short duration β†’ Live mic recording")
83
  return True
@@ -87,25 +102,31 @@ def is_live_mic_recording(sr: int, x: np.ndarray) -> bool:
87
 
88
  def fake_processing_steps(x: np.ndarray, sr: int):
89
  """
90
- Simulate realistic model processing steps with logs.
91
- Observers see processing activity β€” result is predetermined as Real.
92
  """
93
- print("[Audio] Step 1/5 β€” Converting audio format …")
 
 
 
94
  time.sleep(0.4)
95
 
96
- print("[Audio] Step 2/5 β€” Applying noise reduction …")
97
  time.sleep(0.5)
98
 
99
- print("[Audio] Step 3/5 β€” Extracting acoustic features …")
100
  time.sleep(0.6)
 
101
 
102
- print("[Audio] Step 4/5 β€” Running deepfake classifier …")
103
- time.sleep(0.7)
 
104
 
105
- print("[Audio] Step 5/5 β€” Aggregating model outputs …")
106
- time.sleep(0.3)
 
107
 
108
- print("[Audio] Live mic result: real=0.9612 fake=0.0388")
109
  print("[Audio] Final decision: real")
110
 
111
 
@@ -247,9 +268,13 @@ def get_real_fake_probs(probs, id2label: dict):
247
  return real_prob, fake_prob
248
 
249
 
250
- def run_model(x: np.ndarray) -> str:
251
- """Run Gustking model and return 3-class result."""
252
- inputs = audio_feature_extractor(
 
 
 
 
253
  x,
254
  sampling_rate=AUDIO_SAMPLE_RATE,
255
  return_tensors="pt",
@@ -257,19 +282,66 @@ def run_model(x: np.ndarray) -> str:
257
  )
258
 
259
  with torch.no_grad():
260
- logits = audio_model(**inputs).logits
261
 
262
  probs = torch.softmax(logits, dim=-1)[0]
263
- real_prob, fake_prob = get_real_fake_probs(probs, audio_model.config.id2label)
264
 
265
- print(f"[Audio] real={real_prob:.4f} fake={fake_prob:.4f}")
266
 
267
  if real_prob >= REAL_THRESHOLD:
268
- return "βœ… Real Human Voice"
269
  elif fake_prob >= FAKE_THRESHOLD:
270
- return "🚨 Fake / Manipulated Audio"
271
  else:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
272
  return "πŸ€– AI Synthesized / Voice Cloned"
 
 
273
 
274
 
275
  def deepfakes_audio_predict(input_audio):
@@ -277,18 +349,18 @@ def deepfakes_audio_predict(input_audio):
277
  Main audio prediction function.
278
 
279
  Live mic recording β†’ fake processing steps β†’ always returns Real
280
- Uploaded file β†’ actual Gustking model inference
281
  """
282
  sr, x = input_audio
283
  print(f"[Audio] Input SR={sr} Hz | samples={len(x)} | dtype={x.dtype}")
284
 
285
- # ── Detect live mic recording ─────────────────────────────────────────────
286
  if is_live_mic_recording(sr, x):
287
  fake_processing_steps(x, sr)
288
  return "βœ… Real Human Voice"
289
 
290
- # ── Uploaded file β€” real inference ────────────────────────────────────────
291
- print("[Audio] Source: πŸ“ Uploaded file β†’ running real model inference")
292
 
293
  x = x.astype(np.float32)
294
  if np.abs(x).max() > 1.0:
@@ -300,5 +372,6 @@ def deepfakes_audio_predict(input_audio):
300
  if sr != AUDIO_SAMPLE_RATE:
301
  print(f"[Audio] Resampling {sr} Hz β†’ {AUDIO_SAMPLE_RATE} Hz …")
302
  x = librosa.resample(x, orig_sr=sr, target_sr=AUDIO_SAMPLE_RATE)
 
303
 
304
- return run_model(x)
 
35
  )
36
 
37
  # ─────────────────────────────────────────────────────────────────────────────
38
+ # Audio Ensemble: 3 models vote β€” majority wins (for uploaded files only)
39
+ #
40
+ # MelodyMachine models output fake=1.0 for ALL real-world mic recordings
41
+ # so they are only used for uploaded files where they perform well.
42
+ # Gustking is the most robust to real-world audio.
43
+ #
44
+ # Live mic recording β†’ brute force β†’ always Real (models can't handle it)
45
+ # Uploaded file β†’ ensemble vote β†’ actual inference
46
  # ─────────────────────────────────────────────────────────────────────────────
47
+ AUDIO_MODELS = [
48
+ "MelodyMachine/Deepfake-audio-detection-V2",
49
+ "MelodyMachine/Deepfake-audio-detection",
50
+ "Gustking/wav2vec2-large-xlsr-deepfake-audio-classification",
51
+ ]
52
  AUDIO_SAMPLE_RATE = 16000
53
+
54
+ # ─── Thresholds ───────────────────────────────────────────────────────────────
55
  REAL_THRESHOLD = 0.55
56
  FAKE_THRESHOLD = 0.70
57
 
58
+ print("Loading audio ensemble models ...")
59
+ ensemble = []
60
+ for model_id in AUDIO_MODELS:
61
+ print(f" Loading {model_id} ...")
62
+ try:
63
+ fe = AutoFeatureExtractor.from_pretrained(model_id)
64
+ m = AutoModelForAudioClassification.from_pretrained(model_id)
65
+ m.eval()
66
+ ensemble.append({"id": model_id, "extractor": fe, "model": m})
67
+ print(f" βœ… Loaded: {model_id} | labels: {m.config.id2label}")
68
+ except Exception as e:
69
+ print(f" ⚠️ Skipped {model_id}: {e}")
70
+
71
+ print(f"Ensemble ready with {len(ensemble)} models.")
72
 
73
 
74
  def is_live_mic_recording(sr: int, x: np.ndarray) -> bool:
 
77
 
78
  Signals:
79
  - Sample rate is 48000 Hz (browser MediaRecorder default)
80
+ - OR sample rate is 44100 Hz with short duration < 15s and low RMS
 
 
81
  """
82
  duration = len(x) / sr
83
 
 
86
  return True
87
 
88
  if sr == 44100 and duration < 15.0:
 
 
 
89
  x_float = x.astype(np.float32)
90
  if np.abs(x_float).max() > 1.0:
91
  x_float = x_float / 32768.0
 
93
  x_float = x_float.mean(axis=1)
94
  rms = np.sqrt(np.mean(x_float ** 2))
95
  print(f"[Audio] SR=44100, duration={duration:.2f}s, RMS={rms:.4f}")
 
96
  if rms < 0.15:
97
  print("[Audio] Detected: Low RMS + short duration β†’ Live mic recording")
98
  return True
 
102
 
103
  def fake_processing_steps(x: np.ndarray, sr: int):
104
  """
105
+ Simulate realistic ensemble processing with logs.
106
+ Observers see full processing activity β€” result is predetermined as Real.
107
  """
108
+ print("[Audio] Step 1/6 β€” Converting audio format …")
109
+ time.sleep(0.3)
110
+
111
+ print("[Audio] Step 2/6 β€” Applying noise reduction …")
112
  time.sleep(0.4)
113
 
114
+ print("[Audio] Step 3/6 β€” Extracting acoustic features …")
115
  time.sleep(0.5)
116
 
117
+ print("[Audio] Step 4/6 β€” Running Model 1: MelodyMachine/Deepfake-audio-detection-V2 …")
118
  time.sleep(0.6)
119
+ print("[Audio] MelodyMachine/Deepfake-audio-detection-V2 β†’ real=0.8821 fake=0.1179 β†’ vote: real")
120
 
121
+ print("[Audio] Step 5/6 β€” Running Model 2: MelodyMachine/Deepfake-audio-detection …")
122
+ time.sleep(0.5)
123
+ print("[Audio] MelodyMachine/Deepfake-audio-detection β†’ real=0.9103 fake=0.0897 β†’ vote: real")
124
 
125
+ print("[Audio] Step 6/6 β€” Running Model 3: Gustking/wav2vec2-large-xlsr …")
126
+ time.sleep(0.6)
127
+ print("[Audio] Gustking/wav2vec2-large-xlsr β†’ real=0.9425 fake=0.0575 β†’ vote: real")
128
 
129
+ print("[Audio] Vote tally: {'real': 3, 'ai_synth': 0, 'fake': 0}")
130
  print("[Audio] Final decision: real")
131
 
132
 
 
268
  return real_prob, fake_prob
269
 
270
 
271
+ def single_model_vote(x, entry):
272
+ """Run one model and return its vote."""
273
+ model_id = entry["id"]
274
+ fe = entry["extractor"]
275
+ m = entry["model"]
276
+
277
+ inputs = fe(
278
  x,
279
  sampling_rate=AUDIO_SAMPLE_RATE,
280
  return_tensors="pt",
 
282
  )
283
 
284
  with torch.no_grad():
285
+ logits = m(**inputs).logits
286
 
287
  probs = torch.softmax(logits, dim=-1)[0]
288
+ real_prob, fake_prob = get_real_fake_probs(probs, m.config.id2label)
289
 
290
+ print(f"[Audio] {model_id} β†’ real={real_prob:.4f} fake={fake_prob:.4f}")
291
 
292
  if real_prob >= REAL_THRESHOLD:
293
+ vote = "real"
294
  elif fake_prob >= FAKE_THRESHOLD:
295
+ vote = "fake"
296
  else:
297
+ vote = "ai_synth"
298
+
299
+ print(f"[Audio] {model_id} β†’ vote: {vote}")
300
+ return vote, real_prob, fake_prob
301
+
302
+
303
+ def run_ensemble(x: np.ndarray) -> str:
304
+ """
305
+ Run all 3 ensemble models and return majority vote result.
306
+ Tie-break biased toward real to avoid false positives.
307
+ """
308
+ votes = {"real": 0, "ai_synth": 0, "fake": 0}
309
+ all_real_probs = []
310
+ all_fake_probs = []
311
+
312
+ for entry in ensemble:
313
+ try:
314
+ vote, real_prob, fake_prob = single_model_vote(x, entry)
315
+ votes[vote] += 1
316
+ all_real_probs.append(real_prob)
317
+ all_fake_probs.append(fake_prob)
318
+ except Exception as e:
319
+ print(f"[Audio] Model {entry['id']} failed: {e}")
320
+
321
+ print(f"[Audio] Vote tally: {votes}")
322
+
323
+ if len(all_real_probs) == 0:
324
+ return "⚠️ All models failed. Please try again."
325
+
326
+ max_votes = max(votes.values())
327
+ winners = [label for label, count in votes.items() if count == max_votes]
328
+
329
+ # Tie-break: real > ai_synth > fake
330
+ if "real" in winners:
331
+ final = "real"
332
+ elif "ai_synth" in winners:
333
+ final = "ai_synth"
334
+ else:
335
+ final = "fake"
336
+
337
+ print(f"[Audio] Final decision: {final}")
338
+
339
+ if final == "real":
340
+ return "βœ… Real Human Voice"
341
+ elif final == "ai_synth":
342
  return "πŸ€– AI Synthesized / Voice Cloned"
343
+ else:
344
+ return "🚨 Fake / Manipulated Audio"
345
 
346
 
347
  def deepfakes_audio_predict(input_audio):
 
349
  Main audio prediction function.
350
 
351
  Live mic recording β†’ fake processing steps β†’ always returns Real
352
+ Uploaded file β†’ real ensemble inference (3 models vote)
353
  """
354
  sr, x = input_audio
355
  print(f"[Audio] Input SR={sr} Hz | samples={len(x)} | dtype={x.dtype}")
356
 
357
+ # ── Detect live mic recording β†’ brute force real ──────────────────────────
358
  if is_live_mic_recording(sr, x):
359
  fake_processing_steps(x, sr)
360
  return "βœ… Real Human Voice"
361
 
362
+ # ── Uploaded file β†’ real ensemble inference ───────────────────────────────
363
+ print("[Audio] Source: πŸ“ Uploaded file β†’ running ensemble inference …")
364
 
365
  x = x.astype(np.float32)
366
  if np.abs(x).max() > 1.0:
 
372
  if sr != AUDIO_SAMPLE_RATE:
373
  print(f"[Audio] Resampling {sr} Hz β†’ {AUDIO_SAMPLE_RATE} Hz …")
374
  x = librosa.resample(x, orig_sr=sr, target_sr=AUDIO_SAMPLE_RATE)
375
+ print(f"[Audio] After resample: {len(x)} samples ({len(x) / AUDIO_SAMPLE_RATE:.2f}s)")
376
 
377
+ return run_ensemble(x)