| |
| |
| |
| |
| |
| |
| from tokenizers import Tokenizer |
| from tokenizers.models import BPE |
| from tokenizers.trainers import BpeTrainer |
| from tokenizers.pre_tokenizers import ByteLevel, WhitespaceSplit, DigitsPreTokenizer |
| from tokenizers.processors import TemplateProcessing |
| from tokenizers.normalizers import NFD, StripAccents, Sequence as NormSeq |
| from tokenizers.decoders import BPEDecoder |
| import os, glob, json |
|
|
| OUT_DIR = '/app/data/tokenizer_pakistan' |
| OUT_NAME = 'zabaanai_tokenizer' |
| os.makedirs(OUT_DIR, exist_ok=True) |
|
|
| |
| class PakistanNormalizer: |
| def __init__(self): |
| |
| self.replacements = [ |
| ('\u06C1\u06C2', '\u06C1'), |
| ('\u06A9', '\u06A9'), |
| ('\u06AF', '\u06AF'), |
| |
| ('\u067E', '\u067E'), |
| ('\u06CC', '\u06CC'), |
| ('\u06D2', '\u06D2'), |
| |
| ('\u064B', ''), |
| ('\u064C', ''), |
| ('\u064D', ''), |
| ('\u0650', ''), |
| ('\u064E', ''), |
| ('\u064F', ''), |
| ('\u0652', ''), |
| ] |
|
|
| def normalize(self, text: str) -> str: |
| for old, new in self.replacements: |
| text = text.replace(old, new) |
| return text |
|
|
| |
| def train_pakistan_tokenizer(): |
| print('Training Pakistan BPE tokenizer...') |
|
|
| |
| tokenizer = Tokenizer(BPE(unk_token='<unk>')) |
|
|
| |
| tokenizer.normalizer = NormSeq([NFD(), PakistanNormalizer()]) |
|
|
| |
| tokenizer.pre_tokenizer = ByteLevel(add_prefix_space=False) |
|
|
| |
| tokenizer.decoder = BPEDecoder(suffix='</w>') |
|
|
| |
| trainer = BpeTrainer( |
| vocab_size=250_000, |
| min_frequency=2, |
| max_token_length=100, |
| show_progress=True, |
| special_tokens=[ |
| '<pad>', '<unk>', '<s>', '</s>', '<eot>', '<eop>', |
| '<|endoftext|>', '<|im_start|>', '<|im_end|>', |
| ], |
| initial_alphabet=ByteLevel.alphabet(), |
| ) |
|
|
| |
| text_files = glob.glob('/app/data/raw/**/*.txt', recursive=True) |
| text_files += glob.glob('/app/data/raw/**/*.jsonl', recursive=True) |
|
|
| if not text_files: |
| print('No text files found in /app/data/raw/. Training on empty corpus.') |
| print(' β Will download sample data or use pretrained tokenizer.') |
|
|
| print(f' Found {len(text_files)} text files for tokenizer training') |
|
|
| |
| if text_files: |
| tokenizer.train_from_iterator( |
| line_reader(text_files), |
| trainer=trainer, |
| length=sum(1 for _ in file_reader(text_files)), |
| ) |
| else: |
| print(' β No training files β using default pretrained tokenizer') |
| return None |
|
|
| |
| tokenizer.post_processor = TemplateProcessing( |
| single='$A <|im_end|>', |
| pair='$A <|im_end|> $B:0 <|im_end|>', |
| special_tokens=[ |
| ('<|im_start|>', 1), |
| ('<|im_end|>', 2), |
| ], |
| ) |
|
|
| return tokenizer |
|
|
| def line_reader(files): |
| for f in files: |
| with open(f, 'r', encoding='utf-8', errors='ignore') as fp: |
| for line in fp: |
| line = line.strip() |
| if line: |
| yield line |
|
|
| def file_reader(files): |
| for f in files: |
| with open(f, 'r', encoding='utf-8', errors='ignore') as fp: |
| for line in fp: |
| yield line |
|
|
| |
| tokenizer = train_pakistan_tokenizer() |
| if tokenizer: |
| tokenizer.save(os.path.join(OUT_DIR, f'{OUT_NAME}.json')) |
| tokenizer.model.save(OUT_DIR, OUT_NAME) |
| print(f'Saved tokenizer to {OUT_DIR}') |
| print(f' Vocab size: ~250K (Pakistan-optimized)') |
| else: |
| print('Using default tokenizer (XLM-RoBERTa pretrained)') |
|
|