File size: 12,092 Bytes
991af26 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 | """
Production Cloud Tokenizer Training Runner - ViuMini-MoE-242M
=============================================================
Architecture: Byte-Level BPE (48,000 Vocabulary)
Pre-Tokenizer: Llama-3 / Indic Regex with Unicode Combining Marks (\p{M})
Data Corpus: ViuAI/viu-mini-raw-pretrain (>105B Tokens Pretraining Corpus)
Ratio: 45% Devanagari Hindi + 35% Hinglish + 20% English & Reasoning
Special Tokens: 13 tokens including <soch>, </soch>, <|user|>, <|assistant|>
Designed for high-speed execution in Google Colab (CPU or T4) / Kaggle Notebooks.
Execution time: ~4-5 minutes on cloud 10 Gbps network.
"""
import os
import sys
import time
from pathlib import Path
# Ensure dependencies are available
try:
import datasets
import tokenizers
import huggingface_hub
except ImportError:
print("[setup] Installing required cloud dependencies...")
os.system("pip install -q tokenizers datasets huggingface_hub pyarrow")
from huggingface_hub import HfApi
from tokenizers import Tokenizer, Regex
from tokenizers.models import BPE
from tokenizers.trainers import BpeTrainer
from tokenizers.pre_tokenizers import Split, ByteLevel, Sequence
from tokenizers.decoders import ByteLevel as ByteLevelDecoder
from tokenizers.normalizers import NFC, Sequence as NormSequence
from datasets import load_dataset
# Configuration
HF_DATASET_REPO = "ViuAI/viu-mini-raw-pretrain"
VOCAB_SIZE = 48000
TOTAL_TRAIN_LINES = 500000
OUTPUT_TOKENIZER_PATH = "tokenizer.json"
def _get_hf_token():
# SECURITY: token kabhi hardcode mat karo — env / Kaggle / Colab secrets se lo.
tok = os.environ.get("HF_TOKEN") or os.environ.get("HUGGING_FACE_HUB_TOKEN")
if not tok:
try:
from kaggle_secrets import UserSecretsClient
tok = UserSecretsClient().get_secret("HF_TOKEN")
except Exception:
pass
if not tok:
try:
from google.colab import userdata
tok = userdata.get("HF_TOKEN")
except Exception:
pass
return tok
HF_TOKEN = _get_hf_token()
# Llama 3 / Indic regex pattern: includes \p{M} so Devanagari vowel signs stay attached to consonants
INDIC_LLAMA3_PATTERN = (
r"(?i:'s|'t|'re|'ve|'m|'ll|'d)|"
r"[^\r\n\p{L}\p{N}]?[\p{L}\p{M}]+|"
r"\p{N}{1,3}|"
r" ?[^\s\p{L}\p{N}\p{M}]+[\r\n]*|"
r"\s*[\r\n]+|"
r"\s+(?!\S)|\s+"
)
SPECIAL_TOKENS = [
"<pad>",
"<bos>",
"<eos>",
"<unk>",
"<mask>", # v2 (2026-09-24): `<mask|>` typo fixed
"<|hindi|>",
"<|english|>",
"<|hinglish|>",
"<soch>",
"</soch>",
"<|user|>",
"<|assistant|>",
"<|system|>",
]
BENCHMARK_SAMPLES = [
# Hinglish (Romanized Hindi)
"mai tumse bahut pyaar karta hu",
"bhai kal party me kya scene hai?",
"yaar ye phone ka network bahut slow hai",
"tumne khana khaya kya abhi tak?",
"<|user|> mujhe train ka status batao\n<|assistant|> <soch> Checking train schedule... </soch> aapki train time par hai.",
# Devanagari Hindi
"नमस्ते, आप कैसे हैं?",
"मुझे हिंदी में कहानी सुनाओ",
"भारतीय संविधान का अनुच्छेद 21 जीवन के अधिकार की रक्षा करता है।",
"क्षत्रिय ज्ञानी व्यक्ति त्रिशूल लेकर आया।",
"<|user|> भारत की राजधानी क्या है?\n<|assistant|> <soch> नई दिल्ली भारत की राजधानी है। </soch> नई दिल्ली।",
# English
"The quick brown fox jumps over the lazy dog.",
"Artificial intelligence is transforming real-world problem solving.",
"Can you explain quantum computing in simple terms?",
"DeepSeek-R1 utilizes reinforcement learning for reasoning verification.",
]
def build_cloud_streaming_iterator(repo_id: str, max_lines: int, token: str):
"""Stream lines from Hub. v2 mix (OPTIONAL rule): 30% Hindi, 35% Hinglish, 35% English (English-boosted; v1 was 45/35/20)."""
api = HfApi(token=token)
repo_files = api.list_repo_files(repo_id, repo_type="dataset")
hindi_files = [f for f in repo_files if (f.startswith("hindi/") or f.startswith("hindi_fixed/")) and f.endswith(".parquet")]
hinglish_files = [f for f in repo_files if f.startswith("hinglish/") and (f.endswith(".parquet") or f.endswith(".jsonl"))]
english_files = [f for f in repo_files if (f.startswith("distilled/") or f.startswith("english_fixed/")) and f.endswith(".parquet")]
print(f"[corpus] Discovered on Hub:")
print(f" - Hindi files: {len(hindi_files)}")
print(f" - Hinglish files: {len(hinglish_files)}")
print(f" - English files: {len(english_files)}")
target_hinglish = int(max_lines * 0.35)
target_hindi = int(max_lines * 0.30)
target_english = max_lines - target_hindi - target_hinglish
print(f"[targets] Hindi: {target_hindi:,} lines | Hinglish: {target_hinglish:,} lines | English: {target_english:,} lines")
def fetch_stream(file_list, target_count, category_name):
emitted = 0
for fpath in file_list:
if emitted >= target_count:
break
try:
ds = load_dataset(repo_id, data_files=fpath, split="train", streaming=True, token=token)
for row in ds:
text = (row.get("text", "") or "").strip()
if len(text) >= 15:
yield text
emitted += 1
if emitted >= target_count:
break
except Exception as exc:
continue
print(f"[stream] Finished streaming {emitted:,} lines for {category_name}")
def interleave_generator():
hi_gen = fetch_stream(hindi_files, target_hindi, "Hindi")
h_gen = fetch_stream(hinglish_files, target_hinglish, "Hinglish")
e_gen = fetch_stream(english_files, target_english, "English/Reasoning")
generators = {"hindi": hi_gen, "hinglish": h_gen, "english": e_gen}
pattern = ["hinglish"] * 7 + ["hindi"] * 6 + ["english"] * 7 # v2: 35/30/35 (v1 tha 5/3/2 = 50/30/20)
active = {"hindi": True, "hinglish": True, "english": True}
total_emitted = 0
while total_emitted < max_lines and any(active.values()):
progress = False
for cat in pattern:
if not active[cat]:
continue
try:
line = next(generators[cat])
yield line
total_emitted += 1
progress = True
if total_emitted % 50000 == 0:
print(f" [progress] Streamed {total_emitted:,} / {max_lines:,} training lines...")
if total_emitted >= max_lines:
return
except StopIteration:
active[cat] = False
if not progress:
break
return interleave_generator()
def run_evaluation(tok: Tokenizer):
"""Run comprehensive quality audit on the trained tokenizer."""
print("\n" + "=" * 70)
print("PRODUCTION TOKENIZER EVALUATION & QUALITY AUDIT")
print("=" * 70)
# 1. Special token isolation check
print("\n[Audit 1] Special Tokens Atomic Isolation Check:")
isolation_passed = True
for st in SPECIAL_TOKENS:
enc = tok.encode(st)
if len(enc.tokens) == 1 and enc.tokens[0] == st:
print(f" [PASS] {st:15} -> Token ID: {enc.ids[0]}")
else:
isolation_passed = False
print(f" [FAIL] {st:15} -> Split into {enc.tokens} (IDs: {enc.ids})")
if isolation_passed:
print(" -> All 13 special tokens successfully isolated as single atomic IDs.")
# 2. Multi-lingual fertility benchmark
print("\n[Audit 2] Multi-Lingual Fertility & Compression Benchmark:")
category_fertility = {"Hinglish": [], "Hindi": [], "English": []}
for sample in BENCHMARK_SAMPLES:
enc = tok.encode(sample)
words = sample.split()
ratio = len(enc.tokens) / max(len(words), 1)
if any(ord(c) >= 0x0900 and ord(c) <= 0x097F for c in sample):
category_fertility["Hindi"].append(ratio)
elif "mai" in sample or "bhai" in sample or "yaar" in sample or "train" in sample:
category_fertility["Hinglish"].append(ratio)
else:
category_fertility["English"].append(ratio)
print("\nFertility Evaluation Summary (Tokens per Word):")
for category, ratios in category_fertility.items():
if ratios:
avg_ratio = sum(ratios) / len(ratios)
target = 1.8 if category == "Hindi" else (1.5 if category == "Hinglish" else 1.3)
status = "EXCELLENT" if avg_ratio <= target else ("ACCEPTABLE" if avg_ratio <= target + 0.3 else "HIGH")
print(f" - {category:10}: {avg_ratio:.2f} tokens/word (Target: <= {target:.1f}) [{status}]")
# 3. Round-trip lossless decoding check
print("\n[Audit 3] Round-Trip Lossless Decoding Check:")
fidelity_passed = True
for sample in BENCHMARK_SAMPLES:
enc = tok.encode(sample)
decoded = tok.decode(enc.ids, skip_special_tokens=False)
if decoded.strip() != sample.strip():
fidelity_passed = False
print(f" [FAIL] Round-trip mismatch:\n Expected: {sample}\n Actual: {decoded}")
if fidelity_passed:
print(" [PASS] 100% Round-trip lossless fidelity verified across all test samples.")
print("=" * 70 + "\n")
return isolation_passed and fidelity_passed
def main():
start_time = time.time()
print("=" * 70)
print("ViuMini-MoE-242M: Production Tokenizer Training (Indic-Optimized)")
print(f"Target Vocabulary: {VOCAB_SIZE:,} | Training Samples: {TOTAL_TRAIN_LINES:,}")
print(f"Corpus Source: {HF_DATASET_REPO}")
print("=" * 70)
# Initialize Tokenizer Architecture with Indic-optimized Llama-3 Split Regex
tok = Tokenizer(BPE(unk_token="<unk>"))
tok.normalizer = NormSequence([NFC()])
tok.pre_tokenizer = Sequence([
Split(pattern=Regex(INDIC_LLAMA3_PATTERN), behavior="isolated"),
ByteLevel(add_prefix_space=False, use_regex=False),
])
tok.decoder = ByteLevelDecoder()
trainer = BpeTrainer(
vocab_size=VOCAB_SIZE,
min_frequency=2,
show_progress=True,
special_tokens=SPECIAL_TOKENS,
initial_alphabet=ByteLevel.alphabet(),
)
# Build streaming training iterator
print("[train] Preparing balanced multi-lingual streaming iterator...")
training_iterator = build_cloud_streaming_iterator(
repo_id=HF_DATASET_REPO,
max_lines=TOTAL_TRAIN_LINES,
token=HF_TOKEN,
)
# Train Byte-Level BPE
print(f"[train] Commencing Rust-accelerated BPE training on {TOTAL_TRAIN_LINES:,} samples...")
tok.train_from_iterator(training_iterator, trainer=trainer, length=TOTAL_TRAIN_LINES)
# Save trained tokenizer locally
tok.save(OUTPUT_TOKENIZER_PATH)
file_size_mb = os.path.getsize(OUTPUT_TOKENIZER_PATH) / (1024 * 1024)
print(f"[train] Tokenizer saved locally: {OUTPUT_TOKENIZER_PATH} ({file_size_mb:.2f} MB)")
# Execute Evaluation Suite
audit_success = run_evaluation(tok)
# Upload to Hugging Face Hub
if HF_TOKEN:
print(f"[hub] Uploading production tokenizer to {HF_DATASET_REPO}...")
api = HfApi(token=HF_TOKEN)
api.upload_file(
path_or_fileobj=OUTPUT_TOKENIZER_PATH,
path_in_repo="tokenizer/tokenizer.json",
repo_id=HF_DATASET_REPO,
repo_type="dataset",
)
print(f"[hub] Successfully uploaded tokenizer/tokenizer.json to {HF_DATASET_REPO}!")
elapsed = time.time() - start_time
print(f"[complete] All steps finished successfully in {elapsed / 60:.2f} minutes.")
if __name__ == "__main__":
main()
|