İçeriğe geç

HuggingFace Tokenizers Rust + Production Pipeline: Üretim-Kalite Tokenizer'ı Sıfırdan Eğitmek

HuggingFace tokenizers crate'inin Rust mimarisi, 6 katmanlı pipeline (Normalizer → PreTokenizer → Model → PostProcessor → Decoder → Trainer), tokenizer.json format anatomisi, Türkçe production-grade end-to-end training, Rust internals (parallel processing, SIMD, ahash, mmap), tiktoken/SentencePiece conversion, threading + caching + FFI overhead, benchmarklar.

Şükrü Yusuf KAYA
80 dakikalık okuma
İleri
HuggingFace Tokenizers Rust + Production Pipeline: Üretim-Kalite Tokenizer'ı Sıfırdan Eğitmek
🦀 Rust altyapısının arkasında — tek bir kütüphane, milyarlarca token/saniye
HuggingFace tokenizers, 2020'de Anthony Moi öncülüğünde Rust ile yazıldı. Bugün Python ekosisteminde fiili standart: `transformers`, `datasets`, `text-generation-inference`, `vLLM` — hepsi alttan bu kütüphaneyi kullanıyor. Rust seçimi tesadüf değil: SIMD, lock-free hashmap, memory-mapped I/O ile tek thread 1M token/saniye, 16-core'da 16M+. 80 dakika sonra: 6 katmanlı pipeline'ı (Normalizer → PreTokenizer → Model → PostProcessor → Decoder → Trainer) ezbere çizebilecek, kendi Türkçe tokenizer'ını production-grade eğitebilecek, tokenizer.json'ı satır satır okuyabilecek, FFI overhead'ini optimize edebilecek, tiktoken/SentencePiece'i HF formatına convert edebileceksin. Bu, modern LLM mühendisinin dijital cerrah aletleri.

Ders Haritası (16 Bölüm)#

  1. HuggingFace ekosistemi — transformers vs tokenizers library ayrımı
  2. Niye Rust — performans, safety, ekosistem entegrasyonu
  3. Pipeline mimarisi — 6 katman ve veri akışı
  4. Normalizers — NFC/NFD/NFKC/NFKD, Lowercase, StripAccents, BertNormalizer, Sequence
  5. PreTokenizers — Whitespace, ByteLevel, Metaspace, Digits, Split, UnicodeScripts
  6. Models — BPE, WordPiece, Unigram, WordLevel — algorithm parametreleri
  7. PostProcessors — TemplateProcessing, BertProcessing, RobertaProcessing, ByteLevel
  8. Decoders — round-trip lossless decoding
  9. Trainers — BpeTrainer, WordPieceTrainer, UnigramTrainer config detayları
  10. End-to-end Türkçe training — corpus → tokenizer.json (production-grade)
  11. tokenizer.json format — JSON şeması, vocab merge_rules, byte-by-byte
  12. Loading & saving — pretrained, conversion, custom
  13. Rust internals — parallel processing, ahash, SIMD, mmap, lock-free design
  14. Production deployment — caching, threading, FFI overhead, memory profile
  15. Conversion — tiktoken → HF, SentencePiece → HF, custom formats
  16. Benchmarks — fertility, throughput, memory, Trendyol-LLM karşılaştırma

1. HuggingFace Ekosistemi — Library Ayrımı#

1.1 `transformers` library (model)#

Python package. PyTorch/JAX/TensorFlow model'lerini load eder. AutoTokenizer sınıfı:
from transformers import AutoTokenizer tokenizer = AutoTokenizer.from_pretrained("meta-llama/Meta-Llama-3-8B")
Bu fast tokenizer (Rust-backed) veya slow tokenizer (pure Python) döner. Default fast.

1.2 `tokenizers` library (tokenization)#

Ayrı Python package. Rust-backed core. transformers'ın kullandığı motor.
from tokenizers import Tokenizer tokenizer = Tokenizer.from_pretrained("meta-llama/Meta-Llama-3-8B")
Direkt erişim — daha az overhead, daha hızlı, training mümkün.

1.3 `tiktoken` library (OpenAI native)#

OpenAI'ın kendi Rust BPE library'si. Modül 6.6'da detay. HuggingFace değil.

1.4 `sentencepiece` library (Google)#

Google'ın C++ library'si. Python binding. Modül 6.5'te detay.

1.5 Tercih kriteri#

SenaryoLibrary
HuggingFace model load + tokenize`transformers.AutoTokenizer`
Custom training, advanced pipeline`tokenizers` direkt
OpenAI model token counting`tiktoken`
T5/mT5 multilingual classic`sentencepiece`
Yeni model production`tokenizers` (modern standard)

1.6 İndirme & install#

pip install tokenizers # Rust core, fast pip install transformers # Otomatik tokenizers'ı çeker pip install sentencepiece # T5, mT5 için pip install tiktoken # OpenAI için

1.7 Versioning#

  • `tokenizers` 0.20+ (2024 ortası): Unigram trainer Rust'a taşındı, %50 hız artışı.
  • `tokenizers` 0.21+ (2025): Multimodal token desteği eklendi.
  • `transformers` her zaman uyumlu `tokenizers` versiyonu bağımlısı (dependency pin).

2. Niye Rust#

2.1 Performans nümerik gerçeği#

Pure Python BPE encoding: ~10K token/sec single thread. Rust BPE: ~1M token/sec single thread. 100x hızlanma.
Neden:
  • Compiler optimization: Rust LLVM ile aggressive optimize
  • Type safety: runtime type check yok
  • Cache locality: contiguous memory, predictable layout
  • SIMD: byte processing'de vectorization (avx2, neon)
  • Lock-free hashmap: `ahash` + thread-local lookup

2.2 Memory safety#

C/C++ alternatif olabilirdi. Ama:
  • Buffer overflow risk = production crash
  • Manual memory management hata-yatkın
  • Rust ownership system → derleme zamanı garantiler
SentencePiece (C++) production'da yıllarca sorun yaşadı: rare crash'ler, undefined behavior. HF tokenizers (Rust) baştan beri sağlam.

2.3 Ekosistem#

  • pyo3 / maturin: Python binding generator, mükemmel UX
  • napi-rs: Node.js binding (HF tokenizers WASM bridge için)
  • JNI: Java binding (production servers için)
  • C ABI: any language

2.4 Async ready#

Rust tokio runtime ile async tokenization mümkün. Production gRPC server tokenizer'ı paralel batch'lerde async işleyebilir.

2.5 Karşılaştırma: BPE encoding 10K cümle#

Pure Python (Karpathy minbpe): 24.3 sec SentencePiece (C++): 1.4 sec tiktoken (Rust): 0.18 sec tokenizers (Rust): 0.22 sec (HF, biraz fazla overhead)
tiktoken HF tokenizers'tan biraz daha hızlı — daha az feature, daha tight loop. HF tokenizers daha esnek (custom pipeline) ama %20 daha yavaş.

3. 6 Katmanlı Pipeline Mimarisi#

3.1 Veri akışı#

Raw text ↓ Normalizer Normalized text (str) ↓ PreTokenizer List of (str, (start, end)) # Pre-tokens with byte offsets ↓ Model (BPE/WordPiece/Unigram/WordLevel) List of (token_id, (start, end)) # Final tokens ↓ PostProcessor List of (token_id, (start, end)) # With special tokens added ↓ [Decoder for reverse direction] str # Decoded text

3.2 Her katmanın görevi#

Normalizer

Unicode normalization, accent stripping, lowercasing, custom replace. Reversible olmayabilir (lossy).

PreTokenizer

Whitespace/punctuation/byte split. Pre-token'lar arasında boundary çizer. Model bu boundary'leri asla geçemez.

Model

Gerçek tokenization algoritması: BPE merges veya WordPiece longest-match veya Unigram Viterbi. Pre-token içinde subword'lere ayırır.

PostProcessor

Special token ekleme (BOS, EOS, [CLS], [SEP]), template processing, attention mask oluşturma.

Decoder

Token IDs → text reverse mapping. Whitespace handling, byte-level decode.

Trainer

Corpus'tan vocab + merge rules öğrenme. Pipeline'ın dışında çalışır, sonuçları model'e write eder.

3.3 Pipeline immutable mı?#

Hayır. Runtime'da update edebilirsin:
tokenizer.normalizer = NFD() tokenizer.pre_tokenizer = Whitespace() # ...
Ama her değişiklik tokenizer'ı fresh state'e götürür — eski tokenize edilmiş outputs invalid.

3.4 Her katman opsiyonel#

# Minimal tokenizer: sadece Model (WordLevel) tokenizer = Tokenizer(WordLevel(unk_token="[UNK]")) # Normalizer yok, PreTokenizer yok, PostProcessor yok
Ama production-grade için tüm katmanlar tipik.

4. Normalizers — Unicode Hijyeni#

4.1 NFC / NFD / NFKC / NFKD#

Unicode Normalization Forms — same character'ın multiple representations'unu unify eder.
Örnek: "é" karakteri iki şekilde temsil edilebilir:
  1. U+00E9 (precomposed)
  2. U+0065 + U+0301 (e + combining acute) — decomposed
Normalizers:
  • NFC: Composition. Tek karakter halinde (U+00E9). Production'da en yaygın.
  • NFD: Decomposition. Birden çok karakter (U+0065 U+0301). StripAccents'ten önce.
  • NFKC: Compatibility composition. "fi" (U+FB01) → "fi". Daha agresif.
  • NFKD: Compatibility decomposition.
from tokenizers.normalizers import NFC, NFD, NFKC, NFKD tokenizer.normalizer = NFC()
Türkçe için NFC sağlam tercih. İ (U+0130), ı (U+0131), ğ (U+011F), ş (U+015F), ç (U+00E7), ö (U+00F6), ü (U+00FC) tek-codepoint, NFC bunları korur.

4.2 Lowercase#

from tokenizers.normalizers import Lowercase tokenizer.normalizer = Lowercase()
Türkçe tehlike: "İ" (büyük noktalı İ) lowercase'de Python'da "i̇" (i + combining dot) verir, beklenmedik. Türkçe-aware lowercase için `str.casefold()` veya regex preprocessing. `tokenizers.normalizers.Lowercase()` Unicode default — Türkçe için optimal değil.

4.3 StripAccents#

Accent'ları kaldırır (NFD ile birlikte kullanılır):
from tokenizers.normalizers import NFD, StripAccents, Sequence tokenizer.normalizer = Sequence([NFD(), StripAccents()])
Türkçe TEHLİKE: "şehir" → "sehir" (ş → s, ı/ç/ş/ğ etc. dropped). Türkçe semantiği bozulur, asla kullanma.

4.4 BertNormalizer#

BERT-style: NFD + StripAccents + Lowercase + cleaning. Sequence wrapper:
from tokenizers.normalizers import BertNormalizer tokenizer.normalizer = BertNormalizer( clean_text=True, # control chars temizle handle_chinese_chars=True, # Çinli karakterler arasına space strip_accents=None, # None = lower case için strip, ya da False lowercase=True, )
Türkçe için ne yapmalıyım?
  • `strip_accents=False` (Türkçe spesifik karakterleri koru)
  • `lowercase=True` veya `False` (cased model istiyorsan False)
  • BERT-base-Turkish-cased: `lowercase=False, strip_accents=False`
  • BERT-base-Turkish-uncased: `lowercase=True, strip_accents=False`

4.5 Replace#

Regex-based substitution:
from tokenizers.normalizers import Replace tokenizer.normalizer = Replace(pattern=r"\s+", content=" ") # multiple spaces → single

4.6 Sequence (compose)#

Birden fazla normalizer'ı zincirle:
from tokenizers.normalizers import Sequence, NFC, Replace, Lowercase tokenizer.normalizer = Sequence([ NFC(), Replace(r"\s+", " "), Lowercase(), ])

4.7 Strip, Prepend#

from tokenizers.normalizers import Strip, Prepend Strip(left=True, right=True) # whitespace trim Prepend(prepend=" ") # SentencePiece-style ▁ benzeri

4.8 Türkçe production önerisi#

from tokenizers.normalizers import Sequence, NFC, Replace tokenizer.normalizer = Sequence([ NFC(), # Unicode normalize Replace(r"[\u200B-\u200F]", ""), # zero-width chars temizle Replace(r"\s+", " "), # whitespace collapse Strip(), # trim ])
Türkçe karakterler korunur, cleanup yapılır.

5. PreTokenizers — Boundary Çizenler#

5.1 Whitespace#

Whitespace + punctuation boundary'leri:
from tokenizers.pre_tokenizers import Whitespace tokenizer.pre_tokenizer = Whitespace() # "Merhaba, dünya!" → ["Merhaba", ",", "dünya", "!"]
Word-level, punctuation ayrı. BERT/WordPiece için klasik.

5.2 WhitespaceSplit#

Sadece whitespace boundary (punctuation kelime parçası):
from tokenizers.pre_tokenizers import WhitespaceSplit tokenizer.pre_tokenizer = WhitespaceSplit() # "Merhaba, dünya!" → ["Merhaba,", "dünya!"]

5.3 Punctuation#

Sadece punctuation boundary:
from tokenizers.pre_tokenizers import Punctuation tokenizer.pre_tokenizer = Punctuation() # "Merhaba,dünya!" → ["Merhaba", ",", "dünya", "!"]

5.4 ByteLevel — GPT-2/4 stili#

from tokenizers.pre_tokenizers import ByteLevel tokenizer.pre_tokenizer = ByteLevel( add_prefix_space=True, # " Merhaba" tek token (space prefix) use_regex=True, # GPT-2 regex pattern trim_offsets=True, # offset cleanup )
Önemli: ByteLevel pre-tokenizer text'i UTF-8 byte'lara çevirir, sonra GPT-2 regex pattern uygular. Sonuçta her token byte-level → asla UNK yok.

5.5 Metaspace — SentencePiece stili#

from tokenizers.pre_tokenizers import Metaspace tokenizer.pre_tokenizer = Metaspace( replacement="▁", # U+2581 lower one eighth block prepend_scheme="first", # ya da "always" / "never" )
Whitespace'i ▁ ile değiştirir. Modül 6.5'teki SentencePiece davranışı.

5.6 Digits#

Sayıları digit-by-digit veya chunk:
from tokenizers.pre_tokenizers import Digits tokenizer.pre_tokenizer = Digits(individual_digits=True) # "2026 yılı" → ["2", "0", "2", "6", " yılı"]
Math reasoning model'leri için kritik (4-digit aritmetiği token-pattern olarak öğrenmek).

5.7 Split — custom delimiter#

from tokenizers.pre_tokenizers import Split tokenizer.pre_tokenizer = Split( pattern=r"[A-Z][a-z]+", behavior="isolated", # ya da "removed" / "merged_with_previous" / "merged_with_next" invert=False, )

5.8 CharDelimiterSplit#

Belirli char'da split:
CharDelimiterSplit(delimiter="|")

5.9 BertPreTokenizer#

from tokenizers.pre_tokenizers import BertPreTokenizer tokenizer.pre_tokenizer = BertPreTokenizer()
Whitespace + punctuation + cleanup. BERT compat için.

5.10 UnicodeScripts#

Unicode script boundary'leri (Latin, Cyrillic, Arabic, ...):
from tokenizers.pre_tokenizers import UnicodeScripts tokenizer.pre_tokenizer = UnicodeScripts()
Multilingual modelde script boundary'lerini korumak için.

5.11 Sequence (compose)#

from tokenizers.pre_tokenizers import Sequence, Whitespace, Punctuation, Digits tokenizer.pre_tokenizer = Sequence([ Whitespace(), Punctuation(), Digits(individual_digits=True), ])

5.12 Türkçe için önerim#

from tokenizers.pre_tokenizers import ByteLevel tokenizer.pre_tokenizer = ByteLevel(add_prefix_space=True, use_regex=True)
GPT-style byte-level: byte fallback, multilingual ready, modern standard.

6. Models — Algoritma Çekirdeği#

6.1 BPE#

from tokenizers.models import BPE model = BPE( vocab=None, # dict: {token: id} merges=None, # list: [("a", "b"), ...] unk_token="<unk>", byte_fallback=True, # rare chars → bytes (Llama-3 stili) dropout=None, # BPE-dropout (Provilkov 2020) fuse_unk=False, end_of_word_suffix=None, continuing_subword_prefix=None, )
Önemli paramlar:
  • byte_fallback: True → asla UNK, rare chars byte-level fallback.
  • dropout: 0.0-1.0. Training'te %X merge'i skip (regularization).
  • continuing_subword_prefix: "##" (WordPiece-style ama BPE'de) — pre-tokenize word continuation marker.
  • end_of_word_suffix: "" (eski BPE word boundary marker).

6.2 WordPiece#

from tokenizers.models import WordPiece model = WordPiece( vocab=None, unk_token="[UNK]", max_input_chars_per_word=100, # uzun kelimeler için limit continuing_subword_prefix="##", )
Detay Modül 6.4'te.

6.3 Unigram#

from tokenizers.models import Unigram model = Unigram( vocab=None, # list: [(token, log_prob), ...] unk_id=None, byte_fallback=False, )
Detay Modül 6.5'te.

6.4 WordLevel — basit#

from tokenizers.models import WordLevel model = WordLevel( vocab=None, # dict unk_token="<unk>", )
Subword yok — tam kelimeler tek token. Vocab kelime listesi. Sadece pre-tokenizer'ın kestiği boundary'leri ID'ye çevirir. Modern LLM için pratik değil (vocab patlar).

6.5 Algorithm seçim matrisi#

AlgoritmaAvantajDezavantajKim kullanıyor
BPEBasit, hızlı, byte-fallbackOOV risk (byte_fallback olmadan)GPT-2/3/4, Llama-3, Mistral
WordPieceLikelihood-based, morfolojiUNK risk, byte fallback yokBERT, DistilBERT
UnigramProbabilistic, subword regularizationTraining yavaşT5, mT5, ALBERT, XLNet
WordLevelTrivialVocab patlamasıNadir

6.6 Türkçe için tercih#

Modern: BPE + byte_fallback=True. Llama-3 pattern. Türkçe morphology iyi yakalanır, byte-level coverage.

7. PostProcessors — Special Token Enjeksiyonu#

7.1 TemplateProcessing#

En esnek. "single" ve "pair" template:
from tokenizers.processors import TemplateProcessing tokenizer.post_processor = TemplateProcessing( single="[CLS] $A [SEP]", pair="[CLS] $A [SEP] $B:1 [SEP]:1", special_tokens=[ ("[CLS]", tokenizer.token_to_id("[CLS]")), ("[SEP]", tokenizer.token_to_id("[SEP]")), ], )
Syntax:
  • `A\`, \`B`: input sequence A ve B (pair tasks için)
  • `:0`, `:1`: token_type_id (BERT segment ID)
  • `[CLS]`, `[SEP]`: literal special tokenlar

7.2 BertProcessing — shortcut#

from tokenizers.processors import BertProcessing tokenizer.post_processor = BertProcessing( sep=("[SEP]", tokenizer.token_to_id("[SEP]")), cls=("[CLS]", tokenizer.token_to_id("[CLS]")), )

7.3 RobertaProcessing#

RoBERTa BPE için:
from tokenizers.processors import RobertaProcessing tokenizer.post_processor = RobertaProcessing( sep=("</s>", tokenizer.token_to_id("</s>")), cls=("<s>", tokenizer.token_to_id("<s>")), add_prefix_space=True, )

7.4 ByteLevel#

from tokenizers.processors import ByteLevel tokenizer.post_processor = ByteLevel(trim_offsets=True)
ByteLevel pre-tokenizer ile uyumlu offset cleanup.

7.5 Custom chat template — direkt PostProcessor değil#

Chat templates Modül 6.7'de gördüğümüz Jinja2 templates. PostProcessor değil — `tokenizer.chat_template` field'ı.

8. Decoders — Round-Trip Lossless#

8.1 BPE Decoder#

from tokenizers.decoders import BPEDecoder tokenizer.decoder = BPEDecoder()

8.2 WordPiece Decoder#

from tokenizers.decoders import WordPiece tokenizer.decoder = WordPiece(prefix="##", cleanup=True)

8.3 ByteLevel Decoder#

from tokenizers.decoders import ByteLevel tokenizer.decoder = ByteLevel()

8.4 Metaspace Decoder#

from tokenizers.decoders import Metaspace tokenizer.decoder = Metaspace(replacement="▁")

8.5 Lossless requirement#

Ideal decoder: `decode(encode(x)) == x` her zaman. Pratik:
  • ByteLevel BPE: ✅ tam lossless
  • WordPiece: ⚠️ "##" prefix bilgi taşır ama whitespace handling değişebilir
  • Metaspace: ✅ ▁ → space tam reversible
  • BPE classic: ⚠️ word boundary marker depending on training

8.6 Strip, Replace, Sequence#

from tokenizers.decoders import Strip, Replace, Sequence Sequence([ByteLevel(), Replace("▁", " "), Strip(content=" ", left=1)])

9. Trainers — Vocab Öğrenme#

9.1 BpeTrainer#

from tokenizers.trainers import BpeTrainer trainer = BpeTrainer( vocab_size=32000, min_frequency=2, # min pair count to consider special_tokens=["<pad>", "<unk>", "<s>", "</s>", "<|im_start|>", "<|im_end|>"], initial_alphabet=ByteLevel.alphabet(), # tüm 256 byte continuing_subword_prefix=None, end_of_word_suffix=None, show_progress=True, max_token_length=None, )
Önemli paramlar:
  • vocab_size: target. Türkçe-only 32K-50K, multilingual 100K+.
  • min_frequency: 2 default. Küçük corpus için 1.
  • special_tokens: vocab başına eklenir, ID 0,1,2,... reserved.
  • initial_alphabet: base chars/bytes. ByteLevel.alphabet() → 256 byte garanti.

9.2 WordPieceTrainer#

from tokenizers.trainers import WordPieceTrainer trainer = WordPieceTrainer( vocab_size=32000, min_frequency=2, special_tokens=["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]"], continuing_subword_prefix="##", end_of_word_suffix=None, show_progress=True, )

9.3 UnigramTrainer#

from tokenizers.trainers import UnigramTrainer trainer = UnigramTrainer( vocab_size=32000, special_tokens=["<pad>", "<unk>", "<s>", "</s>"], initial_alphabet=[], shrinking_factor=0.75, # round başına %25 küçült unk_token="<unk>", max_piece_length=16, n_sub_iterations=2, seed_sentencepiece_size=1_000_000, show_progress=True, )
Unigram detay Modül 6.5'te. Bu trainer Kudo 2018 algorithm'ini Rust'ta implement eder (v0.20+).

9.4 WordLevelTrainer#

from tokenizers.trainers import WordLevelTrainer trainer = WordLevelTrainer( vocab_size=10000, min_frequency=5, special_tokens=["<unk>"], )

9.5 Trainer çalıştırma#

files = ["corpus/wiki-tr.txt", "corpus/news-tr.txt"] tokenizer.train(files, trainer)
Veya Python iterator'dan:
def batch_iterator(): for batch in dataset.iter(batch_size=1000): yield batch["text"] tokenizer.train_from_iterator(batch_iterator(), trainer=trainer, length=len(dataset))

9.6 Threading#

HF tokenizers default tüm CPU core'ları kullanır:
import os os.environ["RAYON_NUM_THREADS"] = "16" # explicit thread sayısı
Rayon = Rust'ın parallel iterator library'si.

10. End-to-End Türkçe Tokenizer Training (Production-Grade)#

Tüm pipeline'ı birleştiren tam training script. Sonuç: `turk-bpe-32k.json` (~2 MB).

Adım adım kodun:#

from tokenizers import Tokenizer from tokenizers.models import BPE from tokenizers.normalizers import Sequence, NFC, Replace, Strip from tokenizers.pre_tokenizers import ByteLevel from tokenizers.processors import ByteLevel as ByteLevelProcessor from tokenizers.decoders import ByteLevel as ByteLevelDecoder from tokenizers.trainers import BpeTrainer # 1. Tokenizer init tokenizer = Tokenizer(BPE(unk_token=None, byte_fallback=True)) # 2. Normalizer tokenizer.normalizer = Sequence([ NFC(), Replace(r"[\u200B-\u200F\uFEFF]", ""), # zero-width chars Replace(r"\s+", " "), # whitespace collapse Strip(), ]) # 3. Pre-tokenizer tokenizer.pre_tokenizer = ByteLevel(add_prefix_space=True, use_regex=True) # 4. Post-processor tokenizer.post_processor = ByteLevelProcessor(trim_offsets=True) # 5. Decoder tokenizer.decoder = ByteLevelDecoder() # 6. Trainer config trainer = BpeTrainer( vocab_size=32000, min_frequency=2, special_tokens=[ "<|endoftext|>", "<|pad|>", "<|im_start|>", "<|im_end|>", "<|user|>", "<|assistant|>", "<|system|>", "<|tool|>", ], initial_alphabet=ByteLevel.alphabet(), show_progress=True, max_token_length=24, # max sub-word length ) # 7. Train files = [ "corpus/wiki-tr.txt", "corpus/news-tr.txt", "corpus/oscar-tr.txt", "corpus/literature-tr.txt", ] tokenizer.train(files, trainer) # 8. Save tokenizer.save("turk-bpe-32k.json") # 9. Test result = tokenizer.encode("İstanbul Boğazı'nda balıkçı tekneleri sallanıyor.") print("Tokens:", result.tokens) print("IDs:", result.ids) print("Count:", len(result.ids))

Training time benchmark#

10 GB Türkçe corpus, 16-core EPYC:
  • Step 1-5 (setup): <1 sec
  • Step 7 (BPE training): ~25 minutes
  • Total: ~25 minutes

Memory profile#

  • Corpus mmap: ~10 GB (virtual, disk-backed)
  • BPE training peak: ~12 GB RAM
  • Output tokenizer.json: ~2 MB

Production önerileri#

  1. Corpus diversity: Wikipedia + OSCAR + news + literature + code mix
  2. Cleaning: HTML strip, deduplicate, language detect
  3. Min frequency: 2 ideal, 1 only for very small corpus
  4. Special tokens: chat format'ın hepsi vocab'da olsun
  5. max_token_length: 16-24 sweet spot
  6. initial_alphabet: byte-level garanti için ByteLevel.alphabet()

11. tokenizer.json Format Anatomisi#

11.1 Üst-seviye yapı#

{ "version": "1.0", "truncation": null, "padding": null, "added_tokens": [...], "normalizer": {...}, "pre_tokenizer": {...}, "post_processor": {...}, "decoder": {...}, "model": {...} }

11.2 added_tokens#

Special tokenlar:
"added_tokens": [ { "id": 0, "content": "<|endoftext|>", "single_word": false, "lstrip": false, "rstrip": false, "normalized": false, "special": true }, { "id": 1, "content": "<|im_start|>", ... } ]

11.3 normalizer#

"normalizer": { "type": "Sequence", "normalizers": [ {"type": "NFC"}, {"type": "Replace", "pattern": {"Regex": "\\s+"}, "content": " "} ] }

11.4 pre_tokenizer#

"pre_tokenizer": { "type": "ByteLevel", "add_prefix_space": true, "trim_offsets": true, "use_regex": true }

11.5 model (BPE)#

"model": { "type": "BPE", "dropout": null, "unk_token": null, "continuing_subword_prefix": null, "end_of_word_suffix": null, "fuse_unk": false, "byte_fallback": true, "vocab": { "!": 0, "\"": 1, ... "Ġmerhaba": 4523, "Ġdünya": 6711, ... }, "merges": [ "Ġ d", "e r", "i n", ... ] }

11.6 vocab byte encoding#

"Ġ" U+0120 — GPT-2 byte encoding'inde space byte (0x20). Bu, "Ġmerhaba" aslında " merhaba" (space-prefixed). Modül 6.6'da detay.

11.7 merges sırası kritik#

BPE merges sırayla uygulanır. İlk merge en sık pair, son merge en nadir. İşin matematiği Modül 6.2'de.

11.8 Dosya boyutu beklenen#

  • 32K vocab BPE: ~2-3 MB
  • 100K vocab: ~6-8 MB
  • 200K vocab (o200k): ~12 MB

11.9 Sürüm uyumluluğu#

`"version": "1.0"` — tokenizers 0.10+ format. `"version": "0.x"` legacy. Yeni training her zaman "1.0".

12. Loading & Saving — Format Galaxies#

12.1 HF native#

# Save tokenizer.save("turk-bpe-32k.json") # Load from tokenizers import Tokenizer tokenizer = Tokenizer.from_file("turk-bpe-32k.json")

12.2 HF Hub'dan pretrained#

tokenizer = Tokenizer.from_pretrained("meta-llama/Meta-Llama-3-8B-Instruct")
Oto-download, cache (default `~/.cache/huggingface`).

12.3 transformers AutoTokenizer ile uyumlu#

from transformers import PreTrainedTokenizerFast tokenizer = PreTrainedTokenizerFast(tokenizer_file="turk-bpe-32k.json") tokenizer.save_pretrained("./my-tokenizer") # Çıktı: my-tokenizer/tokenizer.json + tokenizer_config.json + special_tokens_map.json

12.4 Legacy BERT format (vocab.txt)#

from tokenizers import BertWordPieceTokenizer tokenizer = BertWordPieceTokenizer("vocab.txt", lowercase=False)

12.5 Legacy SentencePiece (.model)#

# transformers auto-converts SP .model to fast tokenizer from transformers import T5Tokenizer tokenizer = T5Tokenizer.from_pretrained("google/mt5-base") fast = tokenizer.train_new_from_iterator(...)
Manuel conversion: `convert_slow_tokenizer` utility.

12.6 tiktoken → HF conversion#

Direct değil. Workaround:
import tiktoken enc = tiktoken.get_encoding("cl100k_base") # 1. Vocab extract vocab = {} for i in range(enc.n_vocab): try: token = enc.decode_single_token_bytes(i).decode("utf-8", errors="replace") vocab[token] = i except: pass # 2. HF BPE oluştur (merges yok!) from tokenizers import Tokenizer from tokenizers.models import BPE tokenizer = Tokenizer(BPE(vocab=vocab, merges=[], byte_fallback=True)) # Note: merges yok → bu sadece encode yapamayan dummy. Tam conversion için merge inferring gerekir.
Tam tiktoken → HF için `hf-internal-testing/llama-tokenizer` benzeri community converter'ları var.

13. Rust Internals — Performansın Kaynakları#

13.1 Parallel processing (rayon)#

Batch encode paralel:
// Rust internals (simplified) use rayon::prelude::*; pub fn encode_batch(&self, inputs: Vec<&str>) -> Vec<Encoding> { inputs.par_iter() .map(|input| self.encode(input)) .collect() }
Python call:
results = tokenizer.encode_batch(["cümle 1", "cümle 2", "cümle 3"])
N core'da N kat hızlanma (embarrassingly parallel).

13.2 ahash — fast hashmap#

Default Rust HashMap SipHash kullanır (DoS resistance, ama yavaş). HF tokenizers `ahash` library:
  • 2-3x daha hızlı lookup
  • Vocab lookup'da kritik (her char için)
  • Production-tested, no DoS issue (tokenizer input trusted)

13.3 SIMD vectorization#

Byte-level operations Rust auto-vectorized:
  • AVX2 (x86_64): 32 byte at once
  • NEON (ARM, Apple Silicon): 16 byte at once
  • UTF-8 validation, byte-to-unicode mapping, BPE merge application — hepsi SIMD-friendly

13.4 mmap (memory-mapped I/O)#

Corpus dosyaları `mmap` ile okunur:
  • OS page cache leverage
  • Disk I/O bandwidth saturate (NVMe için ~3 GB/sec)
  • RAM kullanımı virtual (OS yönetir)

13.5 Lock-free design#

Training sırasında token frequency counts ConcurrentHashMap kullanmıyor — her thread kendi local count'unu tutar, sonunda merge. Lock contention sıfır.

13.6 Zero-copy strings#

Rust `str` ve `&str` zero-copy reference. UTF-8 byte slice'lar copy edilmeden işlenir.

13.7 Profile detayı (encode 1M cümle)#

Normalizer: 12% time PreTokenizer: 8% BPE lookup: 65% time (vocab dictionary lookup) PostProcessor: 3% Overhead: 12% (FFI, allocation, etc.)
Bottleneck: BPE lookup. ahash + cache-friendly vocab layout ile optimize.

14. Production Deployment — Pratik Konular#

14.1 Threading#

Default: tüm core'lar. Container'da CPU limit'i varsa explicit ayarla:
import os os.environ["RAYON_NUM_THREADS"] = "4" # Kubernetes pod 4 CPU

14.2 Caching#

Tokenize sonuçları idempotent — cache safe. Production'da:
  • Redis cache (hash(text) → tokens)
  • LRU local cache (Python `functools.lru_cache`)
  • Database column (her satır için precomputed tokens)
Genel kural: aynı text'i 2x+ tokenize ediyorsan cache.

14.3 FFI overhead#

Python ↔ Rust FFI overhead ~3-10 microsecond per call. Küçük input'lar için bu önemli:
# YAVAŞ — 1M call x 5 μs = 5 sec overhead for sentence in corpus: tokens = tokenizer.encode(sentence) # HIZLI — 1 call, batch processing tokens_batch = tokenizer.encode_batch(corpus)
10x-100x hızlanma sadece batch ile.

14.4 Memory profile#

Tokenizer load: ~50-100 MB (vocab + merges). Encoding overhead: ~1 KB per encoding (token IDs + offsets). Production server: typical 200 MB RAM tokenizer için.

14.5 Multi-process worker pattern#

from multiprocessing import Pool def worker(text): return tokenizer.encode(text) with Pool(8) as p: results = p.map(worker, corpus)
Ama HF tokenizers zaten Rust thread-pool kullanıyor — `encode_batch` çoğu durumda yeterli, Python multiprocessing gereksiz.

14.6 vLLM / TGI integration#

Production inference server'lar (vLLM, TGI, SGLang) HF tokenizer'ı doğrudan kullanır. Custom pipeline gerekmiyor.

14.7 Streaming (real-time)#

LLM generation sırasında token-by-token decode:
for token_id in model_output_stream: text = tokenizer.decode([token_id]) print(text, end="", flush=True)
ByteLevel BPE'de partial decode tehlikesi: byte boundary'sinden ortadan kesilmiş UTF-8 char'lar. Çözüm: `skip_special_tokens=True` + ByteLevel decoder'ın native partial UTF-8 handling'ı.

15. Conversion — Diğer Formatlardan HF'ye#

15.1 tiktoken → HF#

Manuel inferred merges (complex). Community tool: `hf-internal/tiktoken-to-hf`.

15.2 SentencePiece → HF#

Kolay (transformers auto-conversion):
from transformers import AutoTokenizer tokenizer = AutoTokenizer.from_pretrained("google/mt5-base") # Internal: SentencePiece .model → HF tokenizer.json tokenizer.save_pretrained("./mt5-tokenizer")

15.3 OpenAI'dan kendi format'a#

OpenAI tokenizer indirilemez (closed). Conversion mümkün değil.

15.4 BERT vocab.txt → HF#

from tokenizers import BertWordPieceTokenizer tokenizer = BertWordPieceTokenizer("vocab.txt", lowercase=False) tokenizer.save("bert-tr.json")

15.5 GPT-2 encoder.json + vocab.bpe → HF#

from tokenizers import Tokenizer, models, pre_tokenizers, decoders, processors import json with open("encoder.json") as f: vocab = json.load(f) with open("vocab.bpe") as f: merges = [tuple(line.split()) for line in f.readlines()[1:] if line.strip()] tokenizer = Tokenizer(models.BPE(vocab=vocab, merges=merges)) tokenizer.pre_tokenizer = pre_tokenizers.ByteLevel(add_prefix_space=False) tokenizer.decoder = decoders.ByteLevel() tokenizer.post_processor = processors.ByteLevel(trim_offsets=False) tokenizer.save("gpt2-hf.json")

16. Benchmarks — Türkçe Karşılaştırma#

16.1 Fertility (token/word) — 1 sayfa Türkçe metin (300 kelime)#

TokenizerVocabTokenstoken/word
GPT-2 (r50k)50K~7202.40
GPT-4 (cl100k)100K~5101.70
GPT-4o (o200k)200K~4301.43
Llama-3128K~4401.47
TurkBPE-32k (custom)32K~3601.20
Trendyol-LLM32K~3601.20
BERT-base-Turkish32K~4651.55
Custom 32K Türkçe-only BPE → Llama-3'e göre %18 daha az token.

16.2 Encoding throughput (single thread)#

LibraryImplementationTokens/sec
Karpathy minbpePure Python~10K
sentencepieceC++~700K
HF tokenizersRust~900K
tiktokenRust~1.1M

16.3 Batch encoding (16 thread)#

HF tokenizers 16-core'da ~14M token/sec (near-linear scaling).

16.4 Memory#

Vocab boyutuRAM
32K~50 MB
100K~150 MB
200K~280 MB

16.5 Training time (10 GB Türkçe corpus, 16 core)#

  • BPE: ~25 min
  • WordPiece: ~30 min
  • Unigram: ~90 min (slower algorithm)

16.6 İndirme & startup time#

Production load:
  • Tokenizer.from_file(): ~50-200 ms (vocab parsing)
  • from_pretrained(): ilk kez ~3 sec (network + parse), cached ~100 ms

Egzersizler#

Egzersiz 1#

Türkçe corpus 5 GB için BPE training config: `vocab_size`, `min_frequency`, `initial_alphabet`, `max_token_length`, `special_tokens` ne olmalı? Sebep ile.

Egzersiz 2#

Normalizer pipeline: `Sequence([NFD(), StripAccents(), Lowercase()])` Türkçe için niye kötü? Doğru pipeline ne?

Egzersiz 3#

`add_prefix_space=True` ne anlama gelir? GPT-2 ByteLevel pre-tokenizer'da neden var?

Egzersiz 4#

tokenizer.json'da `"merges"` listesi sıralı. Sırasının önemi ne? İlk merge ile son merge arasında fark?

Egzersiz 5#

Production'da `encode()` 1M kez çağrılıyor, %FFI overhead ne kadar? `encode_batch()` ile karşılaştırma yap.

Egzersiz 6#

BpeTrainer'da `min_frequency=10` set ettin. Sonuç: vocab size hedeften küçük geldi. Niye? Nasıl düzeltirsin?

Egzersiz 7#

SentencePiece .model dosyasını HF tokenizer.json'a manuel convert et. Adımları yaz.

Egzersiz 8#

ByteLevel decoder'da "partial UTF-8" problemi ne? Streaming generation'da nasıl ortaya çıkar?

Egzersiz 9#

Custom tokenizer 32K vocab'a sahip. RAM kullanımı ölçüldüğünde 500 MB. Beklentin 50 MB civarı idi. Olası sebepler?

Egzersiz 10#

`tokenizer.train_from_iterator()` ile streaming corpus'tan training. Bellek dağılımı nasıl?
✅ Ders 6.8 Özeti — Production Tokenizer Mühendisliği
HuggingFace tokenizers Rust ile yazılmış, modern fiili standard. 6 katmanlı pipeline: Normalizer → PreTokenizer → Model → PostProcessor → Decoder → Trainer. Türkçe için NFC + ByteLevel + BPE + byte_fallback önerim. `tokenizer.json` format-portable. Rust internals: rayon parallel, ahash, SIMD, mmap → 1M token/sec single thread. Production: `encode_batch`, threading, caching, partial UTF-8 handling. tiktoken/SentencePiece conversion mümkün. Custom Türkçe 32K BPE → Llama-3'ten %18 daha az token. Modül 6.9'da tokenizer evaluation metriklerine geçeceğiz.

Sıradaki Ders: Tokenizer Evaluation#

Modül 6.9'da: fertility, compression ratio, OOV rate, perplexity downstream impact, cross-lingual fertility, A/B testing, bits-per-character information theory, tokenization 'tax' in cost terms, capstone evaluation framework — TurkTokenizer-tr için ölçüm araçları.

Sık Sorulan Sorular

İkisi de aynı Rust core'u kullanır. AutoTokenizer transformers integration için daha kolay (model + tokenizer birlikte). tokenizers library training + custom pipeline için daha güçlü. Production: model serving ise AutoTokenizer, custom training ise tokenizers direkt.

Yorumlar & Soru-Cevap

(0)
Yorum yazmak için giriş yap.
Yorumlar yükleniyor...

İlgili İçerikler