voidful commited on
Commit
f70a24d
·
verified ·
1 Parent(s): 8b6f80e

Warm CPU acoustic verifiers

Browse files
Files changed (3) hide show
  1. README.md +5 -4
  2. app.py +30 -6
  3. tests/test_inference_cleanup.py +5 -3
README.md CHANGED
@@ -335,10 +335,11 @@ Median-F0 只在 multi-chunk lattice 真的出現兩條以上可選路徑、必
335
  時,才對 retained DP candidates 延遲計算一次;單一路徑、single/K32、semantic reject、
336
  joined 與 final waveform 不再支付無用的 pYIN latency。真正進入多路徑排序時,pYIN 演算法與
337
  transition score 定義沒有變更。
338
- Pinned ECAPA release speaker encoder 會在 Space 啟動階段於 CPU 完成載入,並以固定、非語音、
339
- 不保存的合成波形各跑一次 local-batchrelease-third dry-run;因此第一次公開合成不再承擔
340
- SpeechBrain import、權重初始化、encoder 建構及第一次 CPU kernel/page-fault 的冷啟動成本。
341
- 正式請求仍重新 encode 真實 waveform;warmup 不寫入 request cache,也不改 speaker gate 數值。
 
342
 
343
  Generation work 以實際產生的 chunks 與其 speech units 累計:初始 trajectory 花費
344
  `chunk count` 與全文 speech units;每次 refill 再加 1 chunk 與該段 units。任一 request 最多
 
335
  時,才對 retained DP candidates 延遲計算一次;單一路徑、single/K32、semantic reject、
336
  joined 與 final waveform 不再支付無用的 pYIN latency。真正進入多路徑排序時,pYIN 演算法與
337
  transition score 定義沒有變更。
338
+ Pinned ECAPA、SQUIM pYIN runtime 會在 Space 啟動階段於 CPU 完成載入,並以固定、非語音、
339
+ 不保存的合成波形執行 local-batchrelease-third 與 acoustic dry-run;因此第一次公開合成
340
+ 不再承擔 SpeechBrain import、權重初始化、encoder 建構及第一次 CPU kernel/page-fault/JIT
341
+ 的冷啟動成本。正式請求仍真實 waveform 重跑所有量測;warmup 不寫入 request cache,
342
+ 也不改 speaker、SQUIM、F0 transition 或 echo gate 數值。
343
 
344
  Generation work 以實際產生的 chunks 與其 speech units 累計:初始 trajectory 花費
345
  `chunk count` 與全文 speech units;每次 refill 再加 1 chunk 與該段 units。任一 request 最多
app.py CHANGED
@@ -564,10 +564,10 @@ def _get_ecapa_encoder():
564
  return _ECAPA_ENCODER
565
 
566
 
567
- def _preload_release_speaker_runtime() -> None:
568
- """Move deterministic CPU speaker load and first forwards out of requests."""
569
 
570
- print("[BlueMagpie] preloading release speaker verifier on CPU ...")
571
  encoder = _get_ecapa_encoder()
572
  if encoder is None:
573
  raise RuntimeError("release speaker verifier preload failed")
@@ -575,7 +575,8 @@ def _preload_release_speaker_runtime() -> None:
575
  timeline = np.arange(sample_count, dtype=np.float64) / float(SR)
576
  warmup_waveform = np.asarray(
577
  0.08 * np.sin(2.0 * np.pi * 180.0 * timeline)
578
- + 0.03 * np.sin(2.0 * np.pi * 263.0 * timeline),
 
579
  dtype=np.float32,
580
  )
581
  anchor = _speaker_anchor_array(DEFAULT_CENTROID)
@@ -601,7 +602,30 @@ def _preload_release_speaker_runtime() -> None:
601
  for evidence in warmup_evidence
602
  ):
603
  raise RuntimeError("release speaker verifier warmup failed")
604
- print("[BlueMagpie] release speaker verifier ready")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
605
 
606
 
607
  def _apply_speed(
@@ -982,7 +1006,7 @@ def _speaker_anchor_array(centroid: torch.Tensor) -> np.ndarray:
982
  return np.asarray(anchor / norm, dtype=np.float32)
983
 
984
 
985
- _preload_release_speaker_runtime()
986
 
987
 
988
  def _generate_trajectory(
 
564
  return _ECAPA_ENCODER
565
 
566
 
567
+ def _preload_release_cpu_runtime() -> None:
568
+ """Move deterministic CPU verifier loads and first forwards out of requests."""
569
 
570
+ print("[BlueMagpie] preloading release CPU verifiers ...")
571
  encoder = _get_ecapa_encoder()
572
  if encoder is None:
573
  raise RuntimeError("release speaker verifier preload failed")
 
575
  timeline = np.arange(sample_count, dtype=np.float64) / float(SR)
576
  warmup_waveform = np.asarray(
577
  0.08 * np.sin(2.0 * np.pi * 180.0 * timeline)
578
+ + 0.04 * np.sin(2.0 * np.pi * 360.0 * timeline)
579
+ + 0.02 * np.sin(2.0 * np.pi * 540.0 * timeline),
580
  dtype=np.float32,
581
  )
582
  anchor = _speaker_anchor_array(DEFAULT_CENTROID)
 
602
  for evidence in warmup_evidence
603
  ):
604
  raise RuntimeError("release speaker verifier warmup failed")
605
+ squim_evidence = squim_objective_evidence_from_audio(
606
+ warmup_waveform,
607
+ SR,
608
+ )
609
+ if not all(
610
+ np.isfinite(value)
611
+ for value in (
612
+ squim_evidence.stoi,
613
+ squim_evidence.pesq,
614
+ squim_evidence.si_sdr,
615
+ )
616
+ ):
617
+ raise RuntimeError("release SQUIM verifier warmup failed")
618
+ median_f0_hz = active_audio_median_f0_hz(
619
+ warmup_waveform,
620
+ SR,
621
+ )
622
+ if (
623
+ median_f0_hz is None
624
+ or not np.isfinite(median_f0_hz)
625
+ or median_f0_hz < 1.0
626
+ ):
627
+ raise RuntimeError("release F0 transition runtime warmup failed")
628
+ print("[BlueMagpie] release CPU verifiers ready")
629
 
630
 
631
  def _apply_speed(
 
1006
  return np.asarray(anchor / norm, dtype=np.float32)
1007
 
1008
 
1009
+ _preload_release_cpu_runtime()
1010
 
1011
 
1012
  def _generate_trajectory(
tests/test_inference_cleanup.py CHANGED
@@ -633,7 +633,7 @@ def test_space_defers_transition_f0_to_actual_multi_path_ranking():
633
  assert "_apply_speed(\n audio,\n final_speed" in generate
634
 
635
 
636
- def test_space_preloads_release_speaker_runtime_before_serving_requests():
637
  source = (ROOT / "app.py").read_text(encoding="utf-8")
638
  tree = ast.parse(source)
639
  functions = {
@@ -647,15 +647,17 @@ def test_space_preloads_release_speaker_runtime_before_serving_requests():
647
  if isinstance(node, ast.Expr)
648
  and isinstance(node.value, ast.Call)
649
  and isinstance(node.value.func, ast.Name)
650
- and node.value.func.id == "_preload_release_speaker_runtime"
651
  ]
652
 
653
  assert len(preload_calls) == 1
654
  assert preload_calls[0].lineno > functions["_speaker_anchor_array"].end_lineno
655
  preload_source = ast.get_source_segment(
656
  source,
657
- functions["_preload_release_speaker_runtime"],
658
  )
659
  assert "speaker_evidence_from_audio(" in preload_source
660
  assert "release_speaker_evidence_from_audio(" in preload_source
 
 
661
  assert "np.arange(sample_count" in preload_source
 
633
  assert "_apply_speed(\n audio,\n final_speed" in generate
634
 
635
 
636
+ def test_space_preloads_release_cpu_runtime_before_serving_requests():
637
  source = (ROOT / "app.py").read_text(encoding="utf-8")
638
  tree = ast.parse(source)
639
  functions = {
 
647
  if isinstance(node, ast.Expr)
648
  and isinstance(node.value, ast.Call)
649
  and isinstance(node.value.func, ast.Name)
650
+ and node.value.func.id == "_preload_release_cpu_runtime"
651
  ]
652
 
653
  assert len(preload_calls) == 1
654
  assert preload_calls[0].lineno > functions["_speaker_anchor_array"].end_lineno
655
  preload_source = ast.get_source_segment(
656
  source,
657
+ functions["_preload_release_cpu_runtime"],
658
  )
659
  assert "speaker_evidence_from_audio(" in preload_source
660
  assert "release_speaker_evidence_from_audio(" in preload_source
661
+ assert "squim_objective_evidence_from_audio(" in preload_source
662
+ assert "active_audio_median_f0_hz(" in preload_source
663
  assert "np.arange(sample_count" in preload_source