| |
| """Generateur de soumission parametrable : lin = <modele> + beam KenLM, sna = sna_ps greedy. |
| Casse du 1er caractere toujours copiee du greedy (gain valide). |
| Usage : MODEL=... ARPA=... ALPHA=.. BETA=.. LSB=0|1 OUT=... python gen_sub.py |
| """ |
| import csv |
| import glob |
| import json |
| import os |
|
|
| import soundfile as sf |
| import torch |
| from multiprocessing import Pool |
| from pyctcdecode import build_ctcdecoder |
| from transformers import AutoModelForCTC, AutoProcessor |
|
|
| SR = 16000 |
| CACHE = "/scratch/p2_16k" |
| LINM = os.environ.get("MODEL", "/root/models/joint_cont_best") |
| ARPA = os.environ.get("ARPA", "/scratch/lm/lin_5g.arpa") |
| ALPHA = float(os.environ.get("ALPHA", "0.5")) |
| BETA = float(os.environ.get("BETA", "0.5")) |
| LSB = os.environ.get("LSB", "0") == "1" |
| OUT = os.environ.get("OUT", "/root/sub_gen.csv") |
|
|
|
|
| def norm(s): |
| return " ".join(str(s).replace("|", " ").split()) |
|
|
|
|
| def batches(sel, budget): |
| durs = {f: sf.info(f).duration for f in sel} |
| sel = sorted(sel, key=lambda f: -durs[f]) |
| bs, cur, acc = [], [], 0.0 |
| for f in sel: |
| if cur and acc + durs[f] > budget: |
| bs.append(cur) |
| cur, acc = [], 0.0 |
| cur.append(f) |
| acc += durs[f] |
| if cur: |
| bs.append(cur) |
| return bs |
|
|
|
|
| def main(): |
| lang = json.load(open(os.environ.get("LANGF", "/root/test_lang.json"))) |
| files = sorted(glob.glob(os.path.join(CACHE, "*.wav"))) |
| ids = [os.path.splitext(os.path.basename(f))[0] for f in files] |
| lin = [f for f in files if lang[os.path.splitext(os.path.basename(f))[0]] == "lin"] |
| sna = [f for f in files if lang[os.path.splitext(os.path.basename(f))[0]] == "sna"] |
| print("lin=%d (%s + KenLM a=%.2f b=%.2f lsb=%s) | sna=%d (sna_ps greedy)" |
| % (len(lin), os.path.basename(LINM), ALPHA, BETA, LSB, len(sna)), flush=True) |
| out = {} |
|
|
| |
| proc = AutoProcessor.from_pretrained(LINM) |
| tok = proc.tokenizer |
| m = AutoModelForCTC.from_pretrained(LINM, dtype=torch.float32).cuda().eval() |
| logs, order, greedy = [], [], [] |
| with torch.inference_mode(): |
| for b in batches(lin, 90): |
| au = [sf.read(f, dtype="float32")[0] for f in b] |
| x = proc(au, sampling_rate=SR, return_tensors="pt", padding=True) |
| x = {k: v.cuda() for k, v in x.items()} |
| lg = m(**x).logits.log_softmax(-1).float().cpu().numpy() |
| for j, f in enumerate(b): |
| logs.append(lg[j]) |
| order.append(f) |
| greedy.append(norm(tok.decode(lg[j].argmax(-1)))) |
| del m |
| torch.cuda.empty_cache() |
|
|
| v = tok.get_vocab() |
| lab = [None] * len(v) |
| for t, i in v.items(): |
| lab[i] = t |
| lab[tok.word_delimiter_token_id] = " " |
| lab[tok.unk_token_id] = "⁇" |
| lab[tok.pad_token_id] = "" |
| dec = build_ctcdecoder(lab, kenlm_model_path=ARPA, alpha=ALPHA, beta=BETA, |
| lm_score_boundary=LSB) |
| with Pool(8) as p: |
| hyps = dec.decode_batch(p, logs, beam_width=64) |
| for f, h, g in zip(order, hyps, greedy): |
| h = norm(h) |
| if h and g: |
| h = g[:1] + h[1:] |
| out[os.path.splitext(os.path.basename(f))[0]] = h |
| print("lin OK", flush=True) |
|
|
| |
| proc2 = AutoProcessor.from_pretrained("/root/models/sna_ps_best") |
| m2 = AutoModelForCTC.from_pretrained("/root/models/sna_ps_best", |
| dtype=torch.bfloat16).cuda().eval() |
| with torch.inference_mode(): |
| for b in batches(sna, 140): |
| au = [sf.read(f, dtype="float32")[0] for f in b] |
| x = proc2(au, sampling_rate=SR, return_tensors="pt", padding=True) |
| x = {k: v.to("cuda", dtype=torch.bfloat16 if v.dtype == torch.float32 else v.dtype) |
| for k, v in x.items()} |
| pid = m2(**x).logits.float().argmax(-1).cpu().numpy() |
| for f, s in zip(b, proc2.batch_decode(pid)): |
| out[os.path.splitext(os.path.basename(f))[0]] = norm(s) |
| del m2 |
| torch.cuda.empty_cache() |
| print("sna OK", flush=True) |
|
|
| |
| fb = {} |
| ref = "/root/sub_p2_KENLM.csv" |
| if os.path.exists(ref): |
| fb = {r["ID"]: r["Target"] for r in csv.DictReader(open(ref, encoding="utf-8"))} |
| filled = 0 |
| for i in ids: |
| if not out.get(i, "").strip() and fb.get(i, "").strip(): |
| out[i] = fb[i] |
| filled += 1 |
| with open(OUT, "w", newline="", encoding="utf-8") as fo: |
| w = csv.writer(fo) |
| w.writerow(["ID", "Target"]) |
| for i in ids: |
| w.writerow([i, out.get(i) or "a"]) |
| empty = sum(1 for i in ids if not out.get(i, "").strip()) |
| print("GEN_DONE %s | %d IDs | vides=%d | combles=%d" % (OUT, len(ids), empty, filled), flush=True) |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|