Rthur2003 commited on
Commit
c4ee459
·
1 Parent(s): 00d7f18

feat: update local demo for AURIS AI music detection with enhanced dashboard and model comparison

Browse files
Files changed (3) hide show
  1. _bars_temp.png +0 -0
  2. _gauge_temp.png +0 -0
  3. local_demo.py +287 -201
_bars_temp.png CHANGED
_gauge_temp.png CHANGED
local_demo.py CHANGED
@@ -1,7 +1,6 @@
1
  """
2
  AURIS Local Demo — AI Music Detection
3
- Gradio arayüzü ile eğitilmiş modeli doğrudan test et.
4
- Backend'e gerek yok, model local'de çalışır.
5
 
6
  Çalıştır:
7
  python local_demo.py
@@ -16,13 +15,15 @@ from pathlib import Path
16
  import gradio as gr
17
  import numpy as np
18
 
19
- # ── Model yükleme ──────────────────────────────────────────────────
20
 
21
  MODELS_DIR = Path(__file__).parent / "models"
22
  FIGURES_DIR = Path(__file__).parent.parent / "docs" / "academic" / "figures"
23
 
 
 
24
  with open(MODELS_DIR / "auris_classifier_v1.pkl", "rb") as f:
25
- model = pickle.load(f)
26
 
27
  with open(MODELS_DIR / "feature_scaler_v1.pkl", "rb") as f:
28
  scaler = pickle.load(f)
@@ -36,15 +37,23 @@ with open(MODELS_DIR / "training_results.json", "r") as f:
36
  best_model_name = training_results.get("_best_model", "Gradient Boosting")
37
  n_features = training_results.get("_n_features", 47)
38
  importance = training_results.get("_feature_importance", {})
39
- top_features = sorted(importance.items(), key=lambda x: x[1], reverse=True)[:10]
 
 
 
 
 
 
 
40
 
 
41
  print(f"Model: {best_model_name} | Features: {n_features}")
42
- print(f"Figures: {FIGURES_DIR}")
43
 
44
 
45
- # ── Feature extraction (simplified — same as training pipeline) ────
46
 
47
- def extract_features_from_audio(audio_path: str) -> dict:
48
  """Extract 47 features from audio file using librosa."""
49
  import librosa
50
  from scipy import stats as sp_stats
@@ -105,9 +114,8 @@ def extract_features_from_audio(audio_path: str) -> dict:
105
  total_energy = harm_energy + perc_energy + 1e-10
106
  harmonic_ratio = harm_energy / total_energy
107
 
108
- # Mel features
109
  mel = librosa.feature.melspectrogram(y=y, sr=sr, hop_length=512)
110
- mel_db = librosa.power_to_db(mel)
111
  mel_flatness = float(np.mean(librosa.feature.spectral_flatness(S=mel)))
112
 
113
  # Onset
@@ -128,7 +136,6 @@ def extract_features_from_audio(audio_path: str) -> dict:
128
  else:
129
  pitch_std_cents = 0.0
130
 
131
- # Heuristic scores (same sigmoid as training)
132
  def _sigmoid(x, center=0.5, steepness=6.0):
133
  return 1.0 / (1.0 + np.exp(-steepness * (x - center)))
134
 
@@ -136,7 +143,6 @@ def extract_features_from_audio(audio_path: str) -> dict:
136
  temporal_patterns = float(_sigmoid(1.0 - tempo_cv, 0.6, 5) if tempo_cv > 0 else 0.5)
137
  harmonic_structure = float(_sigmoid(harmonic_ratio, 0.5, 4))
138
 
139
- # Build feature dict matching feature_columns_v1.json order
140
  feats = {
141
  "rms_energy": rms_mean,
142
  "rms_std": rms_std,
@@ -186,280 +192,359 @@ def extract_features_from_audio(audio_path: str) -> dict:
186
  "formant_consistency_score": 0.0,
187
  "breath_pattern_score": float(_sigmoid(rms_dynamic_range, 0.3, 5)),
188
  }
189
-
190
  return feats, duration_sec
191
 
192
 
193
  # ── Prediction ──────────────────────────────────────────────────
194
 
195
  def predict(audio_file):
196
- """Run AURIS model on uploaded audio."""
197
  if audio_file is None:
198
- return (
199
- "Dosya yükleyin / Upload a file",
200
- None, None, None, None, None
201
- )
 
202
 
203
  t0 = time.time()
204
 
205
- # Handle Gradio audio input (can be tuple or path)
206
  if isinstance(audio_file, tuple):
207
  audio_path = audio_file[0] if isinstance(audio_file[0], str) else None
208
  if audio_path is None:
209
- return ("Geçersiz dosya", None, None, None, None, None)
210
  else:
211
  audio_path = audio_file
212
 
213
  try:
214
  feats, duration = extract_features_from_audio(audio_path)
215
  except Exception as e:
216
- return (f"Hata: {e}", None, None, None, None, None)
217
 
218
- # Build feature vector in correct column order
219
  X = np.array([[feats.get(col, 0.0) for col in feature_cols]], dtype=np.float32)
220
  X = np.nan_to_num(X, nan=0.0, posinf=1.0, neginf=-1.0)
221
  X_scaled = scaler.transform(X)
222
 
223
  elapsed = time.time() - t0
224
-
225
- # Get probability
226
- prob = model.predict_proba(X_scaled)[0]
227
  ai_prob = float(prob[1])
228
  human_prob = float(prob[0])
229
- is_ai = ai_prob > 0.5
230
 
231
- # Verdict
232
  if ai_prob > 0.8:
233
- verdict = f"AI Üretimi Müzik Tespit Edildi%{ai_prob*100:.1f} güven"
234
- color = "#a64b3c"
235
  elif ai_prob > 0.5:
236
- verdict = f"Muhtemelen AI Üretimi — %{ai_prob*100:.1f} güven"
237
- color = "#c99347"
238
  elif ai_prob > 0.3:
239
- verdict = f"Muhtemelen İnsan Yapımı — %{human_prob*100:.1f} güven"
240
- color = "#c99347"
241
  else:
242
- verdict = f"İnsan Yapımı Müzik — %{human_prob*100:.1f} güven"
243
- color = "#7fb069"
244
 
245
- # Feature scores display
246
  sr_pct = feats["spectral_regularity"] * 100
247
  tp_pct = feats["temporal_patterns"] * 100
248
  hs_pct = feats["harmonic_structure"] * 100
249
 
250
- details_md = f"""
251
- ## Sonuç / Result
252
-
253
- | | |
254
- |---|---|
255
- | **Karar** | {'AI Üretimi' if is_ai else 'İnsan Yapımı'} |
256
- | **AI Olasılığı** | %{ai_prob*100:.1f} |
257
- | **İnsan Olasılığı** | %{human_prob*100:.1f} |
258
- | **Model** | {best_model_name} |
259
- | **Süre** | {duration:.1f}s |
260
- | **İşlem Süresi** | {elapsed:.2f}s |
261
-
262
- ## Ses Özellik Analizi
263
-
264
- | Özellik | Skor | Yorum |
265
- |---------|------|-------|
266
- | Spektral Düzenlilik | %{sr_pct:.0f} | {'AI benzeri düzenlilik' if sr_pct > 60 else 'Doğal varyasyon'} |
267
- | Zamansal Örüntüler | %{tp_pct:.0f} | {'Metronomik hassasiyet' if tp_pct > 60 else 'Doğal zamanlama'} |
268
- | Harmonik Yapı | %{hs_pct:.0f} | {'Tahmin edilebilir paternler' if hs_pct > 60 else 'Organik harmonik yapı'} |
269
-
270
- ## En Önemli 10 Özellik (Bu Dosya İçin)
271
-
272
- | Özellik | Değer | Global Önem |
273
- |---------|-------|-------------|
274
- """
275
- for fname, imp in top_features:
276
- val = feats.get(fname, 0.0)
277
- details_md += f"| {fname} | {val:.4f} | {imp:.4f} |\n"
278
-
279
- # Gauge plot
280
- import matplotlib
281
- matplotlib.use("Agg")
282
- import matplotlib.pyplot as plt
283
- import matplotlib.patches as mpatches
284
-
285
- fig, ax = plt.subplots(figsize=(6, 3), subplot_kw={"projection": "polar"})
286
- fig.patch.set_facecolor("#1a1207")
287
 
 
 
 
288
  theta = np.linspace(np.pi, 0, 100)
289
  r = np.ones(100)
290
- # Background arc
291
- ax.plot(theta, r, color="#3d2817", linewidth=20, alpha=0.3)
292
- # Score arc
293
- score_end = int(ai_prob * 100)
294
- if score_end > 0:
295
- c = "#7fb069" if ai_prob < 0.4 else "#c99347" if ai_prob < 0.7 else "#a64b3c"
296
- ax.plot(theta[:score_end], r[:score_end], color=c, linewidth=20)
297
-
298
- # Needle
299
- needle_angle = np.pi - ai_prob * np.pi
300
- ax.plot([needle_angle, needle_angle], [0, 0.85], color="#faf6ed", linewidth=2)
301
- ax.scatter([needle_angle], [0.85], color="#faf6ed", s=30, zorder=5)
302
-
303
- ax.set_ylim(0, 1.2)
304
- ax.set_yticklabels([])
305
- ax.set_xticklabels([])
306
- ax.spines["polar"].set_visible(False)
307
- ax.grid(False)
308
-
309
- ax.text(0, -0.3, f"%{ai_prob*100:.0f}", ha="center", va="center",
310
- fontsize=28, fontweight="bold", color="#faf6ed",
311
- transform=ax.transAxes)
312
- ax.text(0, -0.45, "AI Olasılığı", ha="center", va="center",
313
- fontsize=10, color="#c99347", transform=ax.transAxes)
314
-
315
- plt.tight_layout()
316
- gauge_path = str(Path(__file__).parent / "_gauge_temp.png")
317
- plt.savefig(gauge_path, dpi=100, bbox_inches="tight",
318
- facecolor="#1a1207", edgecolor="none")
319
- plt.close()
320
-
321
- # Feature bars plot
322
- fig2, ax2 = plt.subplots(figsize=(6, 2.5))
323
- fig2.patch.set_facecolor("#1a1207")
324
- ax2.set_facecolor("#1a1207")
325
-
326
- bars_data = [
327
- ("Spektral Düzenlilik", sr_pct),
328
- ("Zamansal Örüntüler", tp_pct),
329
- ("Harmonik Yapı", hs_pct),
330
  ]
331
- y_pos = np.arange(len(bars_data))
332
- vals = [v for _, v in bars_data]
333
- colors = ["#c99347" if v > 60 else "#7fb069" for v in vals]
334
-
335
- ax2.barh(y_pos, vals, color=colors, edgecolor="#3d2817", height=0.6)
336
- ax2.set_yticks(y_pos)
337
- ax2.set_yticklabels([n for n, _ in bars_data], color="#faf6ed", fontsize=10)
338
- ax2.set_xlim(0, 100)
339
- ax2.set_xlabel("Skor (%)", color="#c99347")
340
- ax2.tick_params(colors="#c99347")
341
- ax2.spines["top"].set_visible(False)
342
- ax2.spines["right"].set_visible(False)
343
- ax2.spines["bottom"].set_color("#3d2817")
344
- ax2.spines["left"].set_color("#3d2817")
345
-
346
  for i, v in enumerate(vals):
347
- ax2.text(v + 1, i, f"%{v:.0f}", va="center", color="#faf6ed", fontsize=10)
348
-
349
- plt.tight_layout()
350
- bars_path = str(Path(__file__).parent / "_bars_temp.png")
351
- plt.savefig(bars_path, dpi=100, bbox_inches="tight",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
352
  facecolor="#1a1207", edgecolor="none")
353
  plt.close()
354
 
355
- return verdict, gauge_path, bars_path, details_md
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
356
 
357
 
358
  # ── Figures gallery ─────────────────────────────────────────────
359
 
360
  def get_figure_paths():
361
- """Get all academic figure paths."""
362
  if FIGURES_DIR.exists():
363
  return sorted(str(p) for p in FIGURES_DIR.glob("*.png"))
364
  return []
365
 
366
 
367
- # ── Gradio UI ───────────────────────────────────────────────────
368
 
369
- AURIS_CSS = """
370
- .gradio-container {
371
- background: linear-gradient(135deg, #1a1207 0%, #2a1f10 50%, #1a1207 100%) !important;
372
- font-family: 'Segoe UI', sans-serif;
373
- }
374
- .dark { background: #1a1207 !important; }
375
- h1, h2, h3 { color: #c99347 !important; }
376
- p, span, label { color: #faf6ed !important; }
377
- .gr-button-primary {
378
- background: linear-gradient(135deg, #c99347, #e7c77a) !important;
379
- color: #1a1207 !important;
380
- border: none !important;
381
- font-weight: bold !important;
382
- }
383
- footer { display: none !important; }
384
- """
385
 
386
- HEADER_MD = """
387
- # AURIS — AI Music Detection System
388
 
389
- **Yapay Zeka Müzik Tespit Platformu**
390
 
391
- Model: **{model}** | Özellikler: **{n_feat}** | Veri: **{n_samples}** örnek | AUC: **{auc}**
 
392
  """.format(
393
- model=best_model_name,
394
- n_feat=n_features,
395
  n_samples=training_results.get("_n_samples", "?"),
396
- auc=training_results.get(best_model_name, {}).get("roc_auc", "?"),
397
  )
398
 
399
- ALL_MODELS_MD = "## Tüm Model Sonuçları\n\n| Model | Accuracy | F1 | ROC-AUC | Süre |\n|-------|----------|-----|---------|------|\n"
400
- for name, data in sorted(
401
- ((k, v) for k, v in training_results.items()
402
- if not k.startswith("_") and isinstance(v, dict)),
403
- key=lambda x: -x[1].get("roc_auc", 0),
404
- ):
405
- ALL_MODELS_MD += (
406
- f"| {name} | {data.get('accuracy', 0):.4f} | "
407
- f"{data.get('f1', 0):.4f} | {data.get('roc_auc', 0):.4f} | "
 
 
 
 
 
 
408
  f"{data.get('train_time_sec', 0):.1f}s |\n"
409
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
410
 
 
411
 
412
- with gr.Blocks(css=AURIS_CSS, title="AURIS — AI Music Detection", theme=gr.themes.Base()) as demo:
 
 
 
 
 
 
 
 
 
 
413
 
414
- gr.Markdown(HEADER_MD)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
415
 
416
  with gr.Tabs():
417
- # ── Tab 1: Analysis ──
418
- with gr.Tab("Analiz / Analysis"):
419
  with gr.Row():
420
  with gr.Column(scale=1):
421
  audio_input = gr.Audio(
422
- label="Audio Dosyası Yükle",
423
  type="filepath",
424
  )
425
  analyze_btn = gr.Button(
426
- "Analiz Et / Analyze",
427
  variant="primary",
428
  size="lg",
429
  )
430
-
431
- with gr.Column(scale=1):
432
  verdict_text = gr.Textbox(
433
- label="Sonuç / Verdict",
434
  interactive=False,
435
  lines=2,
436
  )
437
- gauge_img = gr.Image(
438
- label="AI Olasılığı",
439
- type="filepath",
440
- height=200,
441
- )
442
- bars_img = gr.Image(
443
- label="Özellik Skorları",
444
  type="filepath",
445
- height=180,
446
  )
447
 
448
- details_output = gr.Markdown(label="Detaylar")
449
 
450
  analyze_btn.click(
451
  fn=predict,
452
  inputs=[audio_input],
453
- outputs=[verdict_text, gauge_img, bars_img, details_output],
454
  )
455
 
456
- # ── Tab 2: Model Comparison ──
457
- with gr.Tab("Model Karşılaştırması"):
458
  gr.Markdown(ALL_MODELS_MD)
459
 
460
- # ── Tab 3: Academic Figures ──
461
- with gr.Tab("Akademik Görseller"):
462
- gr.Markdown("## Eğitim ve Değerlendirme Görselleri")
463
  figure_paths = get_figure_paths()
464
  if figure_paths:
465
  gr.Gallery(
@@ -469,20 +554,21 @@ with gr.Blocks(css=AURIS_CSS, title="AURIS — AI Music Detection", theme=gr.the
469
  height="auto",
470
  object_fit="contain",
471
  )
472
- else:
473
- gr.Markdown("*Görseller bulunamadı.*")
474
-
475
- gr.Markdown(
476
- "---\n"
477
- "*AURIS v1 — Düzce Üniversitesi Bilgisayar Mühendisliği Bitirme Projesi*\n\n"
478
- "*Hasan Arthur Altuntaş — 2026*"
479
- )
480
 
 
 
 
 
 
 
 
 
481
 
482
  if __name__ == "__main__":
483
  demo.launch(
484
  server_name="0.0.0.0",
485
- server_port=7861,
486
  share=False,
487
  inbrowser=True,
 
488
  )
 
1
  """
2
  AURIS Local Demo — AI Music Detection
3
+ Tüm eğitilmiş modelleri test edebileceğin local Gradio arayüzü.
 
4
 
5
  Çalıştır:
6
  python local_demo.py
 
15
  import gradio as gr
16
  import numpy as np
17
 
18
+ # ── Paths ───────────────────────────────────────────────────────
19
 
20
  MODELS_DIR = Path(__file__).parent / "models"
21
  FIGURES_DIR = Path(__file__).parent.parent / "docs" / "academic" / "figures"
22
 
23
+ # ── Load artifacts ──────────────────────────────────────────────
24
+
25
  with open(MODELS_DIR / "auris_classifier_v1.pkl", "rb") as f:
26
+ main_model = pickle.load(f)
27
 
28
  with open(MODELS_DIR / "feature_scaler_v1.pkl", "rb") as f:
29
  scaler = pickle.load(f)
 
37
  best_model_name = training_results.get("_best_model", "Gradient Boosting")
38
  n_features = training_results.get("_n_features", 47)
39
  importance = training_results.get("_feature_importance", {})
40
+ top_features = sorted(importance.items(), key=lambda x: x[1], reverse=True)[:15]
41
+
42
+ # All model results sorted by AUC
43
+ all_models = sorted(
44
+ ((k, v) for k, v in training_results.items()
45
+ if not k.startswith("_") and isinstance(v, dict)),
46
+ key=lambda x: -x[1].get("roc_auc", 0),
47
+ )
48
 
49
+ print(f"AURIS Local Demo")
50
  print(f"Model: {best_model_name} | Features: {n_features}")
51
+ print(f"Total models: {len(all_models)}")
52
 
53
 
54
+ # ── Feature extraction ──────────────────────────────────────────
55
 
56
+ def extract_features_from_audio(audio_path: str) -> tuple[dict, float]:
57
  """Extract 47 features from audio file using librosa."""
58
  import librosa
59
  from scipy import stats as sp_stats
 
114
  total_energy = harm_energy + perc_energy + 1e-10
115
  harmonic_ratio = harm_energy / total_energy
116
 
117
+ # Mel
118
  mel = librosa.feature.melspectrogram(y=y, sr=sr, hop_length=512)
 
119
  mel_flatness = float(np.mean(librosa.feature.spectral_flatness(S=mel)))
120
 
121
  # Onset
 
136
  else:
137
  pitch_std_cents = 0.0
138
 
 
139
  def _sigmoid(x, center=0.5, steepness=6.0):
140
  return 1.0 / (1.0 + np.exp(-steepness * (x - center)))
141
 
 
143
  temporal_patterns = float(_sigmoid(1.0 - tempo_cv, 0.6, 5) if tempo_cv > 0 else 0.5)
144
  harmonic_structure = float(_sigmoid(harmonic_ratio, 0.5, 4))
145
 
 
146
  feats = {
147
  "rms_energy": rms_mean,
148
  "rms_std": rms_std,
 
192
  "formant_consistency_score": 0.0,
193
  "breath_pattern_score": float(_sigmoid(rms_dynamic_range, 0.3, 5)),
194
  }
 
195
  return feats, duration_sec
196
 
197
 
198
  # ── Prediction ──────────────────────────────────────────────────
199
 
200
  def predict(audio_file):
201
+ """Run AURIS model on uploaded audio and return rich results."""
202
  if audio_file is None:
203
+ return "Dosya yükleyin", None, None, ""
204
+
205
+ import matplotlib
206
+ matplotlib.use("Agg")
207
+ import matplotlib.pyplot as plt
208
 
209
  t0 = time.time()
210
 
 
211
  if isinstance(audio_file, tuple):
212
  audio_path = audio_file[0] if isinstance(audio_file[0], str) else None
213
  if audio_path is None:
214
+ return "Gecersiz dosya", None, None, ""
215
  else:
216
  audio_path = audio_file
217
 
218
  try:
219
  feats, duration = extract_features_from_audio(audio_path)
220
  except Exception as e:
221
+ return f"Hata: {e}", None, None, ""
222
 
 
223
  X = np.array([[feats.get(col, 0.0) for col in feature_cols]], dtype=np.float32)
224
  X = np.nan_to_num(X, nan=0.0, posinf=1.0, neginf=-1.0)
225
  X_scaled = scaler.transform(X)
226
 
227
  elapsed = time.time() - t0
228
+ prob = main_model.predict_proba(X_scaled)[0]
 
 
229
  ai_prob = float(prob[1])
230
  human_prob = float(prob[0])
 
231
 
 
232
  if ai_prob > 0.8:
233
+ verdict = f"AI Uretimi Muzik Tespit Edildi | %{ai_prob*100:.1f} guven"
 
234
  elif ai_prob > 0.5:
235
+ verdict = f"Muhtemelen AI Uretimi | %{ai_prob*100:.1f} guven"
 
236
  elif ai_prob > 0.3:
237
+ verdict = f"Muhtemelen Insan Yapimi | %{human_prob*100:.1f} guven"
 
238
  else:
239
+ verdict = f"Insan Yapimi Muzik | %{human_prob*100:.1f} guven"
 
240
 
 
241
  sr_pct = feats["spectral_regularity"] * 100
242
  tp_pct = feats["temporal_patterns"] * 100
243
  hs_pct = feats["harmonic_structure"] * 100
244
 
245
+ # ── Main dashboard plot (16:9) ──
246
+ fig = plt.figure(figsize=(16, 9), facecolor="#1a1207")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
 
248
+ # ── Left: Gauge + verdict ──
249
+ ax_gauge = fig.add_axes([0.02, 0.45, 0.28, 0.50], projection="polar")
250
+ ax_gauge.set_facecolor("#1a1207")
251
  theta = np.linspace(np.pi, 0, 100)
252
  r = np.ones(100)
253
+ ax_gauge.plot(theta, r, color="#3d2817", linewidth=24, alpha=0.4)
254
+ score_end = max(1, int(ai_prob * 100))
255
+ c = "#7fb069" if ai_prob < 0.4 else "#c99347" if ai_prob < 0.7 else "#a64b3c"
256
+ ax_gauge.plot(theta[:score_end], r[:score_end], color=c, linewidth=24)
257
+ needle = np.pi - ai_prob * np.pi
258
+ ax_gauge.plot([needle, needle], [0, 0.82], color="#faf6ed", linewidth=2.5)
259
+ ax_gauge.scatter([needle], [0.82], color="#faf6ed", s=40, zorder=5)
260
+ ax_gauge.set_ylim(0, 1.2)
261
+ ax_gauge.set_yticklabels([])
262
+ ax_gauge.set_xticklabels([])
263
+ ax_gauge.spines["polar"].set_visible(False)
264
+ ax_gauge.grid(False)
265
+
266
+ fig.text(0.16, 0.42, f"%{ai_prob*100:.0f}", ha="center", va="center",
267
+ fontsize=42, fontweight="bold", color="#faf6ed")
268
+ fig.text(0.16, 0.36, "AI Olasiligi", ha="center", va="center",
269
+ fontsize=12, color="#c99347")
270
+
271
+ label = "AI URETIMI" if ai_prob > 0.5 else "INSAN YAPIMI"
272
+ label_color = "#a64b3c" if ai_prob > 0.5 else "#7fb069"
273
+ fig.text(0.16, 0.30, label, ha="center", va="center",
274
+ fontsize=16, fontweight="bold", color=label_color,
275
+ bbox=dict(boxstyle="round,pad=0.4", facecolor="#2a1f10",
276
+ edgecolor=label_color, linewidth=2))
277
+
278
+ # ── Middle: Feature bars ──
279
+ ax_bars = fig.add_axes([0.35, 0.55, 0.28, 0.35])
280
+ ax_bars.set_facecolor("#2a1f10")
281
+ bars = [
282
+ ("Spektral Duzenlilik", sr_pct),
283
+ ("Zamansal Oruntuler", tp_pct),
284
+ ("Harmonik Yapi", hs_pct),
 
 
 
 
 
 
 
 
285
  ]
286
+ y_pos = np.arange(len(bars))
287
+ vals = [v for _, v in bars]
288
+ colors_b = ["#c99347" if v > 60 else "#7fb069" if v < 40 else "#e7c77a" for v in vals]
289
+
290
+ ax_bars.barh(y_pos, vals, color=colors_b, edgecolor="#3d2817", height=0.55)
291
+ ax_bars.set_yticks(y_pos)
292
+ ax_bars.set_yticklabels([n for n, _ in bars], color="#faf6ed", fontsize=11)
293
+ ax_bars.set_xlim(0, 100)
294
+ ax_bars.set_xlabel("Skor (%)", color="#c99347", fontsize=10)
295
+ ax_bars.tick_params(colors="#c99347", labelsize=9)
296
+ for spine in ax_bars.spines.values():
297
+ spine.set_color("#3d2817")
 
 
 
298
  for i, v in enumerate(vals):
299
+ ax_bars.text(v + 1.5, i, f"%{v:.0f}", va="center", color="#faf6ed",
300
+ fontsize=11, fontweight="bold")
301
+ ax_bars.set_title("Ses Ozellik Analizi", color="#c99347", fontsize=13,
302
+ fontweight="bold", pad=8)
303
+
304
+ # ── Right: Top feature importance ──
305
+ ax_imp = fig.add_axes([0.70, 0.35, 0.27, 0.58])
306
+ ax_imp.set_facecolor("#2a1f10")
307
+ top10 = top_features[:10]
308
+ imp_names = [n for n, _ in top10]
309
+ imp_vals = [v * 100 for _, v in top10]
310
+ imp_colors = plt.cm.copper(np.linspace(0.3, 0.85, len(imp_names)))
311
+
312
+ ax_imp.barh(np.arange(len(imp_names)), imp_vals, color=imp_colors,
313
+ edgecolor="#3d2817", height=0.6)
314
+ ax_imp.set_yticks(np.arange(len(imp_names)))
315
+ ax_imp.set_yticklabels(imp_names, color="#faf6ed", fontsize=8)
316
+ ax_imp.invert_yaxis()
317
+ ax_imp.set_xlabel("Onem (%)", color="#c99347", fontsize=9)
318
+ ax_imp.tick_params(colors="#c99347", labelsize=8)
319
+ for spine in ax_imp.spines.values():
320
+ spine.set_color("#3d2817")
321
+ ax_imp.set_title("En Onemli Ozellikler", color="#c99347", fontsize=12,
322
+ fontweight="bold", pad=8)
323
+
324
+ # ── Bottom: Info strip ──
325
+ info_text = (
326
+ f"Model: {best_model_name} | Sure: {duration:.1f}s | "
327
+ f"Islem: {elapsed:.2f}s | Ozellik: {n_features} | "
328
+ f"Tempo: {feats['tempo_bpm']:.0f} BPM | "
329
+ f"RMS: {feats['rms_energy']:.4f}"
330
+ )
331
+ fig.text(0.50, 0.06, info_text, ha="center", va="center",
332
+ fontsize=10, color="#c99347",
333
+ bbox=dict(boxstyle="round,pad=0.6", facecolor="#2a1f10",
334
+ edgecolor="#3d2817", linewidth=1))
335
+
336
+ # ── Title ──
337
+ fig.text(0.50, 0.97, "AURIS — AI Music Detection System",
338
+ ha="center", va="top", fontsize=20, fontweight="bold",
339
+ color="#c99347")
340
+ fig.text(0.50, 0.93,
341
+ f"{best_model_name} | AUC={training_results.get(best_model_name, {}).get('roc_auc', 0):.4f}",
342
+ ha="center", va="top", fontsize=11, color="#faf6ed", alpha=0.7)
343
+
344
+ # ── Bottom left: Mini model comparison ──
345
+ ax_models = fig.add_axes([0.04, 0.12, 0.58, 0.22])
346
+ ax_models.set_facecolor("#2a1f10")
347
+ model_names = [n for n, _ in all_models]
348
+ model_aucs = [d.get("roc_auc", 0) for _, d in all_models]
349
+ model_types = []
350
+ for _, d in all_models:
351
+ if d.get("type") == "deep_learning":
352
+ model_types.append("#a64b3c")
353
+ else:
354
+ model_types.append("#c99347")
355
+
356
+ x_pos = np.arange(len(model_names))
357
+ ax_models.bar(x_pos, model_aucs, color=model_types, edgecolor="#3d2817",
358
+ width=0.7)
359
+ ax_models.set_xticks(x_pos)
360
+ ax_models.set_xticklabels(model_names, rotation=30, ha="right",
361
+ color="#faf6ed", fontsize=7)
362
+ ax_models.set_ylabel("ROC-AUC", color="#c99347", fontsize=9)
363
+ ax_models.set_ylim(0.80, 0.97)
364
+ ax_models.tick_params(colors="#c99347", labelsize=8)
365
+ for spine in ax_models.spines.values():
366
+ spine.set_color("#3d2817")
367
+ ax_models.set_title("Tum Modeller (sari=ML, kirmizi=DL)", color="#c99347",
368
+ fontsize=10, fontweight="bold", pad=6)
369
+ for i, v in enumerate(model_aucs):
370
+ ax_models.text(i, v + 0.002, f"{v:.3f}", ha="center", va="bottom",
371
+ color="#faf6ed", fontsize=6)
372
+
373
+ dashboard_path = str(Path(__file__).parent / "_dashboard_temp.png")
374
+ plt.savefig(dashboard_path, dpi=120, bbox_inches="tight",
375
  facecolor="#1a1207", edgecolor="none")
376
  plt.close()
377
 
378
+ # ── Details markdown ──
379
+ details_md = f"""
380
+ ## Detayli Sonuclar
381
+
382
+ | Metrik | Deger |
383
+ |--------|-------|
384
+ | AI Olasiligi | %{ai_prob*100:.2f} |
385
+ | Insan Olasiligi | %{human_prob*100:.2f} |
386
+ | Model | {best_model_name} |
387
+ | Audio Suresi | {duration:.1f}s |
388
+ | Islem Suresi | {elapsed:.2f}s |
389
+ | Tempo | {feats['tempo_bpm']:.1f} BPM |
390
+ | RMS Energy | {feats['rms_energy']:.6f} |
391
+ | Spectral Centroid | {feats['spectral_centroid_mean']:.1f} Hz |
392
+ | Spectral Flatness | {feats['spectral_flatness_mean']:.6f} |
393
+ | Harmonic Ratio | {feats['harmonic_ratio']:.4f} |
394
+ | Zero Crossing Rate | {feats['zero_crossing_rate']:.6f} |
395
+ | Beat Count | {feats['beat_count']:.0f} |
396
+
397
+ ## Tum {n_features} Ozellik Degerleri
398
+
399
+ | Ozellik | Deger | Global Onem |
400
+ |---------|-------|-------------|
401
+ """
402
+ for col in feature_cols:
403
+ val = feats.get(col, 0.0)
404
+ imp_val = importance.get(col, 0.0)
405
+ bar = "█" * int(imp_val * 200)
406
+ details_md += f"| {col} | {val:.6f} | {imp_val:.4f} {bar} |\n"
407
+
408
+ return verdict, dashboard_path, None, details_md
409
 
410
 
411
  # ── Figures gallery ─────────────────────────────────────────────
412
 
413
  def get_figure_paths():
 
414
  if FIGURES_DIR.exists():
415
  return sorted(str(p) for p in FIGURES_DIR.glob("*.png"))
416
  return []
417
 
418
 
419
+ # ── Model comparison table ──────────────────────────────────────
420
 
421
+ ALL_MODELS_MD = """## Tum Egitilmis Model Sonuclari
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
422
 
423
+ > **En iyi model**: {best} | **Veri**: {n_samples} ornek | **Ozellik**: {n_feat} | **CV**: 5-fold stratified
 
424
 
425
+ ### Makine Ogrenmesi (ML) Modelleri
426
 
427
+ | Model | Accuracy | Precision | Recall | F1 Score | ROC-AUC | Egitim Suresi |
428
+ |-------|----------|-----------|--------|----------|---------|---------------|
429
  """.format(
430
+ best=best_model_name,
 
431
  n_samples=training_results.get("_n_samples", "?"),
432
+ n_feat=n_features,
433
  )
434
 
435
+ DL_MODELS_MD = """
436
+ ### Derin Ogrenme (DL) Modelleri
437
+
438
+ | Model | Accuracy | Precision | Recall | F1 Score | ROC-AUC | Egitim Suresi |
439
+ |-------|----------|-----------|--------|----------|---------|---------------|
440
+ """
441
+
442
+ for name, data in all_models:
443
+ row = (
444
+ f"| {'**' + name + '**' if name == best_model_name else name} | "
445
+ f"{data.get('accuracy', 0):.4f} | "
446
+ f"{data.get('precision', 0):.4f} | "
447
+ f"{data.get('recall', 0):.4f} | "
448
+ f"{data.get('f1', 0):.4f} | "
449
+ f"{data.get('roc_auc', 0):.4f} | "
450
  f"{data.get('train_time_sec', 0):.1f}s |\n"
451
  )
452
+ if data.get("type") == "deep_learning":
453
+ DL_MODELS_MD += row
454
+ else:
455
+ ALL_MODELS_MD += row
456
+
457
+ ALL_MODELS_MD += DL_MODELS_MD
458
+
459
+ ALL_MODELS_MD += f"""
460
+ ### Ozellik Onemi (En Iyi Model: {best_model_name})
461
+
462
+ | Sira | Ozellik | Onem | |
463
+ |------|---------|------|-|
464
+ """
465
+ for i, (fname, imp) in enumerate(top_features, 1):
466
+ bar = "█" * int(imp * 300)
467
+ ALL_MODELS_MD += f"| {i} | {fname} | {imp:.4f} | {bar} |\n"
468
+
469
+ ALL_MODELS_MD += f"""
470
+ ### Notlar
471
+ - **Veri sizintisi duzeltildi**: `duration_sec` ve `sample_rate` ozelliklerden cikarildi (v2)
472
+ - **Regularizasyon**: max_depth=4, subsample=0.75, min_samples_leaf=12
473
+ - Tree ensemble modellerinde train AUC=1.0 yapisal bir ozelliktir (overfitting degil)
474
+ - DL modelleri (Deep MLP, Residual MLP) regularize edilmis ML'yi geciyor
475
+ """
476
 
477
+ # ── Gradio UI ───────────────────────────────────────────────────
478
 
479
+ AURIS_CSS = """
480
+ .gradio-container {
481
+ background: linear-gradient(135deg, #1a1207 0%, #2a1f10 50%, #1a1207 100%) !important;
482
+ max-width: 1400px !important;
483
+ }
484
+ h1, h2, h3 { color: #c99347 !important; }
485
+ p, span, label, td, th { color: #faf6ed !important; }
486
+ table { border-color: #3d2817 !important; }
487
+ th { background: #2a1f10 !important; color: #c99347 !important; }
488
+ footer { display: none !important; }
489
+ """
490
 
491
+ HEADER_HTML = f"""
492
+ <div style="text-align:center; padding: 16px 0;">
493
+ <h1 style="color:#c99347; margin:0; font-size:2.2em;">AURIS</h1>
494
+ <p style="color:#faf6ed; opacity:0.8; margin:4px 0;">
495
+ AI Music Detection System — Yapay Zeka Muzik Tespit Platformu
496
+ </p>
497
+ <p style="color:#c99347; font-size:0.9em;">
498
+ {best_model_name} | {n_features} ozellik |
499
+ {training_results.get('_n_samples', '?')} ornek |
500
+ AUC: {training_results.get(best_model_name, {}).get('roc_auc', '?')}
501
+ </p>
502
+ </div>
503
+ """
504
+
505
+ with gr.Blocks(title="AURIS — AI Music Detection") as demo:
506
+
507
+ gr.HTML(HEADER_HTML)
508
 
509
  with gr.Tabs():
510
+ with gr.Tab("Analiz"):
 
511
  with gr.Row():
512
  with gr.Column(scale=1):
513
  audio_input = gr.Audio(
514
+ label="Audio Dosyasi Yukle (.mp3, .wav, .flac)",
515
  type="filepath",
516
  )
517
  analyze_btn = gr.Button(
518
+ "Analiz Et",
519
  variant="primary",
520
  size="lg",
521
  )
 
 
522
  verdict_text = gr.Textbox(
523
+ label="Sonuc",
524
  interactive=False,
525
  lines=2,
526
  )
527
+
528
+ with gr.Column(scale=2):
529
+ dashboard_img = gr.Image(
530
+ label="AURIS Dashboard",
 
 
 
531
  type="filepath",
532
+ height=500,
533
  )
534
 
535
+ details_output = gr.Markdown()
536
 
537
  analyze_btn.click(
538
  fn=predict,
539
  inputs=[audio_input],
540
+ outputs=[verdict_text, dashboard_img, gr.State(), details_output],
541
  )
542
 
543
+ with gr.Tab("Model Karsilastirmasi"):
 
544
  gr.Markdown(ALL_MODELS_MD)
545
 
546
+ with gr.Tab("Akademik Gorseller"):
547
+ gr.Markdown("## Egitim ve Degerlendirme Gorselleri")
 
548
  figure_paths = get_figure_paths()
549
  if figure_paths:
550
  gr.Gallery(
 
554
  height="auto",
555
  object_fit="contain",
556
  )
 
 
 
 
 
 
 
 
557
 
558
+ gr.HTML("""
559
+ <div style="text-align:center; padding:12px; opacity:0.6;">
560
+ <p style="color:#c99347; font-size:0.85em;">
561
+ AURIS v1 — Duzce Universitesi Bilgisayar Muhendisligi Bitirme Projesi<br>
562
+ Hasan Arthur Altuntas — 2026
563
+ </p>
564
+ </div>
565
+ """)
566
 
567
  if __name__ == "__main__":
568
  demo.launch(
569
  server_name="0.0.0.0",
570
+ server_port=7862,
571
  share=False,
572
  inbrowser=True,
573
+ css=AURIS_CSS,
574
  )