Spaces:
Sleeping
Sleeping
feat: update local demo for AURIS AI music detection with enhanced dashboard and model comparison
Browse files- _bars_temp.png +0 -0
- _gauge_temp.png +0 -0
- 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 |
-
|
| 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 |
-
# ──
|
| 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 |
-
|
| 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)[:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
|
|
|
|
| 41 |
print(f"Model: {best_model_name} | Features: {n_features}")
|
| 42 |
-
print(f"
|
| 43 |
|
| 44 |
|
| 45 |
-
# ── Feature extraction
|
| 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
|
| 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 |
-
|
| 200 |
-
|
| 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
|
| 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
|
| 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
|
| 234 |
-
color = "#a64b3c"
|
| 235 |
elif ai_prob > 0.5:
|
| 236 |
-
verdict = f"Muhtemelen AI
|
| 237 |
-
color = "#c99347"
|
| 238 |
elif ai_prob > 0.3:
|
| 239 |
-
verdict = f"Muhtemelen
|
| 240 |
-
color = "#c99347"
|
| 241 |
else:
|
| 242 |
-
verdict = f"
|
| 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 |
-
|
| 251 |
-
|
| 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 |
-
#
|
| 291 |
-
|
| 292 |
-
#
|
| 293 |
-
score_end =
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 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(
|
| 332 |
-
vals = [v for _, v in
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
ax2.spines["bottom"].set_color("#3d2817")
|
| 344 |
-
ax2.spines["left"].set_color("#3d2817")
|
| 345 |
-
|
| 346 |
for i, v in enumerate(vals):
|
| 347 |
-
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 352 |
facecolor="#1a1207", edgecolor="none")
|
| 353 |
plt.close()
|
| 354 |
|
| 355 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
# ──
|
| 368 |
|
| 369 |
-
|
| 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 |
-
|
| 387 |
-
# AURIS — AI Music Detection System
|
| 388 |
|
| 389 |
-
|
| 390 |
|
| 391 |
-
Model
|
|
|
|
| 392 |
""".format(
|
| 393 |
-
|
| 394 |
-
n_feat=n_features,
|
| 395 |
n_samples=training_results.get("_n_samples", "?"),
|
| 396 |
-
|
| 397 |
)
|
| 398 |
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
|
| 403 |
-
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 408 |
f"{data.get('train_time_sec', 0):.1f}s |\n"
|
| 409 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 410 |
|
|
|
|
| 411 |
|
| 412 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 413 |
|
| 414 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 415 |
|
| 416 |
with gr.Tabs():
|
| 417 |
-
|
| 418 |
-
with gr.Tab("Analiz / Analysis"):
|
| 419 |
with gr.Row():
|
| 420 |
with gr.Column(scale=1):
|
| 421 |
audio_input = gr.Audio(
|
| 422 |
-
label="Audio
|
| 423 |
type="filepath",
|
| 424 |
)
|
| 425 |
analyze_btn = gr.Button(
|
| 426 |
-
"Analiz Et
|
| 427 |
variant="primary",
|
| 428 |
size="lg",
|
| 429 |
)
|
| 430 |
-
|
| 431 |
-
with gr.Column(scale=1):
|
| 432 |
verdict_text = gr.Textbox(
|
| 433 |
-
label="
|
| 434 |
interactive=False,
|
| 435 |
lines=2,
|
| 436 |
)
|
| 437 |
-
|
| 438 |
-
|
| 439 |
-
|
| 440 |
-
|
| 441 |
-
)
|
| 442 |
-
bars_img = gr.Image(
|
| 443 |
-
label="Özellik Skorları",
|
| 444 |
type="filepath",
|
| 445 |
-
height=
|
| 446 |
)
|
| 447 |
|
| 448 |
-
details_output = gr.Markdown(
|
| 449 |
|
| 450 |
analyze_btn.click(
|
| 451 |
fn=predict,
|
| 452 |
inputs=[audio_input],
|
| 453 |
-
outputs=[verdict_text,
|
| 454 |
)
|
| 455 |
|
| 456 |
-
|
| 457 |
-
with gr.Tab("Model Karşılaştırması"):
|
| 458 |
gr.Markdown(ALL_MODELS_MD)
|
| 459 |
|
| 460 |
-
|
| 461 |
-
|
| 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=
|
| 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 |
)
|