Update model card
Browse files
README.md
CHANGED
|
@@ -3,7 +3,7 @@ license: apache-2.0
|
|
| 3 |
language:
|
| 4 |
- tr
|
| 5 |
- en
|
| 6 |
-
library_name:
|
| 7 |
tags:
|
| 8 |
- tokenizer
|
| 9 |
- tokenizers
|
|
@@ -11,14 +11,12 @@ tags:
|
|
| 11 |
- turkish
|
| 12 |
- english
|
| 13 |
- bilingual
|
| 14 |
-
|
| 15 |
-
- wikimedia/wikipedia
|
| 16 |
-
- Helsinki-NLP/opus-100
|
| 17 |
---
|
| 18 |
|
| 19 |
# Multrenizer
|
| 20 |
|
| 21 |
-
|
| 22 |
|
| 23 |
## Links
|
| 24 |
|
|
@@ -27,139 +25,32 @@ Multrenizer is a bilingual English-Turkish Unigram tokenizer built from scratch
|
|
| 27 |
|
| 28 |
## Why Multrenizer?
|
| 29 |
|
| 30 |
-
Standard multilingual tokenizers
|
| 31 |
|
| 32 |
-
|
| 33 |
|
| 34 |
-
|
| 35 |
-
- Apostrophe preservation: forms like `feature'ı`, `merge'lemek`, `İstanbul'da`, and `can't` keep `'` as a real token
|
| 36 |
-
- Compact vocabulary budget: `~26K` target vocab for a Turkish-first bilingual tokenizer
|
| 37 |
-
- Fixed utility budget: dedicated punctuation, emoji, math, currency, and chat symbols
|
| 38 |
-
- Code-switching support: trained on mixed TR-EN text instead of treating it as noise
|
| 39 |
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
- Multrenizer's shipped local artifact is auto-read from `multrenizer-tokenizer/tokenizer.json`; the current released artifact is `25,917` tokens.
|
| 47 |
-
- Example token strings for byte-level models are shown as raw tokenizer pieces. Metrics are based on exact token counts, not prettified decoding.
|
| 48 |
-
|
| 49 |
-
### Compared Tokenizers
|
| 50 |
-
|
| 51 |
-
| Tokenizer | Source | Vocab Size | Algorithm | Type |
|
| 52 |
-
|---|---|---:|---|---|
|
| 53 |
-
| **Multrenizer** | This project | **25,917** | Unigram | Bilingual EN-TR, purpose-built |
|
| 54 |
-
| **Kumru-2B** | [vngrs-ai/Kumru-2B](https://huggingface.co/vngrs-ai/Kumru-2B) | 50,176 | BPE | Turkish LLM (VNGRS, Sep 2025, Mistral-based) |
|
| 55 |
-
| **Turkcell-7B** | [TURKCELL/Turkcell-LLM-7b-v1](https://huggingface.co/TURKCELL/Turkcell-LLM-7b-v1) | 48,351 | BPE | Turkish LLM (Turkcell, Apr 2024, Mistral-based) |
|
| 56 |
-
| **GPT-2** | [openai-community/gpt2](https://huggingface.co/openai-community/gpt2) | 50,257 | BPE | English-centric baseline (OpenAI, 2019) |
|
| 57 |
-
| **Qwen-3** | [Qwen/Qwen3-0.6B](https://huggingface.co/Qwen/Qwen3-0.6B) | 151,643 | BPE | Multilingual (Alibaba, 2025) |
|
| 58 |
-
| **Mistral-3.1** | [mistralai/Mistral-Small-3.1-24B-Base-2503](https://huggingface.co/mistralai/Mistral-Small-3.1-24B-Base-2503) | 131,072 | BPE/SP | Multilingual (Mistral AI, Mar 2025) |
|
| 59 |
-
|
| 60 |
-
### Fertility, Compression, and Token Count
|
| 61 |
-
|
| 62 |
-
Lower fertility means fewer tokens per word. Higher compression means more characters carried per token.
|
| 63 |
|
| 64 |
-
|
| 65 |
-
|---|:---:|:---:|:---:|:---:|:---:|:---:|
|
| 66 |
-
| Vocab Size | **25,917** | 50,176 | 48,351 | 50,257 | 151,643 | 131,072 |
|
| 67 |
-
| **TR Fertility** | **1.627** | 1.649 | 1.917 | 3.785 | 2.616 | 2.384 |
|
| 68 |
-
| EN Fertility | 1.525 | 2.151 | 1.555 | **1.314** | 1.372 | 1.381 |
|
| 69 |
-
| **CS Fertility** | **1.756** | 1.923 | 1.832 | 3.475 | 2.445 | 2.479 |
|
| 70 |
-
| **TR Compression** | **4.783** | 4.719 | 4.060 | 2.056 | 2.976 | 3.265 |
|
| 71 |
-
| EN Compression | 4.148 | 2.942 | 4.068 | **4.816** | 4.610 | 4.580 |
|
| 72 |
-
| **TR Total Tokens (5K)** | **130,844** | 132,637 | 154,166 | 304,345 | 210,334 | 191,682 |
|
| 73 |
-
| EN Total Tokens (5K) | 157,027 | 221,420 | 160,121 | **135,235** | 141,275 | 142,196 |
|
| 74 |
-
| **CS Total Tokens (500)** | **5,525** | 6,050 | 5,762 | 10,933 | 7,693 | 7,799 |
|
| 75 |
-
|
| 76 |
-
Current position:
|
| 77 |
-
|
| 78 |
-
- Best Turkish efficiency in this comparison set: TR fertility, TR compression, TR total tokens
|
| 79 |
-
- Best code-switching efficiency in this comparison set: CS fertility and CS total tokens
|
| 80 |
-
- Competitive English coverage for a Turkish-first tokenizer, but not better than English-native GPT-2 on EN-only token count
|
| 81 |
-
- Only tokenizer here that passes Turkish `I/i` normalization correctly
|
| 82 |
-
|
| 83 |
-
### Morphological Splitting
|
| 84 |
-
|
| 85 |
-
Total tokens needed to represent 10 difficult Turkish words:
|
| 86 |
-
|
| 87 |
-
| Tokenizer | Vocab Size | Total Tokens | Avg per Word |
|
| 88 |
-
|---|---:|:---:|:---:|
|
| 89 |
-
| **Multrenizer** | **25,917** | **32** | **3.2** |
|
| 90 |
-
| Kumru-2B | 50,176 | 35 | 3.5 |
|
| 91 |
-
| Turkcell-7B | 48,351 | 38 | 3.8 |
|
| 92 |
-
| Mistral-3.1 | 131,072 | 71 | 7.1 |
|
| 93 |
-
| Qwen-3 | 151,643 | 73 | 7.3 |
|
| 94 |
-
| GPT-2 | 50,257 | 105 | 10.5 |
|
| 95 |
-
|
| 96 |
-
Selected examples:
|
| 97 |
|
| 98 |
```text
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
İstanbul'da
|
| 108 |
-
Multrenizer: istanbul + ' + da [3 tokens]
|
| 109 |
-
Kumru-2B: İstanbul + ' + da [3 tokens]
|
| 110 |
-
Turkcell-7B: İstanbul + ' + da [3 tokens]
|
| 111 |
-
Qwen-3: İ + stanbul + 'd + a [4 tokens]
|
| 112 |
-
Mistral-3.1: İ + stanbul + 'd + a [4 tokens]
|
| 113 |
-
GPT-2: Ä + ° + stanbul + 'd + a [5 tokens]
|
| 114 |
-
|
| 115 |
-
Afyonkarahisarlılaştıramadıklarımızdan
|
| 116 |
-
Multrenizer: afyonkarahisar + lı + laştı + r + ama + dıkları + mızda + n [8 tokens]
|
| 117 |
-
Kumru-2B: Af + yonkarahisar + lı + laÅŁtır + ama + dık + larımız + dan [8 tokens]
|
| 118 |
-
Turkcell-7B: Afyon + kar + ah + is + arlı + laştır + a + madık + larımızdan [9 tokens]
|
| 119 |
-
Qwen-3: Af + yon + kar + ah + is + ar + lı + la + ÅŁt + ı + ram + ad + ıkl + ar + ımız + dan [16 tokens]
|
| 120 |
-
Mistral-3.1: Af + yon + kar + ah + is + arl + ı + laÅŁt + ı + ram + ad + ıkları + m + ı + zd + an [16 tokens]
|
| 121 |
-
GPT-2: Af + yon + kar + ah + is + arl + ı + la + ÅŁ + t + ı + ram + ad + ı + k + lar + ı + m + ı + z + dan [21 tokens]
|
| 122 |
```
|
| 123 |
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
This is the critical locale-sensitive test:
|
| 127 |
-
|
| 128 |
-
- `İ` must lowercase to `i`
|
| 129 |
-
- `I` must lowercase to `ı`
|
| 130 |
-
|
| 131 |
-
| Input | Expected | Multrenizer | Kumru-2B | Turkcell-7B | GPT-2 | Qwen-3 | Mistral-3.1 |
|
| 132 |
-
|---|---|:---:|:---:|:---:|:---:|:---:|:---:|
|
| 133 |
-
| İstanbul | istanbul | **OK** | FAIL | FAIL | FAIL | FAIL | FAIL |
|
| 134 |
-
| IŞIK | ışık | **OK** | FAIL | FAIL | FAIL | FAIL | FAIL |
|
| 135 |
-
| SIR | sır | **OK** | FAIL | FAIL | FAIL | FAIL | FAIL |
|
| 136 |
-
| İNSAN | insan | **OK** | FAIL | FAIL | FAIL | FAIL | FAIL |
|
| 137 |
-
| ISITMAK | ısıtmak | **OK** | FAIL | FAIL | FAIL | FAIL | FAIL |
|
| 138 |
-
| **Score** | | **8/8** | **0/8** | **0/8** | **0/8** | **0/8** | **0/8** |
|
| 139 |
-
|
| 140 |
-
Multrenizer is the only tokenizer in this comparison that handles Turkish casing correctly.
|
| 141 |
-
|
| 142 |
-
### Code-Switching
|
| 143 |
-
|
| 144 |
-
```text
|
| 145 |
-
"Bu feature'ı implement ederken edge case'leri handle etmeyi unutmayalım."
|
| 146 |
-
|
| 147 |
-
Multrenizer [15 tok] bu | feature | ' | ı | implement | ederken | edge | case | ' | leri | handle | etmeyi | unutmaya | lım | .
|
| 148 |
-
Kumru-2B [20 tok] Bu | fe | ature | ' | ı | imp | lement | ederken | ed | ge | cas | e | ' | leri | hand | le | etmeyi | unutma | yalım | .
|
| 149 |
-
Turkcell-7B [15 tok] Bu | feature | ' | ı | implement | ederken | edge | case | ' | leri | handle | etmeyi | unut | mayalım | .
|
| 150 |
-
GPT-2 [24 tok] Bu | feature | ' | ı | implement | ed | er | ken | edge | case | ' | ler | i | handle | et | me | yi | un | ut | may | al | ı | m | .
|
| 151 |
-
Qwen-3 [22 tok] Bu | feature | ' | ı | implement | ed | er | ken | edge | case | ' | leri | handle | et | m | ey | i | un | ut | may | alım | .
|
| 152 |
-
Mistral-3.1 [20 tok] Bu | feature | 'ı | implement | eder | ken | edge | case | ' | leri | handle | et | me | yi | un | ut | may | al | ım | .
|
| 153 |
-
|
| 154 |
-
"merge'lemek istediğim branch conflict veriyor."
|
| 155 |
-
|
| 156 |
-
Multrenizer [ 8 tok] merge | ' | lemek | istediğim | branch | conflict | veriyor | .
|
| 157 |
-
Kumru-2B [14 tok] mer | ge | ' | lemek | istediÄŁim | b | ran | ch | con | f | lic | t | veriyor | .
|
| 158 |
-
Turkcell-7B [ 8 tok] merge | ' | lemek | istediğim | branch | conflict | veriyor | .
|
| 159 |
-
GPT-2 [16 tok] mer | ge | ' | lem | ek | is | ted | i | ÄŁ | im | branch | conflict | ver | iy | or | .
|
| 160 |
-
Qwen-3 [11 tok] merge | ' | lem | ek | istediÄŁ | im | branch | conflict | ver | iyor | .
|
| 161 |
-
Mistral-3.1 [13 tok] merge | ' | le | mek | ist | edi | ÄŁ | im | branch | conflict | ver | iyor | .
|
| 162 |
-
```
|
| 163 |
|
| 164 |
## Quick Start
|
| 165 |
|
|
@@ -180,45 +71,39 @@ from tokenizers import Tokenizer
|
|
| 180 |
|
| 181 |
tok = Tokenizer.from_file("multrenizer-tokenizer/tokenizer.json")
|
| 182 |
|
| 183 |
-
encoded = tok.encode("İstanbul'da
|
| 184 |
print(encoded.tokens)
|
| 185 |
-
# ['<s>', '
|
| 186 |
|
| 187 |
-
|
| 188 |
-
|
|
|
|
| 189 |
```
|
| 190 |
|
| 191 |
### Load from Hugging Face
|
| 192 |
|
| 193 |
-
```python
|
| 194 |
-
from tokenizers import Tokenizer
|
| 195 |
-
|
| 196 |
-
tok = Tokenizer.from_pretrained("fzengin18/multrenizer")
|
| 197 |
-
|
| 198 |
-
encoded = tok.encode("İstanbul'da güzel bir gün")
|
| 199 |
-
print(encoded.tokens)
|
| 200 |
-
# ['<s>', 'istanbul', "'", 'da', 'güzel', 'bir', 'gün', '</s>']
|
| 201 |
-
```
|
| 202 |
-
|
| 203 |
-
If you use `transformers`, this also works:
|
| 204 |
-
|
| 205 |
```python
|
| 206 |
from transformers import AutoTokenizer
|
| 207 |
|
| 208 |
tok = AutoTokenizer.from_pretrained("fzengin18/multrenizer")
|
| 209 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 210 |
```
|
| 211 |
|
| 212 |
### Train from scratch
|
| 213 |
|
| 214 |
```bash
|
| 215 |
-
# 1. Download and prepare corpus
|
| 216 |
-
python prepare_data.py --size
|
| 217 |
|
| 218 |
# 2. Train tokenizer
|
| 219 |
python train_tokenizer.py --data-dir data/
|
| 220 |
|
| 221 |
-
# 3. Optional: push
|
| 222 |
python train_tokenizer.py --data-dir data/ \
|
| 223 |
--repo-id fzengin18/multrenizer \
|
| 224 |
--hf-token "$HF_TOKEN"
|
|
@@ -232,82 +117,200 @@ python benchmark.py --tr-lines 5000 --en-lines 5000
|
|
| 232 |
|
| 233 |
## Architecture
|
| 234 |
|
| 235 |
-
###
|
| 236 |
|
| 237 |
-
```
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
-> Pre-tokenizer (whitespace + apostrophe + punctuation split)
|
| 245 |
-
-> Unigram model (~26K target vocab)
|
| 246 |
-
-> Post-processor (<s> ... </s>)
|
| 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 |
-
Current
|
| 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 |
-
|
|
| 304 |
-
|
|
| 305 |
-
|
|
| 306 |
-
|
|
| 307 |
-
|
|
| 308 |
-
|
|
| 309 |
-
|
| 310 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 311 |
|
| 312 |
## Project Structure
|
| 313 |
|
|
@@ -317,11 +320,14 @@ multrenizer/
|
|
| 317 |
│ ├── tokenizer.json
|
| 318 |
│ ├── tokenizer_config.json
|
| 319 |
│ └── special_tokens_map.json
|
| 320 |
-
├── prepare_data.py # Corpus download
|
| 321 |
├── train_tokenizer.py # Tokenizer training script
|
| 322 |
-
├── benchmark.py # Benchmark
|
| 323 |
├── benchmark_results.json # Full benchmark output
|
| 324 |
-
├── tests/
|
|
|
|
|
|
|
|
|
|
| 325 |
├── requirements.txt
|
| 326 |
└── pyproject.toml
|
| 327 |
```
|
|
|
|
| 3 |
language:
|
| 4 |
- tr
|
| 5 |
- en
|
| 6 |
+
library_name: tokenizers
|
| 7 |
tags:
|
| 8 |
- tokenizer
|
| 9 |
- tokenizers
|
|
|
|
| 11 |
- turkish
|
| 12 |
- english
|
| 13 |
- bilingual
|
| 14 |
+
- sentencepiece
|
|
|
|
|
|
|
| 15 |
---
|
| 16 |
|
| 17 |
# Multrenizer
|
| 18 |
|
| 19 |
+
A bilingual Turkish-English Unigram tokenizer with **lossless round-trip decode**, **case preservation**, and a **correct utility-token taxonomy** — punctuation, currency, math symbols, and emoji are kept atomic in the vocabulary AND preserved through `decode(skip_special_tokens=True)`.
|
| 20 |
|
| 21 |
## Links
|
| 22 |
|
|
|
|
| 25 |
|
| 26 |
## Why Multrenizer?
|
| 27 |
|
| 28 |
+
Standard multilingual tokenizers commonly fail on Turkish in three ways: (1) they break agglutinative morphemes at suboptimal boundaries, (2) they fold case (`İstanbul` → `istanbul`) so a downstream model can never produce proper nouns again, and (3) they misclassify punctuation and emoji as "special tokens", which means `decode(skip_special_tokens=True)` silently strips them and the model output ends up missing commas, apostrophes, and 😀.
|
| 29 |
|
| 30 |
+
Multrenizer is built to fix all three without giving up Turkish efficiency.
|
| 31 |
|
| 32 |
+
Core design:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
|
| 34 |
+
- **Case preserving.** `İstanbul` stays `İstanbul`, `TÜRKİYE` stays `TÜRKİYE`. The model learns to produce real proper nouns, not lowercased shadows.
|
| 35 |
+
- **Lossless round-trip.** Metaspace pipeline with `prepend_scheme="first"` plus `AddedToken(normalized=True)` — `decode(encode(x)) == x` for case, punctuation, currency, math, and compound emoji.
|
| 36 |
+
- **Correct utility taxonomy.** Only model-control symbols (`<s>`, `<|user|>`, `<|reserved_*|>`, etc.) are flagged `special=True`. Punctuation, currency, math symbols, and emoji are atomic vocab entries with `special=False` — they survive `skip_special_tokens=True`.
|
| 37 |
+
- **Compact vocabulary.** `~26K` total budget, Turkish-first morpheme coverage.
|
| 38 |
+
- **Code-switching aware.** Trained on a TR/EN/CS interleave; mixed text like `merge'lemek istediğim branch` segments cleanly.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
|
| 40 |
+
## Pipeline
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
|
| 42 |
```text
|
| 43 |
+
Raw text
|
| 44 |
+
-> Quote canonicalization (’ ‘ ʼ ' -> ')
|
| 45 |
+
-> NFKC normalization
|
| 46 |
+
-> Strip whitespace
|
| 47 |
+
-> Pre-tokenizer: Metaspace(replacement="▁", prepend_scheme="first", split=True)
|
| 48 |
+
-> Unigram model (~26K vocab)
|
| 49 |
+
-> Decoder: Metaspace(prepend_scheme="first") [mirror of pre-tokenizer]
|
| 50 |
+
-> Post-processor (<s> ... </s>)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
```
|
| 52 |
|
| 53 |
+
No lowercase. No locale-specific I/i replacement (it would be a workaround for a bug Python introduces only when you call `.lower()`, which we don't).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
|
| 55 |
## Quick Start
|
| 56 |
|
|
|
|
| 71 |
|
| 72 |
tok = Tokenizer.from_file("multrenizer-tokenizer/tokenizer.json")
|
| 73 |
|
| 74 |
+
encoded = tok.encode("İstanbul'da %50 indirim 😀")
|
| 75 |
print(encoded.tokens)
|
| 76 |
+
# ['<s>', "▁İstanbul'da", '▁%', '50', '▁indirim', '▁', '😀', '</s>']
|
| 77 |
|
| 78 |
+
# Round-trip is lossless — case, punctuation, emoji all survive.
|
| 79 |
+
print(tok.decode(encoded.ids))
|
| 80 |
+
# "İstanbul'da %50 indirim 😀"
|
| 81 |
```
|
| 82 |
|
| 83 |
### Load from Hugging Face
|
| 84 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
```python
|
| 86 |
from transformers import AutoTokenizer
|
| 87 |
|
| 88 |
tok = AutoTokenizer.from_pretrained("fzengin18/multrenizer")
|
| 89 |
+
ids = tok.encode("Türkiye'de %50 indirim, 100₺ ❤️", add_special_tokens=False)
|
| 90 |
+
|
| 91 |
+
# `skip_special_tokens=True` removes ONLY <...>-formatted control tokens.
|
| 92 |
+
# Punctuation, currency, and emoji are preserved.
|
| 93 |
+
print(tok.decode(ids, skip_special_tokens=True))
|
| 94 |
+
# "Türkiye'de %50 indirim, 100₺ ❤️"
|
| 95 |
```
|
| 96 |
|
| 97 |
### Train from scratch
|
| 98 |
|
| 99 |
```bash
|
| 100 |
+
# 1. Download and prepare corpus (Wikipedia TR+EN, OPUS-100, synthetic CS)
|
| 101 |
+
python prepare_data.py --size large
|
| 102 |
|
| 103 |
# 2. Train tokenizer
|
| 104 |
python train_tokenizer.py --data-dir data/
|
| 105 |
|
| 106 |
+
# 3. Optional: push to Hugging Face Hub
|
| 107 |
python train_tokenizer.py --data-dir data/ \
|
| 108 |
--repo-id fzengin18/multrenizer \
|
| 109 |
--hf-token "$HF_TOKEN"
|
|
|
|
| 117 |
|
| 118 |
## Architecture
|
| 119 |
|
| 120 |
+
### Token Taxonomy
|
| 121 |
|
| 122 |
+
| Tier | Count | `special` flag | Behavior on `skip_special_tokens=True` |
|
| 123 |
+
|---|---:|:---:|---|
|
| 124 |
+
| Named specials (`<s>`, `<\|user\|>`, `<think>`, ...) | 32 | `true` | **Removed** |
|
| 125 |
+
| Reserved (`<\|reserved_0\|>` ... `<\|reserved_511\|>`) | 512 | `true` | **Removed** |
|
| 126 |
+
| Utility — punctuation/symbols (`,`, `!`, `'`, `%`, `$`, `→`) | 169 | `false` | **Preserved** |
|
| 127 |
+
| Utility — emoji (corpus-learned + curated fallback) | ~240 | `false` | **Preserved** |
|
| 128 |
+
| Learned subwords (Unigram) | ~24,700 | `false` | **Preserved** |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
|
| 130 |
+
This is the central correctness fix vs the prior artifact, where 292 utility tokens were incorrectly flagged `special=true` and erased on every decode.
|
| 131 |
|
| 132 |
+
### Vocabulary Budget
|
| 133 |
+
|
| 134 |
+
Target: `26,000`. Actual shipped artifact: `25,679` (Unigram convergence undershoot is normal).
|
| 135 |
+
|
| 136 |
+
The model trained downstream should set its `vocab_size` to **the tokenizer's actual size**, not the 26K target.
|
| 137 |
+
|
| 138 |
+
### Special Tokens
|
| 139 |
|
| 140 |
+
| Category | IDs | Tokens |
|
| 141 |
|---|---|---|
|
| 142 |
+
| **Core** | 0-3 | `<unk>` `<s>` `</s>` `<pad>` |
|
| 143 |
+
| **Chat** | 4-8 | `<\|system\|>` `<\|user\|>` `<\|assistant\|>` `<\|end\|>` `<\|sep\|>` |
|
| 144 |
+
| **Reasoning** | 9-12 | `<think>` `</think>` `<\|step\|>` `<\|reflection\|>` |
|
| 145 |
+
| **Tool Use** | 13-16 | `<tool_call>` `</tool_call>` `<tool_response>` `</tool_response>` |
|
| 146 |
+
| **Code/FIM** | 17-20 | `<\|code\|>` `<\|fim_prefix\|>` `<\|fim_middle\|>` `<\|fim_suffix\|>` |
|
| 147 |
+
| **Language** | 21-22 | `<\|tr\|>` `<\|en\|>` |
|
| 148 |
+
| **RAG** | 23-24 | `<\|context\|>` `<\|/context\|>` |
|
| 149 |
+
| **Multi-modal** | 25-28 | `<\|image\|>` `<\|audio\|>` `<\|video\|>` `<\|file\|>` |
|
| 150 |
+
| **Structured** | 29-31 | `<\|json\|>` `<\|table\|>` `<\|cite\|>` |
|
| 151 |
+
| **Reserved** | 32-543 | `<\|reserved_0\|>` ... `<\|reserved_511\|>` |
|
| 152 |
+
|
| 153 |
+
Look these up at runtime with `tokenizer.token_to_id("<...>")` — do not hardcode IDs in downstream code.
|
| 154 |
+
|
| 155 |
+
### Utility Tokens (special=False, atomic)
|
| 156 |
+
|
| 157 |
+
| Category | Approx. count | Examples |
|
| 158 |
+
|---|---:|---|
|
| 159 |
+
| Punctuation | 38 | `. , ! ? ; : - ( ) [ ] { } / \ " ' ... – — … ‽ ‼ ⁇ ¿ ¡` |
|
| 160 |
+
| Currency & business | 23 | `₺ $ € £ ¥ ₹ ₽ ¢ ฿ ₪ ₸ ₣ ₮ ₩ % ‰ ° § ¶ № @ # &` |
|
| 161 |
+
| Math & science | 33 | `± × ÷ ≠ ≤ ≥ ≈ ∞ √ ∑ ∫ ∂ Δ π α β γ δ ε θ λ μ σ φ ω ∀ ∃ ∈ ⊂ ⊆ ∪ ∩ ⇔ ↦ ∝ ∇ ∮ ∧ ∨ ¬ Α Β Γ Σ Φ Ω` |
|
| 162 |
+
| Programming digraphs | 11 | `=> != == <= >= -> :: ** // && \|\|` |
|
| 163 |
+
| Arrows & symbols | 15 | `→ ← ↑ ↓ ↔ ⇒ • · ★ ☆ ✓ ✗ © ® ™` |
|
| 164 |
+
| Box drawing & UI | 17 | `─ │ ┌ ┐ └ ┘ ├ ┤ ┬ ┴ ┼ ▪ ▫ ◆ ▶ ◀ ▼ ▲` |
|
| 165 |
+
| Typography | 10 | `« » " " ' ' ‹ › „ ‚` |
|
| 166 |
+
| Emoji — faces | 70 | `😀 😂 🤣 😊 😍 🤔 😭 😡 💀 🤖` |
|
| 167 |
+
| Emoji — hands | 28 | `👋 👍 👎 👏 🙏 💪 ✊ ✌️` |
|
| 168 |
+
| Emoji — hearts | 18 | `❤️ 💛 💚 💙 💜 🖤 💔` |
|
| 169 |
+
| Emoji — symbols | 36 | `🔥 ✨ ⭐ ✅ ❌ ⚠️ 💯 🚀` |
|
| 170 |
+
| Emoji — objects | 36 | `💻 📱 🎯 🏆 📊 ☕ 🔗 💰` |
|
| 171 |
+
| Emoji — nature & common | 17 | `🌍 🎉 🎁 ⏰ 📅 📌 🚨 🛒 💳 📞 📧 🏻🏼🏽🏾🏿` |
|
| 172 |
+
| Emoji — flags | 26 | `🇹🇷 🇺🇸 🇬🇧 🇩🇪 🇫🇷 🇨🇳 🇰🇷 🇮🇳 🇸🇦 🇮🇱 🇷🇺 🇧🇷 🇨🇦 🇲🇽 🇦🇺 🇳🇱` |
|
| 173 |
+
| Emoji — compounds (ZWJ/VS16 sequences) | 12 | `❤️ 👨💻 👩💻 🏃♂️ 🏃♀️ 👨👩👧 👨🏫 👩🏫 👍🏻–👍🏿` |
|
| 174 |
+
|
| 175 |
+
ZWJ (`U+200D`) and VS16 (`U+FE0F`) are NOT added as standalone tokens; they exist only inside whole compound-emoji sequences.
|
| 176 |
|
| 177 |
+
### Data Mix
|
| 178 |
|
| 179 |
+
The released artifact is trained on the corpus produced by `prepare_data.py --size large`:
|
| 180 |
|
| 181 |
+
| Stream | Approx. lines | Share | Source |
|
| 182 |
+
|---|---:|:---:|---|
|
| 183 |
+
| Turkish | 1.30M | ~59% | Wikipedia TR streaming |
|
| 184 |
+
| English | 0.71M | ~32% | Wikipedia EN streaming |
|
| 185 |
+
| Code-switching | 0.20M | ~9% | OPUS-100 EN-TR pairs + synthetic templates |
|
| 186 |
|
| 187 |
+
An additional 5K synthetic emoji-rich CS lines are appended to ensure curated emojis make it into the learned vocabulary instead of the post-train fallback list.
|
| 188 |
|
| 189 |
+
## Correctness Tests
|
| 190 |
|
| 191 |
+
The repo ships a pytest suite (`tests/test_tokenizer_correctness.py`) covering:
|
| 192 |
|
| 193 |
+
1. **Round-trip lossless decode** — `decode(encode(x)) == normalize(x)` over Turkish, English, and code-switching samples.
|
| 194 |
+
2. **`skip_special_tokens=True` preserves utility** — punctuation, currency, emoji must survive; only `<...>`-formatted control tokens are removed.
|
| 195 |
+
3. **Compound emoji round-trip** — VS16, ZWJ, regional-indicator pairs decode back to the original sequence.
|
| 196 |
+
4. **Turkish morpheme integrity** — words like `şehirdir`, `kitabımı`, `evlerinden` decode without internal whitespace.
|
| 197 |
+
5. **UNK rate < 0.5%** on a bilingual bench (line-by-line).
|
| 198 |
+
6. **Special token ID stability** — IDs 0-7 (`<unk>`, `<s>`, `</s>`, `<pad>`, `<|system|>`, `<|user|>`, `<|assistant|>`, `<|end|>`).
|
| 199 |
+
7. **Case preservation** — `İstanbul`, `TÜRKİYE`, `ABD`, `AKP` all round-trip without folding.
|
| 200 |
|
| 201 |
+
Current artifact: **60/60 PASS**.
|
| 202 |
|
| 203 |
+
## Benchmark Results
|
| 204 |
|
| 205 |
+
Evaluated on `5,000` Turkish sentences, `5,000` English sentences, and `500` code-switching sentences from the prepared corpus, against five reference tokenizers.
|
| 206 |
+
|
| 207 |
+
### Compared Tokenizers
|
| 208 |
+
|
| 209 |
+
| Tokenizer | Source | Vocab Size | Algorithm | Type |
|
| 210 |
+
|---|---|---:|---|---|
|
| 211 |
+
| **Multrenizer** | This project | **25,679** | Unigram | Bilingual EN-TR, purpose-built |
|
| 212 |
+
| **Kumru-2B** | [vngrs-ai/Kumru-2B](https://huggingface.co/vngrs-ai/Kumru-2B) | 50,176 | BPE | Turkish LLM (VNGRS, Sep 2025, Mistral-based) |
|
| 213 |
+
| **Turkcell-7B** | [TURKCELL/Turkcell-LLM-7b-v1](https://huggingface.co/TURKCELL/Turkcell-LLM-7b-v1) | 48,351 | BPE | Turkish LLM (Turkcell, Apr 2024, Mistral-based) |
|
| 214 |
+
| **GPT-2** | [openai-community/gpt2](https://huggingface.co/openai-community/gpt2) | 50,257 | BPE | English-centric baseline (OpenAI, 2019) |
|
| 215 |
+
| **Qwen-3** | [Qwen/Qwen3-0.6B](https://huggingface.co/Qwen/Qwen3-0.6B) | 151,643 | BPE | Multilingual (Alibaba, 2025) |
|
| 216 |
+
| **Mistral-3.1** | [mistralai/Mistral-Small-3.1-24B-Base-2503](https://huggingface.co/mistralai/Mistral-Small-3.1-24B-Base-2503) | 131,072 | BPE/SP | Multilingual (Mistral AI, Mar 2025) |
|
| 217 |
+
|
| 218 |
+
### Fertility, Compression, and Token Count
|
| 219 |
+
|
| 220 |
+
Lower fertility = fewer tokens per word. Higher compression = more characters per token.
|
| 221 |
+
|
| 222 |
+
| Metric | Multrenizer | Kumru-2B | Turkcell-7B | GPT-2 | Qwen-3 | Mistral-3.1 |
|
| 223 |
+
|---|:---:|:---:|:---:|:---:|:---:|:---:|
|
| 224 |
+
| Vocab Size | **25,679** | 50,176 | 48,351 | 50,257 | 151,643 | 131,072 |
|
| 225 |
+
| **TR Fertility** | **1.63** | 1.65 | 1.92 | 3.79 | 2.62 | 2.38 |
|
| 226 |
+
| EN Fertility | 1.51 | 2.15 | 1.55 | **1.31** | 1.37 | 1.38 |
|
| 227 |
+
| **TR Compression** | **4.79** | 4.72 | 4.06 | 2.06 | 2.98 | 3.27 |
|
| 228 |
+
| EN Compression | 4.18 | 2.94 | 4.07 | **4.82** | 4.61 | 4.58 |
|
| 229 |
+
| **TR Total Tokens (5K sent)** | **130,725** | 132,637 | 154,166 | 304,345 | 210,334 | 191,682 |
|
| 230 |
+
| EN Total Tokens (5K sent) | 155,848 | 221,420 | 160,121 | **135,235** | 141,275 | 142,196 |
|
| 231 |
+
| Round-trip Lossless | 11/11 | 11/11 | 11/11 | 11/11 | 11/11 | 11/11 |
|
| 232 |
+
|
| 233 |
+
Headline:
|
| 234 |
+
|
| 235 |
+
- **Best Turkish efficiency** in this set on every TR metric (fertility, compression, total tokens), with the smallest vocabulary by a wide margin.
|
| 236 |
+
- **Competitive English** — second only to GPT-2 on EN total tokens, and that's an English-native baseline trained on far more English text.
|
| 237 |
+
- **Round-trip lossless on all 11 cases.** This is a minimum-correctness bar; modern BPE tokenizers also pass it. The previous Multrenizer artifact did **not** (it dropped utility tokens via `skip_special_tokens=True`); fixing that is one of the main reasons this version exists.
|
| 238 |
+
|
| 239 |
+
### Morphological Splitting
|
| 240 |
+
|
| 241 |
+
Total tokens needed to represent ten difficult Turkish words:
|
| 242 |
+
|
| 243 |
+
| Tokenizer | Vocab Size | Total Tokens | Avg per Word |
|
| 244 |
+
|---|---:|:---:|:---:|
|
| 245 |
+
| Kumru-2B | 50,176 | **35** | **3.5** |
|
| 246 |
+
| **Multrenizer** | **25,679** | **36** | **3.6** |
|
| 247 |
+
| Turkcell-7B | 48,351 | 38 | 3.8 |
|
| 248 |
+
| Mistral-3.1 | 131,072 | 71 | 7.1 |
|
| 249 |
+
| Qwen-3 | 151,643 | 73 | 7.3 |
|
| 250 |
+
| GPT-2 | 50,257 | 105 | 10.5 |
|
| 251 |
+
|
| 252 |
+
A few selected words:
|
| 253 |
+
|
| 254 |
+
```text
|
| 255 |
+
İstanbul'da
|
| 256 |
+
Multrenizer [1 tok] İstanbul'da ← single token, atomic
|
| 257 |
+
Kumru-2B [3 tok] İstanbul + ' + da
|
| 258 |
+
Turkcell-7B [3 tok] İstanbul + ' + da
|
| 259 |
+
GPT-2 [5 tok] Ä + ° + stanbul + 'd + a
|
| 260 |
+
Qwen-3 [4 tok] İ + stanbul + 'd + a
|
| 261 |
+
Mistral-3.1 [4 tok] İ + stanbul + 'd + a
|
| 262 |
+
|
| 263 |
+
Afyonkarahisarlılaştıramadıklarımızdan
|
| 264 |
+
Multrenizer [8 tok] Afyonkarahisar + lı + laştı + ram + a + dıkları + mızda + n
|
| 265 |
+
Kumru-2B [8 tok] Af + yonkarahisar + lı + laştır + ama + dık + larımız + dan
|
| 266 |
+
Turkcell-7B [9 tok] Afyon + kar + ah + is + arlı + laştır + a + madık + larımızdan
|
| 267 |
+
Qwen-3 [16 tok] (16-piece byte-level fragmentation)
|
| 268 |
+
Mistral-3.1 [16 tok] (16-piece byte-level fragmentation)
|
| 269 |
+
GPT-2 [21 tok] (21-piece byte-level fragmentation)
|
| 270 |
+
|
| 271 |
+
düşünülebileceğini
|
| 272 |
+
Multrenizer [2 tok] düşünül + ebileceğini
|
| 273 |
+
Kumru-2B [3 tok] düşünül + ebil + eceğini
|
| 274 |
+
Turkcell-7B [4 tok] düş + ünü + le + bileceğini
|
| 275 |
+
GPT-2 [12 tok] d + ü + ş + ü + n + ü + le + b + ile + ce + ğ + ini
|
| 276 |
+
```
|
| 277 |
+
|
| 278 |
+
### Round-Trip Lossless Decode
|
| 279 |
+
|
| 280 |
+
11 representative inputs covering case preservation, apostrophe handling, currency, math/measurement symbols, VS16 / ZWJ / regional-indicator emoji, and mixed-content sentences. Multrenizer passes 11/11. All compared modern tokenizers also pass — the value here is being on the right side of the bar (the previous Multrenizer artifact wasn't), not winning a race.
|
| 281 |
+
|
| 282 |
+
```text
|
| 283 |
+
Input Multrenizer ...
|
| 284 |
+
İstanbul'da güzel bir gün geçirdim. OK
|
| 285 |
+
TÜRKİYE büyük harflerle yazılır. OK
|
| 286 |
+
It's a beautiful day, isn't it? OK
|
| 287 |
+
Fiyat: 100₺ ve $50, €30 ve £25. OK
|
| 288 |
+
Türkiye'de %50 indirim ve %25 KDV. OK
|
| 289 |
+
Hava 22°C, yağış %30, basınç 1013hPa. OK
|
| 290 |
+
❤️ ile 💔 farklı duygular ifade eder. OK
|
| 291 |
+
🇹🇷 ve 🇺🇸 bayrakları yan yana asıldı. OK
|
| 292 |
+
Geliştirici 👨💻 olarak çalışıyorum. OK
|
| 293 |
+
Selam 👋! Türkiye'de %50 indirim 100₺. OK
|
| 294 |
+
```
|
| 295 |
+
|
| 296 |
+
### Code-Switching Tokenization
|
| 297 |
+
|
| 298 |
+
```text
|
| 299 |
+
"Bu feature'ı implement ederken edge case'leri handle etmeyi unutmayalım."
|
| 300 |
+
|
| 301 |
+
Multrenizer [12 tok] Bu | feature | 'ı | implement | ederken | edge | case | 'leri | handle | etmeyi | unutmay | alım.
|
| 302 |
+
Kumru-2B [20 tok] Bu | fe | ature | ' | ı | imp | lement | ederken | ed | ge | cas | e | ' | leri | hand | le | etmeyi | unutma | yalım | .
|
| 303 |
+
Turkcell-7B [15 tok] Bu | feature | ' | ı | implement | ederken | edge | case | ' | leri | handle | etmeyi | unut | mayalım | .
|
| 304 |
+
|
| 305 |
+
"merge'lemek istediğim branch conflict veriyor."
|
| 306 |
+
|
| 307 |
+
Multrenizer [ 7 tok] merge'le | mek | istediğim | branch | conflict | veriyor | .
|
| 308 |
+
Kumru-2B [14 tok] mer | ge | ' | lemek | istediğim | b | ran | ch | con | f | lic | t | veriyor | .
|
| 309 |
+
Turkcell-7B [ 8 tok] merge | ' | lemek | istediğim | branch | conflict | veriyor | .
|
| 310 |
+
GPT-2 [16 tok] mer | ge | ' | lem | ek | is | ted | i | ğ | im | branch | conflict | ver | iy | or | .
|
| 311 |
+
```
|
| 312 |
+
|
| 313 |
+
> Full benchmark output (all sections, all sentences, machine-readable) is in `benchmark_results.json`. Regenerate with `python benchmark.py`.
|
| 314 |
|
| 315 |
## Project Structure
|
| 316 |
|
|
|
|
| 320 |
│ ├── tokenizer.json
|
| 321 |
│ ├── tokenizer_config.json
|
| 322 |
│ └── special_tokens_map.json
|
| 323 |
+
├── prepare_data.py # Corpus download (Wikipedia + OPUS-100 + synthetic CS)
|
| 324 |
├── train_tokenizer.py # Tokenizer training script
|
| 325 |
+
├── benchmark.py # Benchmark vs 5 reference tokenizers
|
| 326 |
├── benchmark_results.json # Full benchmark output
|
| 327 |
+
├── tests/
|
| 328 |
+
│ └── test_tokenizer_correctness.py # Round-trip + utility + ID-stability suite
|
| 329 |
+
├── RETRAIN_PLAN.md # Design / pivot history (audit trail)
|
| 330 |
+
├── MINDAI_INTEGRATION.md # Downstream integration recipe
|
| 331 |
├── requirements.txt
|
| 332 |
└── pyproject.toml
|
| 333 |
```
|