Buckets:
| """Shared evaluation for the Sigma VO jobs: two non-Whisper ASR engines (NVIDIA Parakeet-TDT-0.6B-v2 via onnx-asr, | |
| Useful Sensors Moonshine-base) + CommonAccent accent ID. WER is computed after light normalisation (numbers, UK/US | |
| spelling, punctuation) and an explicit alias table for proper nouns an ASR vocabulary cannot know.""" | |
| import re, os, numpy as np, torch, librosa, warnings | |
| warnings.filterwarnings('ignore') | |
| DEV = 'cuda' if torch.cuda.is_available() else 'cpu' | |
| _P = _M = _A = None | |
| def to16(a, sr): | |
| a = np.asarray(a, dtype=np.float32).reshape(-1) | |
| return librosa.resample(a, orig_sr=sr, target_sr=16000).astype(np.float32) if sr != 16000 else a | |
| def parakeet(a16): | |
| global _P | |
| if _P is None: | |
| import onnx_asr | |
| from huggingface_hub import snapshot_download | |
| d = snapshot_download('istupakov/parakeet-tdt-0.6b-v2-onnx', local_dir='/tmp/parakeet') | |
| _P = onnx_asr.load_model('nemo-parakeet-tdt-0.6b-v2', path=d) | |
| return _P.recognize(a16, sample_rate=16000) | |
| def moonshine(a16): | |
| global _M | |
| if _M is None: | |
| from transformers import AutoProcessor, MoonshineForConditionalGeneration | |
| pr = AutoProcessor.from_pretrained('UsefulSensors/moonshine-base') | |
| md = MoonshineForConditionalGeneration.from_pretrained('UsefulSensors/moonshine-base').to(DEV).eval() | |
| _M = (pr, md) | |
| pr, md = _M | |
| inp = pr(a16, sampling_rate=16000, return_tensors='pt').to(DEV) | |
| with torch.no_grad(): ids = md.generate(**inp, max_new_tokens=int(len(a16) / 16000 * 6.5) + 10) | |
| return pr.decode(ids[0], skip_special_tokens=True) | |
| def accent(a16): | |
| global _A | |
| if _A is None: | |
| from speechbrain.inference.classifiers import EncoderClassifier | |
| _A = EncoderClassifier.from_hparams(source='Jzuluaga/accent-id-commonaccent_ecapa', savedir='/tmp/accent', run_opts={'device': DEV}) | |
| out = _A.classify_batch(torch.tensor(a16)[None].to(DEV)) | |
| lp = out[0][0] | |
| pr = torch.softmax(lp, -1) if lp.max() > 0 else lp.exp() | |
| labs = _A.hparams.label_encoder.decode_ndim(torch.arange(pr.numel())) | |
| d = {l: float(p) for l, p in zip(labs, pr.tolist())} | |
| top = max(d, key=d.get) | |
| return d.get('indian', 0.0), top | |
| ONES = 'zero one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen'.split() | |
| TENS = 'x x twenty thirty forty fifty sixty seventy eighty ninety'.split() | |
| def n2w(n): | |
| n = int(n) | |
| if n < 20: return ONES[n] | |
| if n < 100: return TENS[n // 10] + ('' if n % 10 == 0 else ' ' + ONES[n % 10]) | |
| if n < 1000: return ONES[n // 100] + ' hundred' + ('' if n % 100 == 0 else ' ' + n2w(n % 100)) | |
| for d, w in ((10**9, 'billion'), (10**6, 'million'), (1000, 'thousand')): | |
| if n >= d: return n2w(n // d) + ' ' + w + ('' if n % d == 0 else ' ' + n2w(n % d)) | |
| SPELL = {'defense': 'defence', 'program': 'programme', 'programs': 'programmes', 'soul': 'sole'} | |
| ALIAS = { | |
| 'mtar': ['m tar', 'em tar', 'emtar', 'emptar', 'mtaar', 'm taar', 'emtaar', 'em taar', 'amtar', 'emtaar\'s'], | |
| 'nadcap': ['nad cap', 'nadkap', 'nad kap', 'natcap', 'nat cap', 'nadcaap', 'nadcamp'], | |
| 'octans': ['octan s', 'octanes', 'octants'], | |
| 'drdo': ['d r d o'], 'hal': ['h a l'], 'us': ['u s'], 'emi': ['e m i'], | |
| } | |
| def norm(s): | |
| s = s.lower().replace('’', "'").replace('&', ' and ') | |
| s = re.sub(r"(\d),(\d)", r"\1\2", s) | |
| s = re.sub(r"\b(\d+)\b", lambda m: n2w(m.group(1)), s) | |
| s = s.replace('-', ' ').replace('.', ' ') | |
| s = re.sub(r"[^a-z' ]", ' ', s) | |
| s = re.sub(r"'s\b", 's', s).replace("'", '') | |
| s = re.sub(r'\s+', ' ', s).strip() | |
| s = re.sub(r'\ba hundred\b', 'one hundred', s) | |
| s = ' '.join(SPELL.get(w, w) for w in s.split()) | |
| for canon, al in ALIAS.items(): | |
| for x in sorted(al, key=len, reverse=True): s = re.sub(r'\b' + re.escape(x) + r'\b', canon, s) | |
| s = re.sub(r'\bm and a\b', 'manda', s); s = re.sub(r'\bm a\b', 'manda', s); s = re.sub(r'\bmna\b', 'manda', s) | |
| s = re.sub(r'\bdrdos\b', 'drdos', s) | |
| return s | |
| def wer(ref, hyp): | |
| r, h = norm(ref).split(), norm(hyp).split() | |
| d = np.zeros((len(r) + 1, len(h) + 1), int); d[:, 0] = range(len(r) + 1); d[0, :] = range(len(h) + 1) | |
| for i in range(1, len(r) + 1): | |
| for j in range(1, len(h) + 1): d[i, j] = min(d[i-1, j] + 1, d[i, j-1] + 1, d[i-1, j-1] + (r[i-1] != h[j-1])) | |
| return float(d[-1, -1] / max(1, len(r))) | |
| def trim(a, sr, thr=0.012, pad=0.04): | |
| e = np.abs(a) > thr * np.max(np.abs(a) + 1e-9) * 3 | |
| if not e.any(): return a | |
| i0 = max(0, np.argmax(e) - int(pad * sr)); i1 = len(a) - max(0, np.argmax(e[::-1]) - int(pad * 2.5 * sr)) | |
| return a[i0:i1] | |
| def f0med(a16): | |
| try: | |
| f0, v, p = librosa.pyin(a16, fmin=60, fmax=300, sr=16000) | |
| f0 = f0[~np.isnan(f0)]; return float(np.median(f0)) if len(f0) else 0.0 | |
| except Exception: return 0.0 | |
| def evaluate(a, sr, ref): | |
| a16 = to16(a, sr) | |
| p = parakeet(a16); m = moonshine(a16); ind, top = accent(a16) | |
| return dict(parakeet=p, moonshine=m, wer_p=wer(ref, p), wer_m=wer(ref, m), indian=ind, accent=top, dur=len(a) / sr) | |
Xet Storage Details
- Size:
- 5.07 kB
- Xet hash:
- a5d56479ba71ea4332c79f20cd44ce55b721b8c8f892a868e5cd080837686f04
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.