oddadmix's picture
Upload normalize.py with huggingface_hub
aaa46f4 verified
Raw
History Blame Contribute Delete
4.38 kB
"""Text cleaning / normalization for Arabic multi-dialect ASR.
Design goals for the `oddadmix/dialectal-arabic-lahgtna-v2-*` data:
* Strip tashkil (harakat), dagger-alef and tatweel -> the target is
undiacritized text.
* Remove non-verbal annotation tags like ``[laughter]``, ``[exhale]``,
``[inhale]``, ``[mumble]`` etc. (anything in square brackets).
* Collapse whitespace.
Deliberately *kept*, because this is a multi-dialect corpus and these carry
real information:
* Dialectal consonants گ ڨ چ پ ژ ڥ ... (Maghrebi / Iraqi / Gulf spelling).
* Sentence punctuation ، ؟ ! . ... (Whisper can and should predict it).
* Latin letters / digits that appear inside transcripts.
Aggressive letter folding (أإآ->ا, ى->ي, ة->ه) is available via
``normalize_letters=True`` but is OFF by default — for a general multi-dialect
model it tends to hurt real-world output. If you enable it, enable it for BOTH
training targets and evaluation references so WER stays consistent.
"""
from __future__ import annotations
import re
import unicodedata
# --- Arabic diacritics / marks to remove -----------------------------------
# Harakat + tanwin + shadda + sukun + maddah + combining hamza (064B-0652),
# extra combining marks (0653-065F), superscript "dagger" alef (0670),
# Arabic small high/low Quranic marks (06D6-06ED), and honorific marks
# (0610-061A). Tatweel/kashida (0640) is elongation-only and also dropped.
_DIACRITICS = (
"ؐ-ؚ" # Arabic honorific / small high marks
"ً-ٟ" # harakat, tanwin, shadda, sukun, combining hamza, ...
"ٰ" # superscript (dagger) alef
"ۖ-ۜ" # small high Quranic marks
"۟-ۨ"
"۪-ۭ"
)
_DIACRITICS_RE = re.compile(f"[{_DIACRITICS}]")
_TATWEEL_RE = re.compile("ـ+")
# Bracketed non-verbal tags: [laughter], [exhale], [ mumble ], [noise], ...
_BRACKET_TAG_RE = re.compile(r"\[[^\]]*\]")
# Occasionally annotators use <...> or (( )) style tags; strip those too.
_ANGLE_TAG_RE = re.compile(r"<[^>]*>")
# Zero-width / bidi control characters that sometimes ride along in Arabic text.
_ZERO_WIDTH_RE = re.compile("[​-‏‪-‮⁦-⁩]")
_WS_RE = re.compile(r"\s+")
# --- Optional letter folding (OFF by default) ------------------------------
_LETTER_FOLD = {
"أ": "ا", "إ": "ا", "آ": "ا", "ٱ": "ا",
"ى": "ي",
"ؤ": "و",
"ئ": "ي",
"ة": "ه",
"گ": "ك", "ك": "ك",
}
def remove_diacritics(text: str) -> str:
text = _DIACRITICS_RE.sub("", text)
text = _TATWEEL_RE.sub("", text)
return text
def remove_nonverbal_tags(text: str) -> str:
text = _BRACKET_TAG_RE.sub(" ", text)
text = _ANGLE_TAG_RE.sub(" ", text)
return text
def clean_text(text: str, normalize_letters: bool = False) -> str:
"""Full cleaning pipeline. Returns '' if nothing usable remains."""
if not text:
return ""
# Canonical form first, so precomposed vs. decomposed diacritics behave.
text = unicodedata.normalize("NFC", text)
text = _ZERO_WIDTH_RE.sub("", text)
text = remove_nonverbal_tags(text)
text = remove_diacritics(text)
if normalize_letters:
text = "".join(_LETTER_FOLD.get(ch, ch) for ch in text)
text = _WS_RE.sub(" ", text).strip()
return text
# --- self test -------------------------------------------------------------
_SAMPLES = [
"طريقة زوجي، كي رجعت ليه ... عايشين [inhale]",
"مَدارِسْ تقريبًا، وْمُستشفىٰ وَاحِدة فقط [exhale] وكانت الخدمات.",
"شُو فُهُّم عِند الجَمَال؟ ... طلبية كبيرة؟ [laughter]",
"هذيك الشمس خرجت وماذا بيا [mumble] [exhale]",
"المسؤول اللجنة انه يخليه، گال له اني محتاج هذا الراتب", # keep گ
"و كانْ مانْقْدروشْ ... ما عَنْدي حاجة. ڨالي و كانْ [exhale]", # keep ڨ
"[laughter]", # -> becomes empty
"فهَدول الإشياء الكتير مهمين", # multiple spaces
]
if __name__ == "__main__":
import sys
fold = "--fold" in sys.argv
print(f"clean_text(normalize_letters={fold})\n" + "=" * 60)
for s in _SAMPLES:
out = clean_text(s, normalize_letters=fold)
print(f"IN : {s}")
print(f"OUT: {out!r}")
print("-" * 60)