Automatic Speech Recognition
NeMo
Thai
English
speech
streaming
Thai
FastConformer
Transducer
NeMo
Nemotron
Instructions to use iapp/OpenThai2.0-ASR-Streaming with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- NeMo
How to use iapp/OpenThai2.0-ASR-Streaming with NeMo:
import nemo.collections.asr as nemo_asr asr_model = nemo_asr.models.ASRModel.from_pretrained("iapp/OpenThai2.0-ASR-Streaming") transcriptions = asr_model.transcribe(["file.wav"]) - Notebooks
- Google Colab
- Kaggle
File size: 3,638 Bytes
0a9a98f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | #!/usr/bin/env python3
"""Score Thai ASR output the way the OpenThai2.0-ASR-Streaming model card does.
python score.py refs.jsonl hyps_A.jsonl [hyps_B.jsonl] [--boot 1000]
refs.jsonl one line per clip: {"id": ..., "text": <reference>, "cluster": <video/recording/speaker/meeting id>}
(build it from the public dataset; the ids we used are the ones in hypotheses/*.jsonl)
hyps_*.jsonl one line per clip: {"id": ..., "pred_text": <model output>}; every reference id must be present
Prints the character error rate (CER, %) of A, and with B the paired difference A - B with a 95% bootstrap interval
that resamples whole clusters. Normalisation, applied identically to both sides:
chinda_norm.normalize(text, "th") Thai digits to Arabic, curly quotes to ASCII, SARA AM decomposed, spaces between
two Thai characters removed (chinda_norm.py)
num_norm.normalize_numbers spoken Thai numbers to digits, on whole pythainlp newmm tokens (num_norm.py)
then every space is removed, and CER = Levenshtein edits / reference characters over the whole set.
Requires: pythainlp (5.3.5 used for the card) and editdistance.
"""
import argparse, json, os, random, sys
from collections import defaultdict
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import editdistance # noqa: E402
from chinda_norm import normalize # noqa: E402
from num_norm import normalize_numbers # noqa: E402
def prep(t):
return normalize_numbers(normalize(t or "", "th")).replace(" ", "")
def load(path, key):
return {str(r["id"]): r[key] for r in map(json.loads, open(path, encoding="utf-8")) if r}
def main():
ap = argparse.ArgumentParser()
ap.add_argument("refs"); ap.add_argument("a"); ap.add_argument("b", nargs="?")
ap.add_argument("--boot", type=int, default=1000)
a = ap.parse_args()
refs = {str(r["id"]): r for r in map(json.loads, open(a.refs, encoding="utf-8")) if r}
# Take the clip order from hypothesis file A (ours are in the order we decoded them): the bootstrap draws
# clusters in order of first appearance, so this reproduces the intervals on the model card exactly.
order = [str(r["id"]) for r in map(json.loads, open(a.a, encoding="utf-8")) if r]
seen = set(order)
ids = [i for i in order if i in refs] + [i for i in refs if i not in seen]
refs = [refs[i] for i in ids]
R = [prep(r["text"]) for r in refs]
cl = [str(r.get("cluster", r["id"])) for r in refs]
def errs(path):
h = load(path, "pred_text")
missing = [i for i in ids if i not in h]
if missing:
sys.exit(f"{path}: {len(missing)} reference ids missing (first: {missing[0]})")
return [(editdistance.eval(r, prep(h[i])), len(r)) for i, r in zip(ids, R)]
A = errs(a.a)
cer = lambda E: 100 * sum(e for e, _ in E) / sum(n for _, n in E)
print(f"A: CER {cer(A):.2f}% ({len(ids)} clips, {len(set(cl))} clusters)")
if not a.b:
return
B = errs(a.b)
print(f"B: CER {cer(B):.2f}%")
groups = defaultdict(list)
for i, c in enumerate(cl):
groups[c].append(i)
G = list(groups.values()); rnd = random.Random(1); ds = []
for _ in range(a.boot):
idx = [i for _ in G for i in G[rnd.randrange(len(G))]]
ds.append(cer([A[i] for i in idx]) - cer([B[i] for i in idx]))
ds.sort(); lo, hi = ds[int(.025 * a.boot)], ds[int(.975 * a.boot)]
verdict = "A lower" if hi < 0 else "B lower" if lo > 0 else "tie"
print(f"A - B: {cer(A) - cer(B):+.2f} 95% interval [{lo:+.2f}, {hi:+.2f}] -> {verdict}")
if __name__ == "__main__":
main()
|