rupkotha / finetune /purity.py
Deb
rupkotha_1st_commit
f655146
Raw
History Blame Contribute Delete
3.15 kB
# finetune/purity.py
"""Bengali script-purity quality gate for teacher labels.
The teacher (Gemma) writes mostly clean Bengali, but occasionally code-switches
(e.g. a stray Latin or Cyrillic word — we saw `зеленая` leak once). Distillation
caps the student at the label quality, so we filter/repair bad labels before
training. This is the gate referenced in the Bengali-quality investigation.
Heuristics only — no model needed. A human Bengali speaker should still spot-check
a sample of what passes.
"""
import re
import unicodedata
# Bengali Unicode block: U+0980–U+09FF.
_BENGALI = re.compile(r"[ঀ-৿]")
# Letters from other scripts that must NOT appear in a Bengali story body.
_LATIN = re.compile(r"[A-Za-z]")
_CYRILLIC = re.compile(r"[Ѐ-ӿ]")
# Allowed non-letter noise: digits, whitespace, common punctuation, emoji, danda.
_ALLOWED_NONLETTER = re.compile(r"[\s\d\.,!?…\"'“”‘’—\-–—:;()।॥☀-➿\U0001F300-\U0001FAFF]")
def script_stats(text: str) -> dict:
"""Counts of Bengali vs foreign letters and the Bengali-letter ratio."""
text = unicodedata.normalize("NFC", text or "")
bengali = len(_BENGALI.findall(text))
latin = len(_LATIN.findall(text))
cyrillic = len(_CYRILLIC.findall(text))
letters = bengali + latin + cyrillic
ratio = (bengali / letters) if letters else 0.0
return {
"bengali": bengali,
"latin": latin,
"cyrillic": cyrillic,
"foreign": latin + cyrillic,
"bengali_ratio": round(ratio, 4),
}
def is_clean(
text: str,
min_bengali_ratio: float = 0.98,
max_foreign_letters: int = 0,
min_length: int = 40,
) -> tuple[bool, dict]:
"""Decide whether a teacher label is clean enough to train on.
Defaults are strict: essentially zero foreign-script letters. Loosen
max_foreign_letters to 1–2 if you'd rather repair than drop.
Returns (ok, stats).
"""
stats = script_stats(text)
ok = (
len((text or "").strip()) >= min_length
and stats["foreign"] <= max_foreign_letters
and stats["bengali_ratio"] >= min_bengali_ratio
)
return ok, stats
def foreign_words(text: str) -> list[str]:
"""Return whitespace tokens that contain any Latin/Cyrillic letter — useful
for eyeballing exactly what leaked (e.g. ['зеленая'])."""
out = []
for tok in (text or "").split():
if _LATIN.search(tok) or _CYRILLIC.search(tok):
out.append(tok)
return out
if __name__ == "__main__":
samples = {
"good": "আচ্ছা রূপা, চোখ বুজে নাও। চাঁদমামা হাসছে, পুকুরের ধারে ঘাস দুলছে। শুভরাত্রি।",
"leak": "দেখছো зеленая গোল বলটা? ওটা রূপার প্রিয় খেলনা সবুজ আপেল!",
"english": "Once upon a time there was a small red house under the sun.",
}
for name, s in samples.items():
ok, stats = is_clean(s)
print(f"{name:8} ok={ok!s:5} {stats} leaks={foreign_words(s)}")