File size: 9,302 Bytes
a7cf8a7 f26b725 a7cf8a7 a34a31c a7cf8a7 4079ffe a7cf8a7 4079ffe eb271df 4079ffe eb271df 4079ffe a858237 4079ffe eb271df 4079ffe eb271df 4079ffe a858237 4079ffe eb271df 4079ffe eb271df a7cf8a7 a858237 a7cf8a7 4079ffe a7cf8a7 eb271df a7cf8a7 a858237 a7cf8a7 f26b725 a34a31c a7cf8a7 4079ffe a7cf8a7 f26b725 a7cf8a7 4079ffe a7cf8a7 | 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 | ---
language:
- hu
license: apache-2.0
library_name: tokenizers
tags:
- picur
- tokenizer
- hungarian
- magyar
- morphology
- sentencepiece
- unigram
---
# picur-tokenizer
A 49,152-piece SentencePiece Unigram tokenizer for Hungarian,
fitted so that it segments on real morpheme boundaries,
rather than on whatever the compression objective prefers.
```python
from transformers import AutoTokenizer
tok = AutoTokenizer.from_pretrained("picur/picur-tokenizer")
tok.tokenize("a házakban lakó emberekkel")
# ['▁a', '▁ház', 'ak', 'ban', '▁lakó', '▁ember', 'ek', 'kel']
```
## Why another tokenizer
> - Nevezz meg három állatot.
> - Kutyácska, lovacska, verebecske.
> - Jó, de kicsinyítő képző nélkül.
> - Kecs, macs, szar.
Hungarian is agglutinative: one stem carries a long tail of suffixes, so `ház` (house) surfaces as `házban`, `házakban`, `házaimban`.
A tokenizer fitted on compression alone shatters that paradigm across unrelated rows,
and the model then has to learn `ház` several times over - once per spelling of the same word.
Training effort that should have gone into knowledge goes into re-learning morphology instead.
This vocabulary is fitted to keep a stem on one row and let the suffixes be their own pieces,
so a paradigm shares its knowledge instead of spreading it.
## How it segments
| word | pieces | |
|---|---|---|
| `házakban` | `▁ház` `ak` `ban` | stem + plural + inessive |
| `könyvekkel` | `▁könyv` `ek` `kel` | stem + plural + instrumental |
| `emberekkel` | `▁ember` `ek` `kel` | the same two suffixes, the same rows |
| `magyarokat` | `▁magyar` `ok` `at` | back-vowel harmony of the pair above |
| `csapadékvíz` | `▁csapadék` `víz` | compound, cut at the declared seam |
| `tavak` | `▁tav` `ak` | v-stem alternation (`tó` -> `tav`) |
Vowel harmony is why `ban`/`ben`, `kel`/`kal` and `hoz`/`hez`/`höz` all exist as separate rows:
they are the same grammatical function wearing different vowels.
See [Tied rows](#tied-rows) for what is shipped to help a model learn them as one.
## Measured
Every number below is reproducible from public artifacts by `benchmark.py` in this repository:
```
pip install transformers datasets
python benchmark.py
```
It reads a pinned UniMorph commit, five Hugging Face datasets at a fixed document count, and five published tokenizers.
Nothing from the pipeline that produced this vocabulary is involved.
### Paradigm sharing
The property this vocabulary is built for.
On [UniMorph Hungarian](https://github.com/unimorph/hun) first 20,000 lemmas in file order: 19,424 lemmas, 1,014,882 lemma/form pairs:
| tokenizer | vocab | **one stem row + suffixes** | prefix kept | lemma is one token | tokens/form |
|---|---|---|---|---|---|
| **picur/picur-tokenizer** | 49,152 | **45.4%** | **73.1%** | **55.1%** | 3.04 |
| NYTK/PULI-GPT-3SX | 50,000 | 24.5% | 54.9% | 38.5% | 2.98 |
| SZTAKI-HLT/hubert-base-cc | 32,001 | 23.6% | 55.5% | 28.1% | 3.07 |
| FacebookAI/xlm-roberta-base | 250,002 | 10.8% | 60.4% | 12.8% | 3.66 |
| Qwen/Qwen3-8B | 151,643 | 2.0% | 54.9% | 3.1% | 5.29 |
**one stem row + suffixes** asks both halves of the question at once:
the lemma is a SINGLE row, and its inflected form begins with that row.
So every mention of the word teaches one embedding.
Neither half can be won on its own. Cutting everything small satisfies `prefix kept` and destroys `lemma is one token`.
Swallowing whole inflected forms does the reverse.
Only the conjunction says a paradigm shares a row, and it does so at the same token cost per form as the closest baseline.
### Fertility
Characters a token buys, on the first 2,000 documents of each dataset:
| corpus | picur | PULI-GPT-3SX | huBERT | XLM-R | Qwen3 |
|---|---|---|---|---|---|
| `lazos/gutenberg-hu` | 3.740 | **4.050** | 3.915 | 3.381 | 2.417 |
| `lazos/gazette-hu` | 2.960 | **3.808** | 3.665 | 3.392 | 1.997 |
| `SZTAKI-HLT/HunSum-2-abstractive` | 4.001 | **4.544** | 4.487 | 3.803 | 2.367 |
| `open-web-math/open-web-math` | **2.953** | 2.394 | 2.385 | 3.412 | 3.669 |
| `bigcode/starcoderdata` (python) | **2.498** | 1.721 | 2.454 | 3.205 | 4.121 |
**On Hungarian prose the two Hungarian baselines beat this tokenizer, and that is the trade, not a defect.**
PULI buys more characters per token by cutting `házakban` into `ház` `akban`, keeping stem and endings fused;
this vocabulary spends one more token to cut it into `▁ház` `ak` `ban`, and gets the paradigm table above in return.
Where the comparison is not about Hungarian morphology it inverts:
on mathematics and code this vocabulary beats both Hungarian baselines,
because a slice of each was in the fit corpus so technical text does not fall back to bytes.
Qwen wins those outright, as an English-first tokenizer should.
### Special tokens
| id | token | |
|---|---|---|
| 0 | `<\|unk\|>` | |
| 1 | `<\|bos\|>` | one per document |
| 2 | `<\|eos\|>` | |
| 3 | `<\|pad\|>` | |
| 4 | `<\|im_start\|>` | ChatML turn open |
| 5 | `<\|im_end\|>` | ChatML turn close; the stop token for chat |
| 6-21 | `<\|reserved_0..15\|>` | unused, for later extension |
Tool-call markers (`<tools>`, `<tool_call>`, `<tool_response>` and their closers) are present as ordinary pieces, following the Hermes convention.
Note the ChatML split: `<|im_end|>` terminates a *turn*, `<|eos|>` terminates a *sequence*. A chat model should stop on `<|im_end|>`.
### Tied rows
`ties.json` maps 1,357 allomorph rows onto the one row a model should learn first: `ban` -> `ben`, `höz` -> `hoz`, across 100 suffix families.
Vowel harmony makes these variants of one morpheme,
so initialising their embeddings from a shared base gives the model the paradigm for free rather than making it discover the relationship from data.
This is an initialisation aid, not part of tokenization, nothing reads it automatically.
## How it was fitted
The vocabulary is overfitted well past its target size and then **selected down**,
with a declared Hungarian lexicon deciding what survives:
1. **Seed** - 32,888 pieces required up front: 28,000 frequent stems, 1,000 named entities,
1,000 capitalised forms, 432 suffixes, 2,005 alternating stems and 448 programming keywords.
2. **Overfit** - a large Unigram vocabulary is trained on the corpus.
3. **Evict** - any piece that is a regular inflection of a declared headword is removed. `magyarokat` becomes `▁magyar` `ok` `at`:
the whole ending `okat` is not a row, while the plural `ok` and the accusative `at` each are, and every other paradigm reuses them.
4. **Spare** - an eviction is reversed if the word would then segment worse than its morphology says it should.
5. **Prune** - rows the corpus never reaches are dropped.
Corpus: 700 Mchar drawn across 57 Hungarian sources:
encyclopedic, news, legal, parliamentary, academic, literary, subtitles, web and forum.
Plus small code and mathematics slices, so that technical text does not fall back to bytes.
## Limitations
- **Hungarian-first.** English and code are handled but not optimized; other languages fall back to bytes.
- **Coverage, not rules, sets the floor.** A stem outside the 28,000-stem seed can still be mis-split,
and an alternating stem can lose on score: `művek` cuts as `mű` + `vek` where morphology says `műv` + `ek`.
- **Compound seams come from a declared lexicon**, so a compound absent from it gets no guaranteed seam.
- **Cased entity handling is partial.** 1,000 capitalised forms are seeded; beyond those, capitalisation costs a row.
- **Two losses are declared, not accidental.** Metaspace writes the word mark as
U+2581, so a literal U+2581 in the input is indistinguishable from a space. The
tokenizer declares a `Replace(U+2581, " ")` normalizer ahead of Metaspace, which
makes the collision part of the contract instead of a surprise - text carrying
block-drawing characters (a sparkline, say) round-trips with that one character
turned into a space. Leading whitespace on an input is dropped for the same
reason: Metaspace prepends a mark unconditionally. Everything else round-trips
byte-exactly, including every script outside Latin, via byte fallback.
## Attribution
This tokenizer is a derivative work of the following resouces, see `NOTICE`.
- **[morphdb.hu](https://github.com/morphdb/morphdb.hu)** - CC-BY 2.5, (c) Nemeth
Laszlo and Godo Ferenc. The declared lexicon: 351,917 headwords, 37,042 compound
seams, alternation classes and vowel harmony. This decided what the fit protects
and what it evicts.
- **[hunmorph-foma](https://github.com/r0ller/hunmorph-foma)** - BSD-2-Clause,
(c) 2016 r0ller. Finite-state transducers that analysed corpus words into lemma
plus tags, bootstrapping the segmentation this vocabulary is scored against.
- **[UniMorph Hungarian](https://github.com/unimorph/hun)** - CC BY-SA 3.0, derived
from English Wiktionary. Used by `benchmark.py` only: it is scored against and
never built from, which is what makes the paradigm table above independent of
everything that produced the vocabulary.
The tokenizer itself is Apache-2.0.
## Citation
```bibtex
@misc{picur-tokenizer,
title = {picur-tokenizer: a morphology-aware Hungarian tokenizer},
author = {Bencz, Lajos},
year = {2026},
url = {https://huggingface.co/picur/picur-tokenizer}
}
```
|