Spaces:
Running on Zero
Running on Zero
| """ | |
| Aggregate WER evaluation over the NA test set. | |
| Reads manifest.json (references) + results/*/*.json (hypotheses), computes | |
| per-call agent/customer WER (raw + Whisper-normalised), then aggregates: | |
| - per-accent mean WER and accuracy | |
| - overall (word-weighted) WER and accuracy | |
| - distribution: median, worst calls | |
| Usage: | |
| python evaluate_batch.py | |
| python evaluate_batch.py --csv results_summary.csv | |
| """ | |
| import os | |
| import json | |
| import argparse | |
| import statistics | |
| import jiwer | |
| from collections import defaultdict | |
| from eval_common import normalise, normalise_raw | |
| DATA_DIR = r"d:\Desktop\ai-ml-capstone\data\na_testset" | |
| MANIFEST = os.path.join(DATA_DIR, "manifest.json") | |
| RESULTS = os.path.join(DATA_DIR, "results") | |
| def weighted_wer(ref, hyp, norm_fn): | |
| r, h = norm_fn(ref), norm_fn(hyp) | |
| n = len(r.split()) | |
| if n == 0: | |
| return None, 0 | |
| return jiwer.wer(r, h), n | |
| def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--csv", default=None) | |
| args = ap.parse_args() | |
| with open(MANIFEST, encoding="utf-8") as f: | |
| manifest = {m["call_id"]: m for m in json.load(f)} | |
| rows = [] # per-call records | |
| for accent_dir in sorted(os.listdir(RESULTS)) if os.path.isdir(RESULTS) else []: | |
| adir = os.path.join(RESULTS, accent_dir) | |
| if not os.path.isdir(adir): | |
| continue | |
| for fn in sorted(os.listdir(adir)): | |
| if not fn.endswith(".json"): | |
| continue | |
| cid = fn[:-5] | |
| if cid not in manifest: | |
| continue | |
| with open(os.path.join(adir, fn), encoding="utf-8") as f: | |
| hyp = json.load(f) | |
| m = manifest[cid] | |
| ha = " ".join(w["word"] for w in hyp["agent"]) | |
| hc = " ".join(w["word"] for w in hyp["customer"]) | |
| rec = {"call_id": cid, "accent": m["accent"], "domain": m["domain"]} | |
| for tag, norm_fn in [("raw", normalise_raw), ("norm", normalise)]: | |
| wa, na = weighted_wer(m["agent_transcript"], ha, norm_fn) | |
| wc, nc = weighted_wer(m["customer_transcript"], hc, norm_fn) | |
| tot_n = na + nc | |
| tot_w = ((wa or 0) * na + (wc or 0) * nc) / tot_n if tot_n else None | |
| rec[f"{tag}_wer"] = tot_w | |
| rec[f"{tag}_words"] = tot_n | |
| rows.append(rec) | |
| if not rows: | |
| print("No results found. Run run_batch.py first.") | |
| return | |
| # ββ Per-accent aggregation (word-weighted) ββββββββββββββββββββββββββββββββ | |
| print(f"Evaluated {len(rows)} calls\n") | |
| print(f"{'Accent':<16} {'Calls':>5} {'RawAcc':>8} {'NormAcc':>8} {'NormWER':>8} {'MedWER':>8}") | |
| print("-" * 60) | |
| def agg(subset, tag): | |
| num = sum(r[f"{tag}_wer"] * r[f"{tag}_words"] for r in subset) | |
| den = sum(r[f"{tag}_words"] for r in subset) | |
| return num / den if den else None | |
| by_accent = defaultdict(list) | |
| for r in rows: | |
| by_accent[r["accent"]].append(r) | |
| for accent in sorted(by_accent): | |
| s = by_accent[accent] | |
| raw_w = agg(s, "raw") | |
| norm_w = agg(s, "norm") | |
| med = statistics.median([r["norm_wer"] for r in s]) | |
| print(f"{accent:<16} {len(s):>5} {(1-raw_w)*100:>7.1f}% {(1-norm_w)*100:>7.1f}% " | |
| f"{norm_w*100:>7.1f}% {med*100:>7.1f}%") | |
| raw_all = agg(rows, "raw") | |
| norm_all = agg(rows, "norm") | |
| print("-" * 60) | |
| print(f"{'OVERALL':<16} {len(rows):>5} {(1-raw_all)*100:>7.1f}% {(1-norm_all)*100:>7.1f}% " | |
| f"{norm_all*100:>7.1f}%") | |
| print(f"\nTarget: >= 90% normalised accuracy -> " | |
| f"{'PASS' if (1-norm_all) >= 0.90 else 'NEEDS WORK'}") | |
| # ββ Worst 5 calls (normalised) ββββββββββββββββββββββββββββββββββββββββββββ | |
| worst = sorted(rows, key=lambda r: r["norm_wer"], reverse=True)[:5] | |
| print("\nWorst 5 calls (normalised WER):") | |
| for r in worst: | |
| print(f" {r['accent']:<14} {r['call_id']:<32} {r['norm_wer']*100:5.1f}% ({r['domain']})") | |
| if args.csv: | |
| import csv | |
| with open(args.csv, "w", newline="", encoding="utf-8") as f: | |
| w = csv.DictWriter(f, fieldnames=list(rows[0].keys())) | |
| w.writeheader() | |
| w.writerows(rows) | |
| print(f"\nPer-call CSV written: {args.csv}") | |
| if __name__ == "__main__": | |
| main() | |