Spaces:
Running on Zero
Running on Zero
| """ | |
| Find the optimal loudness-gate threshold WITHOUT any new transcription run. | |
| Key idea: the gated pipeline's output for each channel is already known: | |
| - channel active-RMS >= gate -> identical to Phase 1 (results_channels/, no PP) | |
| - channel active-RMS < gate -> identical to Phase 2 (results_channels_pp/, full PP) | |
| (high-pass is near-neutral on this data, so "above gate" ~ Phase 1 is exact enough.) | |
| So we assemble each call per-channel from the two existing result sets according | |
| to a candidate gate, score it, and sweep the gate to find the threshold that | |
| keeps the quiet-channel gains while removing the over-amplification regressions. | |
| This is pure analysis over cached results -> runs in seconds, no model, no CPU run. | |
| Usage: | |
| python simulate_gate.py | |
| python simulate_gate.py --gate -26 # per-call detail at one gate | |
| """ | |
| import os | |
| import json | |
| import argparse | |
| import numpy as np | |
| import soundfile as sf | |
| import jiwer | |
| from eval_common import normalise | |
| from audio_preprocess import highpass, active_rms | |
| DATA = r"d:\Desktop\ai-ml-capstone\data\na_testset" | |
| P1 = "results_channels" # no preprocessing (Phase 1) | |
| P2 = "results_channels_pp" # full preprocessing (Phase 2, target -20 dBFS) | |
| SR = 16000 | |
| def load_result(root, accent, cid): | |
| with open(os.path.join(DATA, root, accent, cid + ".json"), encoding="utf-8") as f: | |
| return json.load(f) | |
| def channel_rms_db(wav_rel): | |
| a, sr = sf.read(os.path.join(DATA, wav_rel), dtype="float32") | |
| if a.ndim > 1: | |
| a = a.mean(axis=1) | |
| a = highpass(a, sr) # match preprocess(): measure post-highpass | |
| return 20 * np.log10(active_rms(a, sr) + 1e-12) | |
| def wer(ref, hyp): | |
| r, h = normalise(ref), normalise(hyp) | |
| n = len(r.split()) | |
| if n == 0: | |
| return 0.0, 0 | |
| return jiwer.wer(r, h), n | |
| def text(words): | |
| return " ".join(w["word"] for w in words) | |
| def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--gate", type=float, default=None, help="show per-call detail at this gate") | |
| ap.add_argument("--sweep", nargs="*", type=float, | |
| default=[-99, -34, -32, -30, -28, -26, -24, -22, -20, 0]) | |
| args = ap.parse_args() | |
| with open(os.path.join(DATA, "manifest.json"), encoding="utf-8") as f: | |
| manifest = {m["call_id"]: m for m in json.load(f)} | |
| with open(os.path.join(DATA, "probe_set.json"), encoding="utf-8") as f: | |
| probe = json.load(f)["calls"] | |
| # Pre-load everything once: refs, both result sets, per-channel RMS. | |
| calls = [] | |
| for p in probe: | |
| cid, accent = p["call_id"], p["accent"] | |
| m = manifest[cid] | |
| calls.append({ | |
| "cid": cid, "tier": p["tier"], | |
| "ref_a": m["agent_transcript"], "ref_c": m["customer_transcript"], | |
| "rms_a": channel_rms_db(m["agent_wav"]), | |
| "rms_c": channel_rms_db(m["customer_wav"]), | |
| "p1_a": text(load_result(P1, accent, cid)["agent"]), | |
| "p1_c": text(load_result(P1, accent, cid)["customer"]), | |
| "p2_a": text(load_result(P2, accent, cid)["agent"]), | |
| "p2_c": text(load_result(P2, accent, cid)["customer"]), | |
| }) | |
| def score_at(gate): | |
| """Word-weighted accuracy across probe at this gate, + per-call accs + #boosted.""" | |
| num = den = 0 | |
| per_call = {} | |
| boosted = 0 | |
| for c in calls: | |
| ha = c["p2_a"] if c["rms_a"] < gate else c["p1_a"] | |
| hc = c["p2_c"] if c["rms_c"] < gate else c["p1_c"] | |
| boosted += (c["rms_a"] < gate) + (c["rms_c"] < gate) | |
| wa, na = wer(c["ref_a"], ha) | |
| wc, nc = wer(c["ref_c"], hc) | |
| cw = (wa * na + wc * nc) / (na + nc) | |
| per_call[c["cid"]] = (1 - cw) * 100 | |
| num += wa * na + wc * nc | |
| den += na + nc | |
| return (1 - num / den) * 100, per_call, boosted | |
| # ββ Gate sweep ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| print("Gate sweep (accuracy assembled from existing P1/P2 results, no re-run):\n") | |
| print(f" {'gate':>6} {'boosted':>7} {'overall':>8} {'Bank_1586157':>12} {'Health_1587175':>14}") | |
| print(" " + "-" * 56) | |
| reg_cid, quiet_cid = "en_US_General_Banking_1586157", "en_US_General_Health_1587175" | |
| for g in args.sweep: | |
| acc, per_call, boosted = score_at(g) | |
| label = {-99: "none", 0: "all"}.get(g, f"{g:.0f}") | |
| print(f" {label:>6} {boosted:>7} {acc:>7.1f}% {per_call[reg_cid]:>11.1f}% {per_call[quiet_cid]:>13.1f}%") | |
| print("\n ('none' = pure Phase 1 / no PP; 'all' = pure Phase 2 / full PP)") | |
| print(" Bank_1586157 = the regression call; Health_1587175 = a quiet-channel call") | |
| # ββ Per-call detail at one gate βββββββββββββββββββββββββββββββββββββββββββ | |
| if args.gate is not None: | |
| acc, per_call, boosted = score_at(args.gate) | |
| print(f"\nPer-call at gate = {args.gate} dBFS (overall {acc:.1f}%, {boosted} channels boosted):\n") | |
| print(f" {'tier':<13} {'call_id':<32} {'rmsA':>6} {'rmsC':>6} {'boost':>9} {'acc':>6}") | |
| print(" " + "-" * 74) | |
| for c in calls: | |
| ba = "A" if c["rms_a"] < args.gate else "-" | |
| bc = "C" if c["rms_c"] < args.gate else "-" | |
| print(f" {c['tier']:<13} {c['cid']:<32} {c['rms_a']:>6.1f} {c['rms_c']:>6.1f} " | |
| f"{ba+bc:>9} {per_call[c['cid']]:>5.1f}%") | |
| if __name__ == "__main__": | |
| main() | |