docs(parakeet): seat A/B vs parakeet-unified-en-0.6b - latency is the int8-on-CPU runtime; unified wins WER
A/B of the live STT seat (fv-ml1 GPU 0, sherpa-onnx int8 v3) against nvidia/parakeet-unified-en-0.6b, measured on GPU 3 with the seat's own image, k2-fsa's published unified int8 export, fp32/fp16 exports made with k2-fsa's recipe, v2 int8, and NeMo 3.0.0 (fp32, bf16 autocast, bf16 weights). - Seat int8 graph runs on one CPU thread (cpu/wall 1.00, GPU 2-9%). - unified-en under NeMo: -121/-234/-530 ms vs the seat at 1-3/3-8/8-20 s (paired, n=120/bin; floor <=6 ms; +50 ms positive control reads +52-54). - unified-en WER lower in every runtime: -0.7 pp clean, -1.5 pp other, -3.2 to -4.4 pp AMI (paired CIs exclude 0). - Seat defects found: hard 400 s input ceiling (HTTP 500), truncation after a quiet 1.5 s pause, and severe long-window dropouts (int8 v3 only). - B-bf16w needs +0.8 to +1.5 GB over the seat's 1,690 MiB on GPU 0. Raw requests, hypotheses, manifests and the full harness under services/parakeet-ab-2026-09-30/. No deploy; live seat untouched apart from 240 light test requests.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
"""Cut the two public long-form files into ~6-minute windows that the seat can actually take whole.
|
||||
|
||||
The seat's ONNX graph has a hard ceiling of 5,000 encoder frames (pos_emb_max_len 5000 -> a 9,999-wide
|
||||
relative-position table) = 400 s; anything longer returns HTTP 500. Windows: nominal boundaries every
|
||||
360 s, each moved to the widest gap between consecutive ground-truth word starts within +-10 s (a pause),
|
||||
cut at the middle of that gap; every window must be < 395 s. Ground truth per window = the timed GT tokens
|
||||
whose start time falls inside it (the investigation's gt/<file>.timed.json, unchanged).
|
||||
Writes data/long/<file>-w<k>.wav and data/long.jsonl {id, file, k, t0, t1, dur, wav, ref_tokens}.
|
||||
"""
|
||||
import json
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
|
||||
OFFSET = float(sys.argv[1]) if len(sys.argv) > 1 else 0.0 # first cut at OFFSET s (second placement)
|
||||
NAME = sys.argv[2] if len(sys.argv) > 2 else "long"
|
||||
import soundfile as sf
|
||||
|
||||
ROOT = "/tank/spikes/scriberr-slicer"
|
||||
OUT = "/tank/spikes/parakeet-ab/data"
|
||||
rows = []
|
||||
for f in ("wilde", "scotus"):
|
||||
tg = json.load(open(f"{ROOT}/gt/{f}.timed.json"))
|
||||
tok, tim = tg["tokens"], tg["times"]
|
||||
a, sr = sf.read(f"{ROOT}/public/{f}.wav", dtype="float32")
|
||||
total = len(a) / sr
|
||||
starts = np.asarray(tim, float)
|
||||
gaps = [(starts[i + 1] - starts[i], (starts[i] + starts[i + 1]) / 2) for i in range(len(starts) - 1)]
|
||||
cuts = [0.0]
|
||||
k = 1
|
||||
while total - cuts[-1] > 395:
|
||||
nominal = cuts[-1] + (OFFSET if (OFFSET and len(cuts) == 1) else 360)
|
||||
cand = [(g, m) for g, m in gaps if abs(m - nominal) <= 10]
|
||||
cut = max(cand)[1] if cand else nominal
|
||||
cuts.append(cut)
|
||||
k += 1
|
||||
cuts.append(total)
|
||||
for k, (t0, t1) in enumerate(zip(cuts, cuts[1:])):
|
||||
assert t1 - t0 < 395, (f, k, t1 - t0)
|
||||
seg = a[int(t0 * sr):int(t1 * sr)]
|
||||
wav = f"{OUT}/{NAME}/{f}-w{k}.wav"
|
||||
import os
|
||||
os.makedirs(f"{OUT}/{NAME}", exist_ok=True)
|
||||
sf.write(wav, seg, sr, subtype="PCM_16")
|
||||
ref = [t for t, s in zip(tok, tim) if t0 <= s < t1]
|
||||
rows.append(dict(id=f"{f}-w{k}", file=f, k=k, t0=round(t0, 2), t1=round(t1, 2), dur=round(t1 - t0, 2),
|
||||
wav=wav, ref_tokens=ref))
|
||||
print(f, k, round(t0, 1), round(t1, 1), "dur", round(t1 - t0, 1), "ref words", len(ref))
|
||||
with open(f"{OUT}/{NAME}.jsonl", "w") as fo:
|
||||
for r in rows:
|
||||
fo.write(json.dumps(r) + "\n")
|
||||
Reference in New Issue
Block a user