Download rmit_hackathon_2026/src/preprocessing.py from 0ewik/test: direct link, hf CLI and curl.
- Browser
- Download file 10.3 kB
-
https://huggingface.co/0ewik/test/resolve/main/rmit_hackathon_2026/src/preprocessing.py
- Command line
-
hf download hf://0ewik/test/rmit_hackathon_2026/src/preprocessing.py
-
curl -L -o preprocessing.py https://huggingface.co/0ewik/test/resolve/main/rmit_hackathon_2026/src/preprocessing.py
10.3 kB
| """ | |
| Preprocessing module — Member B (Dũng) | |
| Chuẩn hóa ký tự, khử nhiễu (de-obfuscation) và trích xuất đặc trưng bảng | |
| """ | |
| import re | |
| import unicodedata | |
| from typing import Callable, Optional | |
| # --------------------------------------------------------------------------- | |
| # Basic text cleaning & de-obfuscation | |
| # --------------------------------------------------------------------------- | |
| LEET_DICT = { | |
| "0": "o", "1": "i", "3": "e", "4": "a", | |
| "5": "s", "7": "t", "@": "a", "$": "s", | |
| } | |
| ZERO_WIDTH_CHARS = ["\u200b", "\u200c", "\u200d", "\ufeff", "\u00ad"] | |
| def remove_zero_width(text: str) -> str: | |
| """Xoa cac ky tu tang hinh (Zero-width joiners, soft hyphens...).""" | |
| for zwc in ZERO_WIDTH_CHARS: | |
| text = text.replace(zwc, "") | |
| return text | |
| def normalize_unicode(text: str) -> str: | |
| """Dua cac ky tu 'giong nhau ve nghia' ve mot dang thong nhat (NFKC).""" | |
| return unicodedata.normalize("NFKC", text) | |
| def decode_leetspeak(text: str) -> str: | |
| """Chuyen cac ky tu leetspeak pho bien ve ky tu Latin thong thuong.""" | |
| for k, v in LEET_DICT.items(): | |
| text = text.replace(k, v) | |
| return text | |
| def clean_and_deobfuscate_text(text: str) -> str: | |
| """ | |
| Pipeline chuan hoa van ban dau vao: | |
| 1. Chuan hoa dang bieu dien Unicode (NFC). | |
| 2. Loai bo cac ky tu dieu khien an (Zero-width characters). | |
| 3. Giai ma leetspeak. | |
| 4. Chuan hoa khoang trang du thua. | |
| """ | |
| if not isinstance(text, str): | |
| return "" | |
| text = unicodedata.normalize("NFC", text) | |
| text = remove_zero_width(text) | |
| text = decode_leetspeak(text) | |
| text = re.sub(r"\s+", " ", text).strip() | |
| return text | |
| # --------------------------------------------------------------------------- | |
| # Code-switching, Teencode & Low-Resource Language (LRL) Heuristics | |
| # --------------------------------------------------------------------------- | |
| COMMON_VIET_SLANG_CODE_SWITCH = { | |
| # Common English-Vietnamese code-switching phrases | |
| "ok không": "được không", | |
| "ok ko": "được không", | |
| "ok k": "được không", | |
| "check giùm": "kiểm tra giúp", | |
| "check hộ": "kiểm tra giúp", | |
| "bypass được": "vượt rào được", | |
| "bypass đc": "vượt rào được", | |
| "pass đc": "vượt qua được", | |
| "fix bug": "sửa lỗi", | |
| "delete hộ": "xóa giúp", | |
| "hack nick": "chiếm đoạt tài khoản", | |
| "share code": "chia sẻ mã nguồn", | |
| "gen text": "tạo văn bản", | |
| "drop database": "xóa cơ sở dữ liệu", | |
| # Common Vietnamese teencode | |
| "ko": "không", "k": "không", "hok": "không", "khum": "không", | |
| "dc": "được", "đc": "được", "dk": "được", | |
| "j": "gì", "ms": "mới", "lm": "làm", | |
| "ntn": "như thế nào", "vs": "với", "ns": "nói", | |
| } | |
| NON_LATIN_SCRIPTS = [ | |
| (re.compile(r"[\u1780-\u17FF]"), "Khmer"), | |
| (re.compile(r"[\u0E80-\u0EFF]"), "Lao"), | |
| (re.compile(r"[\u0E00-\u0E7F]"), "Thai"), | |
| (re.compile(r"[\u0400-\u04FF]"), "Cyrillic"), | |
| (re.compile(r"[\u4E00-\u9FFF]"), "Chinese"), | |
| ] | |
| def normalize_code_switching_and_slang(text: str) -> str: | |
| """Chuan hoa cac tu ngu pha tron ngon ngu (Code-switching) va teencode.""" | |
| lowered = text.lower() | |
| for slang, replacement in COMMON_VIET_SLANG_CODE_SWITCH.items(): | |
| if slang in lowered: | |
| # Replace case-insensitively using regex word boundary when possible | |
| pattern = re.compile(rf"\b{re.escape(slang)}\b", re.IGNORECASE) | |
| text = pattern.sub(replacement, text) | |
| return text | |
| # --------------------------------------------------------------------------- | |
| # Universal Language Pivot & Translation Layer | |
| # --------------------------------------------------------------------------- | |
| class UniversalTranslator: | |
| """ | |
| Module dich da ngon ngu & ngon ngu it tai nguyen (LRL) ve ngon ngu chuan (Pivot: EN/VI). | |
| Ho tro: | |
| 1. Custom translation_fn (neu dung MarianMT, NLLB-200, Google Translate API, v.v.) | |
| 2. Fallback heuristic: Giu nguyen text va chuyen hoa code-switching / teencode. | |
| """ | |
| _custom_translator_fn = None | |
| def set_translator(cls, fn: Callable[[str, str], str]): | |
| """Cai dat ham dich tuy chon: fn(text, target_lang='en') -> translated_text.""" | |
| cls._custom_translator_fn = staticmethod(fn) | |
| def translate_to_pivot(cls, text: str, target_lang: str = "en") -> str: | |
| """ | |
| Dich van ban bat ky ve ngon ngu ban le (Pivot Language: mac dinh la tieng Anh). | |
| Giup ca Ingress Classifier va LLM de dang doi chieu voi luat an toan. | |
| """ | |
| if not text or not text.strip(): | |
| return "" | |
| # Neu nguoi dung da dang ky model dich (NLLB, MarianMT, Google API...) | |
| if cls._custom_translator_fn is not None: | |
| try: | |
| return cls._custom_translator_fn(text, target_lang) | |
| except Exception: | |
| pass | |
| # Fallback mac dinh: Chuan hoa code-switching & leetspeak | |
| normalized = normalize_code_switching_and_slang(text) | |
| return normalized | |
| def detect_code_switching_and_lrl(text: str) -> dict: | |
| """ | |
| Phat hien su xuat hien cua da ngon ngu trong cung mot prompt: | |
| - Code-switching (Pha tron tieng Viet va tieng Anh / tu muon) | |
| - Low-Resource Scripts (Khmer, Lao, Thai, Cyrillic homoglyphs, v.v.) | |
| """ | |
| if not text: | |
| return { | |
| "has_code_switching": False, | |
| "has_lrl_script": False, | |
| "detected_scripts": [], | |
| "needs_translation": False, | |
| } | |
| detected_scripts = [] | |
| for pattern, script_name in NON_LATIN_SCRIPTS: | |
| if pattern.search(text): | |
| detected_scripts.append(script_name) | |
| # Detect code-switching (chua ca tu vung tieng Viet va tu tieng Anh/muon) | |
| words = re.findall(r"\b\w+\b", text.lower()) | |
| has_viet_indicator = any(w in words for w in ["không", "được", "hộ", "giúp", "làm", "là", "và", "của", "cho", "tôi", "em", "anh"]) | |
| has_eng_indicator = any(w in words for w in ["ok", "check", "bypass", "ignore", "prompt", "system", "hack", "mode", "pass", "code", "run", "script"]) | |
| has_code_switch = (has_viet_indicator and has_eng_indicator) or any(k in text.lower() for k in ["ok không", "ok ko", "bypass được", "check giùm"]) | |
| has_lrl = len(detected_scripts) > 0 | |
| return { | |
| "has_code_switching": has_code_switch, | |
| "has_lrl_script": has_lrl, | |
| "detected_scripts": detected_scripts, | |
| "needs_translation": bool(has_lrl or has_code_switch), | |
| } | |
| def compute_subword_fertility_rate(text: str, tokenizer=None) -> float: | |
| """ | |
| Tinh Subword Fertility Rate (SFR): | |
| SFR = So luong tokens / So luong tu thong thuong. | |
| - Neu khong co tokenizer Transformer, dung xap xi byte/subword heuristic. | |
| - SFR binh thuong: 1.0 - 1.5. | |
| - SFR cao (> 2.0): Dau hieu ngon ngu hiem (LRL), code-switching, hoac chuoi ky tu bi lam nhieu. | |
| """ | |
| if not text: | |
| return 1.0 | |
| words = text.strip().split() | |
| word_cnt = max(len(words), 1) | |
| if tokenizer is not None: | |
| try: | |
| tokens = tokenizer.tokenize(text) | |
| return len(tokens) / word_cnt | |
| except Exception: | |
| pass | |
| # Heuristic fallback: kiem tra ky tu phi Latin va ty le do dai tu | |
| non_ascii_chars = len(re.findall(r"[^\x00-\x7F]", text)) | |
| char_len = len(text) | |
| heuristic_token_est = word_cnt + (non_ascii_chars * 0.8) + (char_len / 4.0) | |
| return float(heuristic_token_est / word_cnt) | |
| def preprocess_text(text: str) -> str: | |
| """ | |
| Pipeline tien xu ly day du dung cho feature engineering: | |
| NFKC > Zero-width removal > Leetspeak decode > Code-switching/Slang normalize | |
| """ | |
| if not isinstance(text, str): | |
| return "" | |
| text = normalize_unicode(text) | |
| text = remove_zero_width(text) | |
| text = decode_leetspeak(text) | |
| text = normalize_code_switching_and_slang(text) | |
| return text | |
| # --------------------------------------------------------------------------- | |
| # Feature Engineering -- Character-level statistics | |
| # --------------------------------------------------------------------------- | |
| JAILBREAK_KEYWORDS = [ | |
| "ignore", "forget", "jailbreak", "developer mode", "act as", | |
| "system prompt", "new instructions", "dan mode", "disregard all", | |
| "ignore all", "bypass", "unrestricted", "no restrictions", | |
| "pretend", "roleplay", "simulate", "override", | |
| # Da ngon ngu / Code-switching keywords | |
| "vượt rào", "bỏ qua", "lệnh hệ thống", "chế độ bảo trì", | |
| ] | |
| def text_length(text: str) -> int: | |
| return len(text) | |
| def word_count(text: str) -> int: | |
| return len(text.split()) | |
| def special_char_count(text: str) -> int: | |
| return len(re.findall(r"[^a-zA-Z0-9\s]", text)) | |
| def upper_char_count(text: str) -> int: | |
| return sum(c.isupper() for c in text) | |
| def digit_count(text: str) -> int: | |
| return sum(c.isdigit() for c in text) | |
| def count_jailbreak_keywords(text: str) -> int: | |
| text_lower = text.lower() | |
| return sum(1 for kw in JAILBREAK_KEYWORDS if kw in text_lower) | |
| def upper_ratio(text: str) -> float: | |
| if not text: | |
| return 0.0 | |
| return upper_char_count(text) / len(text) | |
| def extract_tabular_features(text: str, tokenizer=None) -> dict: | |
| """ | |
| Trich xuat day du cac dac trung bang cho mot van ban dau vao. | |
| Returns dict with tabular & cross-lingual features: | |
| text_length, word_count, special_count, upper_count, digit_count, | |
| attack_keyword_count, upper_ratio, sfr_score, has_code_switching, is_lrl_risk. | |
| """ | |
| clean = preprocess_text(text) | |
| lrl_info = detect_code_switching_and_lrl(text) | |
| sfr = compute_subword_fertility_rate(text, tokenizer=tokenizer) | |
| return { | |
| "text_length": text_length(clean), | |
| "word_count": word_count(clean), | |
| "special_count": special_char_count(clean), | |
| "upper_count": upper_char_count(clean), | |
| "digit_count": digit_count(clean), | |
| "attack_keyword_count": count_jailbreak_keywords(clean), | |
| "upper_ratio": upper_ratio(clean), | |
| "sfr_score": round(sfr, 4), | |
| "has_code_switching": int(lrl_info["has_code_switching"]), | |
| "has_lrl_script": int(lrl_info["has_lrl_script"]), | |
| "is_lrl_risk": int(lrl_info["has_lrl_script"] or (sfr > 2.2 and lrl_info["has_code_switching"])), | |
| } | |