Spaces:
Running on Zero
Running on Zero
Update app.py
Browse files
app.py
CHANGED
|
@@ -383,6 +383,7 @@ def build_encoder(config):
|
|
| 383 |
# 5. Load the model on CPU
|
| 384 |
# --------------------------------------------------
|
| 385 |
model = None
|
|
|
|
| 386 |
encoder = None
|
| 387 |
LABELS = []
|
| 388 |
LOAD_ERROR = None
|
|
@@ -398,6 +399,12 @@ try:
|
|
| 398 |
|
| 399 |
config = load_config(ckpt)
|
| 400 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 401 |
# Build the correctly ordered 27-class encoder.
|
| 402 |
encoder = build_encoder(config)
|
| 403 |
LABELS = list(encoder.labels)
|
|
@@ -405,29 +412,61 @@ try:
|
|
| 405 |
# Build the CRNN student model.
|
| 406 |
sed_student = CRNN(**config["net"])
|
| 407 |
|
| 408 |
-
# Load the
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 409 |
beats_path = ensure_beats_checkpoint()
|
| 410 |
beats_ckpt = torch_load(beats_path)
|
| 411 |
|
| 412 |
beats_cfg = BEATsConfig(beats_ckpt["cfg"])
|
| 413 |
-
|
| 414 |
-
|
| 415 |
-
|
| 416 |
-
beats_ckpt["model"]
|
| 417 |
-
)
|
| 418 |
-
|
| 419 |
-
pretrained.eval()
|
| 420 |
|
| 421 |
-
for parameter in
|
| 422 |
parameter.requires_grad = False
|
| 423 |
|
| 424 |
-
# Construct the DCASE
|
|
|
|
| 425 |
model = call_with_supported_kwargs(
|
| 426 |
SEDTask4,
|
| 427 |
hparams=config,
|
| 428 |
encoder=encoder,
|
| 429 |
sed_student=sed_student,
|
| 430 |
-
pretrained_model=
|
| 431 |
opt=None,
|
| 432 |
train_data=None,
|
| 433 |
valid_data=None,
|
|
@@ -438,33 +477,6 @@ try:
|
|
| 438 |
evaluation=False,
|
| 439 |
)
|
| 440 |
|
| 441 |
-
# Ensure raw-audio inference can access BEATs even when
|
| 442 |
-
# the checkpoint configuration uses precomputed embeddings.
|
| 443 |
-
model.pretrained_model = pretrained
|
| 444 |
-
|
| 445 |
-
# Load the trained detector weights.
|
| 446 |
-
state_dict = ckpt.get("state_dict")
|
| 447 |
-
|
| 448 |
-
if not isinstance(state_dict, dict):
|
| 449 |
-
raise ValueError(
|
| 450 |
-
"The checkpoint does not contain a valid state_dict."
|
| 451 |
-
)
|
| 452 |
-
|
| 453 |
-
incompat = model.load_state_dict(
|
| 454 |
-
state_dict,
|
| 455 |
-
strict=False,
|
| 456 |
-
)
|
| 457 |
-
|
| 458 |
-
print("Checkpoint loaded.")
|
| 459 |
-
print(
|
| 460 |
-
"Missing state-dict keys:",
|
| 461 |
-
len(incompat.missing_keys),
|
| 462 |
-
)
|
| 463 |
-
print(
|
| 464 |
-
"Unexpected state-dict keys:",
|
| 465 |
-
len(incompat.unexpected_keys),
|
| 466 |
-
)
|
| 467 |
-
|
| 468 |
model.eval()
|
| 469 |
|
| 470 |
# Quick consistency check.
|
|
@@ -493,45 +505,18 @@ finally:
|
|
| 493 |
# 6. Inference helpers
|
| 494 |
# --------------------------------------------------
|
| 495 |
def compute_embeddings(wav):
|
| 496 |
-
"""
|
| 497 |
-
Extract frame-level BEATs embeddings.
|
| 498 |
|
| 499 |
-
|
| 500 |
-
|
| 501 |
"""
|
| 502 |
-
helper = getattr(model, "get_embeddings", None)
|
| 503 |
-
|
| 504 |
-
if callable(helper):
|
| 505 |
-
try:
|
| 506 |
-
return helper(wav)
|
| 507 |
-
except TypeError:
|
| 508 |
-
pass
|
| 509 |
-
|
| 510 |
-
pretrained_model = getattr(
|
| 511 |
-
model,
|
| 512 |
-
"pretrained_model",
|
| 513 |
-
None,
|
| 514 |
-
)
|
| 515 |
-
|
| 516 |
if pretrained_model is None:
|
| 517 |
-
raise RuntimeError(
|
| 518 |
-
"The model has no pretrained BEATs encoder."
|
| 519 |
-
)
|
| 520 |
|
| 521 |
-
|
| 522 |
-
|
| 523 |
-
|
| 524 |
-
)
|
| 525 |
-
|
| 526 |
-
result = pretrained_model.extract_features(
|
| 527 |
-
wav,
|
| 528 |
-
padding_mask=padding_mask,
|
| 529 |
-
)
|
| 530 |
-
|
| 531 |
-
if isinstance(result, (tuple, list)):
|
| 532 |
-
return result[0]
|
| 533 |
-
|
| 534 |
-
return result
|
| 535 |
|
| 536 |
|
| 537 |
def forward_strong(wav):
|
|
@@ -558,32 +543,14 @@ def forward_strong(wav):
|
|
| 558 |
takes_embeddings = True
|
| 559 |
|
| 560 |
if takes_embeddings:
|
| 561 |
-
|
| 562 |
-
|
| 563 |
-
|
| 564 |
-
for candidate in (
|
| 565 |
-
embeddings,
|
| 566 |
-
embeddings.transpose(1, 2),
|
| 567 |
-
):
|
| 568 |
-
try:
|
| 569 |
-
result = detect(
|
| 570 |
-
feats,
|
| 571 |
-
net,
|
| 572 |
-
embeddings=candidate,
|
| 573 |
-
)
|
| 574 |
-
|
| 575 |
-
break
|
| 576 |
-
|
| 577 |
-
except Exception as exc:
|
| 578 |
-
last_error = exc
|
| 579 |
-
|
| 580 |
-
else:
|
| 581 |
-
raise RuntimeError(
|
| 582 |
-
"Detector failed with both embedding layouts."
|
| 583 |
-
) from last_error
|
| 584 |
-
|
| 585 |
else:
|
| 586 |
-
|
|
|
|
|
|
|
|
|
|
| 587 |
|
| 588 |
# Some recipe versions return (strong_predictions, other_output).
|
| 589 |
strong = (
|
|
@@ -662,6 +629,8 @@ def process_audio(audio_file, threshold):
|
|
| 662 |
)
|
| 663 |
|
| 664 |
model.to(device)
|
|
|
|
|
|
|
| 665 |
|
| 666 |
# Load mono audio at 16 kHz.
|
| 667 |
wav, _ = librosa.load(
|
|
@@ -787,6 +756,11 @@ def process_audio(audio_file, threshold):
|
|
| 787 |
model.to("cpu")
|
| 788 |
except Exception:
|
| 789 |
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 790 |
|
| 791 |
|
| 792 |
# --------------------------------------------------
|
|
|
|
| 383 |
# 5. Load the model on CPU
|
| 384 |
# --------------------------------------------------
|
| 385 |
model = None
|
| 386 |
+
pretrained_model = None # Kept separate so its weights are not part of the SED checkpoint.
|
| 387 |
encoder = None
|
| 388 |
LABELS = []
|
| 389 |
LOAD_ERROR = None
|
|
|
|
| 399 |
|
| 400 |
config = load_config(ckpt)
|
| 401 |
|
| 402 |
+
if config.get("pretrained", {}).get("e2e", False):
|
| 403 |
+
raise ValueError(
|
| 404 |
+
"This app currently targets the official DCASE 2024 baseline "
|
| 405 |
+
"with pretrained.e2e=False and precomputed BEATs embeddings."
|
| 406 |
+
)
|
| 407 |
+
|
| 408 |
# Build the correctly ordered 27-class encoder.
|
| 409 |
encoder = build_encoder(config)
|
| 410 |
LABELS = list(encoder.labels)
|
|
|
|
| 412 |
# Build the CRNN student model.
|
| 413 |
sed_student = CRNN(**config["net"])
|
| 414 |
|
| 415 |
+
# Load only the trained student weights. The official DCASE recipe saves
|
| 416 |
+
# this submodule separately in checkpoint["sed_student"]. Loading the
|
| 417 |
+
# entire Lightning state_dict with strict=False can silently leave the
|
| 418 |
+
# detector partly uninitialized, especially across recipe revisions.
|
| 419 |
+
student_state = ckpt.get("sed_student")
|
| 420 |
+
|
| 421 |
+
if not isinstance(student_state, dict):
|
| 422 |
+
full_state = ckpt.get("state_dict", {})
|
| 423 |
+
if isinstance(full_state, dict):
|
| 424 |
+
prefixes = ("sed_student.", "module.sed_student.")
|
| 425 |
+
for prefix in prefixes:
|
| 426 |
+
extracted = {
|
| 427 |
+
key[len(prefix):]: value
|
| 428 |
+
for key, value in full_state.items()
|
| 429 |
+
if key.startswith(prefix)
|
| 430 |
+
}
|
| 431 |
+
if extracted:
|
| 432 |
+
student_state = extracted
|
| 433 |
+
break
|
| 434 |
+
|
| 435 |
+
if not isinstance(student_state, dict) or not student_state:
|
| 436 |
+
raise ValueError(
|
| 437 |
+
"Could not find trained student weights in checkpoint. "
|
| 438 |
+
"Expected checkpoint['sed_student'] or keys prefixed with 'sed_student.'."
|
| 439 |
+
)
|
| 440 |
+
|
| 441 |
+
# Strict loading is intentional: fail rather than serve random/partial weights.
|
| 442 |
+
sed_student.load_state_dict(student_state, strict=True)
|
| 443 |
+
print(
|
| 444 |
+
f"Loaded student checkpoint strictly ({len(student_state)} tensors)."
|
| 445 |
+
)
|
| 446 |
+
|
| 447 |
+
# Load BEATs separately for online waveform embedding extraction.
|
| 448 |
+
# The official DCASE 2024 baseline uses precomputed BEATs embeddings
|
| 449 |
+
# (pretrained.e2e=False), so BEATs must not be attached to the SEDTask4
|
| 450 |
+
# module or included in its checkpoint state_dict.
|
| 451 |
beats_path = ensure_beats_checkpoint()
|
| 452 |
beats_ckpt = torch_load(beats_path)
|
| 453 |
|
| 454 |
beats_cfg = BEATsConfig(beats_ckpt["cfg"])
|
| 455 |
+
pretrained_model = BEATs(beats_cfg)
|
| 456 |
+
pretrained_model.load_state_dict(beats_ckpt["model"], strict=True)
|
| 457 |
+
pretrained_model.eval()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 458 |
|
| 459 |
+
for parameter in pretrained_model.parameters():
|
| 460 |
parameter.requires_grad = False
|
| 461 |
|
| 462 |
+
# Construct the DCASE detector. It deep-copies the already loaded student
|
| 463 |
+
# into sed_teacher; inference below uses sed_student only.
|
| 464 |
model = call_with_supported_kwargs(
|
| 465 |
SEDTask4,
|
| 466 |
hparams=config,
|
| 467 |
encoder=encoder,
|
| 468 |
sed_student=sed_student,
|
| 469 |
+
pretrained_model=None,
|
| 470 |
opt=None,
|
| 471 |
train_data=None,
|
| 472 |
valid_data=None,
|
|
|
|
| 477 |
evaluation=False,
|
| 478 |
)
|
| 479 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 480 |
model.eval()
|
| 481 |
|
| 482 |
# Quick consistency check.
|
|
|
|
| 505 |
# 6. Inference helpers
|
| 506 |
# --------------------------------------------------
|
| 507 |
def compute_embeddings(wav):
|
| 508 |
+
"""Extract frame-level embeddings in the format used by DCASE's BEATsModel.
|
|
|
|
| 509 |
|
| 510 |
+
Input waveform: [batch, samples].
|
| 511 |
+
Return embeddings: [batch, embedding_channels, frames].
|
| 512 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 513 |
if pretrained_model is None:
|
| 514 |
+
raise RuntimeError("The BEATs encoder has not been loaded.")
|
|
|
|
|
|
|
| 515 |
|
| 516 |
+
# Match local/beats/BEATs.py -> BEATsModel.forward():
|
| 517 |
+
# BEATs returns [batch, frames, 768]; the detector expects [batch, 768, frames].
|
| 518 |
+
features, _ = pretrained_model.extract_features(wav)
|
| 519 |
+
return features.transpose(1, 2).float()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 520 |
|
| 521 |
|
| 522 |
def forward_strong(wav):
|
|
|
|
| 543 |
takes_embeddings = True
|
| 544 |
|
| 545 |
if takes_embeddings:
|
| 546 |
+
# compute_embeddings already returns [batch, 768, frames], which is
|
| 547 |
+
# the orientation of the saved DCASE frame_embeddings arrays.
|
| 548 |
+
result = detect(feats, net, embeddings=embeddings)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 549 |
else:
|
| 550 |
+
raise RuntimeError(
|
| 551 |
+
"This DCASE detect() method does not accept embeddings; "
|
| 552 |
+
"the current checkpoint requires BEATs embeddings."
|
| 553 |
+
)
|
| 554 |
|
| 555 |
# Some recipe versions return (strong_predictions, other_output).
|
| 556 |
strong = (
|
|
|
|
| 629 |
)
|
| 630 |
|
| 631 |
model.to(device)
|
| 632 |
+
if pretrained_model is not None:
|
| 633 |
+
pretrained_model.to(device)
|
| 634 |
|
| 635 |
# Load mono audio at 16 kHz.
|
| 636 |
wav, _ = librosa.load(
|
|
|
|
| 756 |
model.to("cpu")
|
| 757 |
except Exception:
|
| 758 |
pass
|
| 759 |
+
if pretrained_model is not None:
|
| 760 |
+
try:
|
| 761 |
+
pretrained_model.to("cpu")
|
| 762 |
+
except Exception:
|
| 763 |
+
pass
|
| 764 |
|
| 765 |
|
| 766 |
# --------------------------------------------------
|