kranthichand/sigma-vo / code /eval_lib.py
kranthichand's picture
download
raw
5.07 kB
"""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.