waxal2026-backup / code /error_mine.py
Pricile's picture
compactage apres suppression luganda
6eed659
Raw
History Blame Contribute Delete
4.85 kB
#!/usr/bin/env python3
"""Fouille d'erreurs sur les hypotheses champion (banc 4235 clips, refs connues).
Chaque piste n'est retenue que si son gain NET sur la validation complete est positif :
A. normalisation apostrophes/typographie B. paires de substitution frequentes
C. majuscules systematiques (noms propres) D. chiffres vs lettres E. hygiene CSV."""
import csv
import json
import unicodedata
from collections import Counter
import jiwer
MAP = json.load(open("/scratch/rehearsal/mapping.json", encoding="utf-8"))
HYPS = {r["ID"]: r["Target"] for r in csv.DictReader(open("/root/subs/sub_rehearsal_final.csv", encoding="utf-8"))}
LANGS = ["lin", "lug", "sna"]
def norm(t):
return " ".join(str(t).replace("|", " ").split())
def pairs_for(lang):
rs, hs = [], []
for anon, info in MAP.items():
if info["true_lang"] == lang and info["text"].strip():
rs.append(norm(info["text"]))
hs.append(norm(HYPS.get(anon, "")))
return rs, hs
def sc(rs, hs):
return 0.5 * jiwer.wer(rs, hs) + 0.5 * jiwer.cer(rs, hs)
def apply_map(hs, table):
out = []
for h in hs:
out.append(" ".join(table.get(w, w) for w in h.split()))
return out
print("=== E. hygiene CSV ===", flush=True)
for name in ("sub_public.csv", "sub_phase2.csv", "sub_rehearsal_final.csv"):
rows = list(csv.DictReader(open(f"/root/subs/{name}", encoding="utf-8")))
bad = sum(1 for r in rows if "�" in r["Target"] or "Ã" in r["Target"])
empt = sum(1 for r in rows if not r["Target"].strip())
lens = sorted(len(r["Target"].split()) for r in rows)
print(f"{name}: {len(rows)} lignes | mojibake {bad} | vides {empt} | mots p50/p95 {lens[len(lens)//2]}/{lens[int(len(lens)*0.95)]}", flush=True)
CHAR_VARIANTS = [("’", "'"), ("‘", "'"), ("`", "'"), ("“", '"'), ("”", '"')]
for lang in LANGS:
rs, hs = pairs_for(lang)
base = sc(rs, hs)
print(f"\n===== {lang} | baseline banc {base:.4f} ({len(rs)} clips) =====", flush=True)
print("--- A. typographie ---", flush=True)
rjoin, hjoin = " ".join(rs), " ".join(hs)
for ch, repl in CHAR_VARIANTS:
cr, chh = rjoin.count(ch), hjoin.count(ch)
if cr or chh:
print(f" car {ch!r}: refs {cr} vs hyps {chh}", flush=True)
hs2 = [h.translate(str.maketrans({"’": "'", "‘": "'", "`": "'"})) for h in hs]
s2 = sc(rs, hs2)
if s2 < base:
print(f" normalisation apostrophes: {base:.4f} -> {s2:.4f} <== MIEUX", flush=True)
else:
print(f" normalisation apostrophes: {s2 - base:+.5f} (neutre/negatif)", flush=True)
print("--- B. paires de substitution ---", flush=True)
sub = Counter()
for r, h in zip(rs, hs):
rw, hw = r.split(), h.split()
ops = jiwer.process_words([r], [h])
for al in ops.alignments[0]:
if al.type == "substitute":
for i, j in zip(range(al.ref_start_idx, al.ref_end_idx), range(al.hyp_start_idx, al.hyp_end_idx)):
sub[(hw[j], rw[i])] += 1
cands = [(h, r, n) for (h, r), n in sub.most_common(60) if n >= 8 and h.lower() != r.lower()]
print(f" {len(cands)} paires candidates (>=8 occ.)", flush=True)
kept = {}
cur = list(hs)
curs = base
for h, r, n in cands:
trial = apply_map(cur, {h: r})
st = sc(rs, trial)
if st < curs - 1e-6:
kept[h] = r
cur, curs = trial, st
print(f" paires retenues: {len(kept)} | banc {base:.4f} -> {curs:.4f} ({curs - base:+.4f})", flush=True)
if kept:
print(" ex:", dict(list(kept.items())[:8]), flush=True)
print("--- C. majuscules systematiques ---", flush=True)
wc, wcap = Counter(), Counter()
for l in open(f"/scratch/prep/manifests/waxal_{lang}_train.jsonl", encoding="utf-8"):
for w in norm(json.loads(l)["text"]).split():
core = w.strip(".,;:!?\"'")
if len(core) >= 3:
wc[core.lower()] += 1
if core[:1].isupper():
wcap[core.lower()] += 1
always_cap = {w for w in wc if wc[w] >= 5 and wcap[w] / wc[w] >= 0.95}
table = {}
for h in hs:
for w in h.split():
core = w.strip(".,;:!?\"'")
if core.lower() in always_cap and core[:1].islower():
table[w] = w.replace(core, core[:1].upper() + core[1:], 1)
hs3 = apply_map(hs, table)
s3 = sc(rs, hs3)
print(f" {len(always_cap)} mots toujours-capitalises train | {len(table)} formes corrigees | {base:.4f} -> {s3:.4f} ({s3 - base:+.4f})", flush=True)
print("--- D. chiffres ---", flush=True)
rdig = sum(any(c.isdigit() for c in r) for r in rs)
hdig = sum(any(c.isdigit() for c in h) for h in hs)
print(f" refs avec chiffres: {rdig}/{len(rs)} | hyps avec chiffres: {hdig}", flush=True)
print("\nMINE_DONE", flush=True)