| |
| """Etape 1/2 : produit les listes N-BEST des deux langues, avec leurs scores |
| actuels, pour pouvoir les re-classer ensuite par un LM MASQUE BIDIRECTIONNEL. |
| |
| Pourquoi cette piste : arXiv 2606.23306 teste 11 methodes INTERNES au CTC pour |
| recuperer l'ecart a l'oracle (MBR sur posterieurs, MC-dropout, decodage |
| contrastif, rescoreurs entraines) -- aucune n'est significative. La seule qui |
| marche est un posterieur issu d'un MLM bidirectionnel EXTERNE. Cela explique nos |
| propres refutations (moyennage de logits, ensembles medoide, LM neuronal causal). |
| Et notre plus gros ecart a l'oracle est cote shona (oracle 10-best 0.0949 vs |
| 0.1281 retenu) : le goulot y est la SELECTION, exactement ce qu'un rescoreur corrige. |
| |
| lin : beam KenLM alpha=0.6 beta=1.0 lsb=True beam=64 (config du RECORD) |
| sna : beam CTC pur 256 + rescorage sna_r2 w=4.0 (config du RECORD) |
| Les scores sauves sont EXACTEMENT ceux qui produisent le record, pour que |
| lambda=0 reproduise le record bit pour bit (controle de non-regression). |
| """ |
| import csv, json, os, pickle, sys |
| import numpy as np, soundfile as sf, torch |
| from multiprocessing import Pool |
| from pyctcdecode import build_ctcdecoder |
| from transformers import AutoModelForCTC, AutoProcessor |
|
|
| sys.path.insert(0, "/root") |
| from gen_sna_rescore import ctc_score, encode_for, norm |
|
|
| BASE = "/root/sub_LMA06.csv" |
| NBEST = 24 |
| OUT = "/scratch/nbest_all.pkl" |
|
|
| base = {r["ID"]: r["Target"] for r in csv.DictReader(open(BASE, encoding="utf-8"))} |
| lang = json.load(open("/root/test_lang.json")) |
| lin_ids = [k for k in base if lang.get(k) == "lin"] |
| sna_ids = [k for k in base if lang.get(k) == "sna"] |
| print("lin %d | sna %d" % (len(lin_ids), len(sna_ids)), flush=True) |
|
|
| store = {} |
|
|
| |
| with open("/scratch/lm/logits_test_lin.pkl", "rb") as f: |
| keys, logs, greedy = pickle.load(f) |
| tokL = AutoProcessor.from_pretrained("/root/models/joint_cont_best").tokenizer |
| v = tokL.get_vocab() |
| labL = [None] * len(v) |
| for t, i in v.items(): |
| labL[i] = t |
| labL[tokL.word_delimiter_token_id] = " " |
| labL[tokL.unk_token_id] = "⁇" |
| labL[tokL.pad_token_id] = "" |
| dec = build_ctcdecoder(labL, kenlm_model_path="/scratch/lm/lin_5g.arpa", |
| alpha=0.6, beta=1.0, lm_score_boundary=True) |
| with Pool(8) as p: |
| beams = dec.decode_beams_batch(p, logs, beam_width=64, prune_history=True) |
| gmap = dict(zip(keys, greedy)) |
| lin = {} |
| for k, bs in zip(keys, beams): |
| cands, scores = [], [] |
| for b in bs[:NBEST]: |
| h = norm(b[0]) |
| g = gmap[k] |
| if h and g: |
| h = g[:1] + h[1:] |
| if h and h not in cands: |
| cands.append(h); scores.append(float(b[3])) |
| lin[k] = (cands, scores) |
| n_ok = sum(1 for k in lin if lin[k][0] and lin[k][0][int(np.argmax(lin[k][1]))] == base[k]) |
| print("lin n-best OK | argmax reproduit le record sur %d/%d clips" % (n_ok, len(lin)), flush=True) |
| store["lin"] = lin |
| del logs, beams, dec |
|
|
| |
| files = [os.path.join("/scratch/p2_16k", k + ".wav") for k in sna_ids] |
| proc = AutoProcessor.from_pretrained("/root/models/sna_ps_best") |
| m = AutoModelForCTC.from_pretrained("/root/models/sna_ps_best", dtype=torch.float32).cuda().eval() |
| L1 = [] |
| with torch.inference_mode(): |
| for i in range(0, len(files), 4): |
| au = [sf.read(f, dtype="float32")[0] for f in files[i:i + 4]] |
| x = proc(au, sampling_rate=16000, return_tensors="pt", padding=True) |
| x = {kk: vv.cuda() for kk, vv in x.items()} |
| lg = m(**x).logits.log_softmax(-1).float().cpu().numpy() |
| for j in range(len(au)): |
| L1.append(lg[j]) |
| del m; torch.cuda.empty_cache() |
| tokS = proc.tokenizer |
| v = tokS.get_vocab() |
| labS = [None] * len(v) |
| for t, i in v.items(): |
| labS[i] = t |
| labS[tokS.word_delimiter_token_id] = " " |
| labS[tokS.unk_token_id] = "⁇" |
| labS[tokS.pad_token_id] = "" |
| greedyS = [norm(tokS.decode(l.argmax(-1))) for l in L1] |
| decS = build_ctcdecoder(labS) |
| with Pool(8) as p: |
| allb = decS.decode_beams_batch(p, L1, beam_width=256) |
| with Pool(8) as p: |
| dbS = [norm(x) for x in decS.decode_batch(p, L1, beam_width=256)] |
| print("beam sna OK", flush=True) |
|
|
| procR = AutoProcessor.from_pretrained("/scratch/restore/sna_r2_best") |
| mR = AutoModelForCTC.from_pretrained("/scratch/restore/sna_r2_best", dtype=torch.float32).cuda().eval() |
| LG = [] |
| with torch.inference_mode(): |
| for i in range(0, len(files), 4): |
| au = [sf.read(f, dtype="float32")[0] for f in files[i:i + 4]] |
| x = procR(au, sampling_rate=16000, return_tensors="pt", padding=True) |
| x = {kk: vv.cuda() for kk, vv in x.items()} |
| lg = mR(**x).logits.log_softmax(-1).float().cpu().numpy() |
| for j in range(len(au)): |
| LG.append(lg[j]) |
| del mR; torch.cuda.empty_cache() |
| t2 = procR.tokenizer |
| print("rescoreur sna OK", flush=True) |
|
|
| sna = {} |
| for i, k in enumerate(sna_ids): |
| bs = allb[i] |
| c = [norm(b[0]) for b in bs[:50]] |
| a = [(b[2] if len(b) > 3 else 0.0) for b in bs[:50]] |
| for extra in (dbS[i], greedyS[i]): |
| if extra and extra not in c: |
| c.append(extra) |
| a.append(ctc_score(L1[i], encode_for(tokS, extra), tokS.pad_token_id)) |
| resc = np.array([ctc_score(LG[i], encode_for(t2, x), t2.pad_token_id) for x in c]) |
| tot = np.array(a) + 4.0 * resc |
| order = np.argsort(-tot)[:NBEST] |
| sna[k] = ([c[j] for j in order], [float(tot[j]) for j in order]) |
| n_ok = sum(1 for k in sna if sna[k][0] and sna[k][0][0] == base[k]) |
| print("sna n-best OK | argmax reproduit le record sur %d/%d clips" % (n_ok, len(sna)), flush=True) |
| store["sna"] = sna |
|
|
| with open(OUT, "wb") as f: |
| pickle.dump(store, f, protocol=4) |
| tot_h = sum(len(v[0]) for L in store.values() for v in L.values()) |
| print("NBEST_DONE %s | %d hypotheses au total" % (OUT, tot_h), flush=True) |
|
|