Puntata 206
Puntata 206 — Tokenizer
Livello: 🧙 Maestro Yoda · Capitolo 19 · Architetture
Prima del modello c’è il tokenizer. Sembra un dettaglio implementativo. È la decisione che condiziona ogni altra metrica.
Perché esiste un tokenizer
I modelli non leggono testo. Leggono sequenze di interi, ognuno indice in un vocabolario fisso. Il tokenizer è la funzione str → list[int] (e l’inversa detokenizer: list[int] → str).
Le tre opzioni storiche:
- Caratteri / byte. Vocabolario piccolissimo (256 byte, ~150k unicode codepoint). Sequenze lunghissime. Modello molto compute-hungry.
- Parole intere. Vocabolario enorme (milioni). Out-of-vocabulary (OOV) costanti. Disastro per lingue agglutinanti.
- Sotto-parole. Compromesso. Frammenti più grandi di un byte, più piccoli di una parola. Vocabolario 30k–200k. È quello che usiamo da BERT in poi.
I tre algoritmi di sotto-parole che dominano sono BPE, WordPiece, Unigram (SentencePiece). Più una tendenza recente al byte-level.
BPE (Byte Pair Encoding)
Algoritmo nato per compressione (Gage, 1994), riproposto per NLP (Sennrich et al., 2015).
Idea: parti dai singoli caratteri come vocabolario base, poi cerca la coppia di simboli adiacenti più frequente nel corpus e fondila in un nuovo simbolo. Ripeti fino a raggiungere la dimensione di vocabolario desiderata.
Esempio giocattolo (corpus: "low low low lower lower newer newer newer wider"):
- Iniziale:
l, o, w, e, r, n, i, d,</w> - Merge più frequente:
(l, o) → lo - Poi:
(lo, w) → low - Poi:
(e, r) → er - …
Risultato: parole comuni diventano token interi (low, er), parole rare restano frammentate (unprecedented → un, precedent, ed).
Usato in: GPT-2/3/4/5 (via tiktoken), RoBERTa, BART, e in moltissimi modelli open. Variant byte-level (BBPE): opera sui byte UTF-8, non sui codepoint, eliminando l’OOV per qualsiasi unicode.
WordPiece
Usato da BERT (e originariamente da Google Translate). Simile al BPE ma sceglie i merge massimizzando la verosimiglianza del corpus invece della frequenza.
Differenza pratica:
- BPE:
tokenization→token, ization - WordPiece:
tokenization→token, ##ization(il##segnala “questo è continuazione”)
Computazionalmente più costoso da addestrare, ma le decisioni di merge sono in media migliori. Oggi marginale: la maggior parte dei modelli moderni è BPE byte-level.
Unigram (SentencePiece)
Il default di Google (T5, mT5) e di Mistral, Llama, Gemma. Implementato dalla libreria SentencePiece (Kudo, 2018).
Idea opposta a BPE: parti da un vocabolario iniziale grande (tutti i sub-string frequenti), poi togli progressivamente quelli che impattano meno la likelihood del corpus, fino a target size.
Ulteriore vantaggio: SentencePiece tratta lo spazio come carattere normale (lo rappresenta con ▁), quindi non assume tokenizzazione preliminare. Funziona uguale per inglese, cinese, giapponese, codice, mix.
Tipico in: T5 family, Llama (1, 2, 3 — Llama-3 è passato a BPE custom), Mistral, Gemma.
Tiktoken e i tokenizer custom degli ultimi modelli
tiktoken è la libreria open di OpenAI (pip install tiktoken), molto veloce, usata da GPT-3.5+ e GPT-4/5. Implementa BPE byte-level con vocabolari diversi per modello:
| Modello | Encoder | Vocab size |
|---|---|---|
| GPT-2 | gpt2 | 50.257 |
| GPT-3.5 / GPT-4 | cl100k_base | 100.277 |
| GPT-4o / GPT-5 | o200k_base | ~200.000 |
| Claude (Anthropic) | tokenizer proprietario simil-BPE | ~100k–200k |
| Llama 3 / 4 | BPE custom | 128.000 |
| Gemma 2 / 3 | SentencePiece, BPE-style | 256.000 |
| DeepSeek V3 | BPE byte-level custom | ~129.000 |
La tendenza 2024–2026: vocabolari più grandi (128k → 256k). Trade-off:
- Pro: meno token per stessa quantità di testo → context più “denso”, inferenza più rapida.
- Contro: matrice di embedding più grande → più memoria, più parametri “morti” su token rari.
Quanto costa il tokenizer al tuo conto
Pratico, da sapere a memoria:
- Inglese: ~4 caratteri / token. “Hello, world.” → 3-4 token.
- Italiano: ~3.5 caratteri / token. Penalità ~15% rispetto all’inglese.
- Codice: variabile. Linguaggi verbose (Java, C++) sono peggio.
- Cinese/Giapponese con
cl100k: 1 token può essere 1 carattere o anche meno. Penalità anche 3-4× vs inglese. - Lingue agglutinanti (finnico, turco): frammentazione pesante, +30%.
Conseguenza diretta: i prezzi “$X per 1M token” non sono equi tra lingue. Chi scrive in finlandese paga di più, a parità di significato.
I tokenizer più moderni (200k+ vocab) hanno mitigato il problema per le lingue maggiori, ma persistono asimmetrie strutturali.
Effetti collaterali del tokenizer
Casi reali che hanno generato bug visibili:
- Numeri. GPT-3 tokenizzava i numeri in modo strano (es.
5325→5,325). Risultato: addizione disastrosa. Llama-3 e GPT-4o hanno specifically scelto di tokenizzare le cifre una per una per migliorare aritmetica. - Token “fantasma”. GPT-2/3 avevano token derivati da username di forum (
SolidGoldMagikarpè il caso famoso). Prompt che contenevano questi token producevano output completamente fuori distribuzione. - Spazi e punteggiatura. Variazioni come
" hello"vs"hello"vs"hello "sono token diversi. Prompt sensibili allo spazio iniziale è un gotcha classico. - Spelling. Chiedere a un modello “quante ‘r’ ci sono in strawberry?” è andato male per anni proprio per la tokenizzazione. I reasoning model recenti lo fanno bene perché ragionano carattere per carattere.
Multilingua: la frontiera del 2026
Modelli come mT5, NLLB, Aya (Cohere) usano tokenizer specifici multilingua con vocabolari grossi (~256k–512k) addestrati su corpora bilanciati.
Esiste anche la corrente byte-level pura (es. ByT5, MEGABYTE, BLT — Meta 2024): no tokenizer, il modello mangia byte UTF-8. Costo computazionale alto (sequenze ~4× più lunghe), ma:
- Niente OOV mai, su nessuna scrittura.
- Robustezza a typo, encoding strani, emoji nuove.
- Pretraining “language-agnostic” più facile.
Nel 2026 BLT (Byte Latent Transformer) di Meta ha mostrato che si può recuperare il costo via patching dinamico: il modello impara da solo quando aggregare byte in unità più grandi.
Glossario lampo
- OOV — Out Of Vocabulary. Token mai visti al training.
- Special tokens — token di servizio:
<|endoftext|>,<|im_start|>,<s>,</s>. Non testo, sono “metadata in-band”. - Detokenizer — l’inverso: da ID a stringa. Non sempre lossless.
- Sub-word — pezzo di parola, intermedio tra carattere e parola intera.
TEST hands-on
pip install tiktokene prova:
Confronta le lunghezze. Spiega le differenze.import tiktoken enc = tiktoken.get_encoding("o200k_base") print(enc.encode("Ciao, sono un assistente AI.")) print(enc.encode("こんにちは、私はAIアシスタントです。")) print(enc.encode("import numpy as np; print(np.pi)"))- Apri
huggingface.co/spaces/Xenova/the-tokenizer-playground(web) e prova lo stesso testo su 5 tokenizer diversi (GPT-2, GPT-4o, Llama-3, Mistral, Gemma). Scrivi cosa cambia. - Calcola il “costo italiano” del tuo prompt: prendi un testo italiano di 1000 caratteri, tokenizzalo con
cl100k_base. Quanti token? Confronta col fattore atteso (~3.5 char/token = ~285 token). - Bonus: addestra un BPE da zero su un corpus tuo con
sentencepieceotokenizers(HF). 1M righe sono sufficienti. Confronta il vocabolario emerso con quello di Llama-3.
Take-away
Il tokenizer non è “preprocessing”. È un’architettura nascosta: definisce l’alfabeto su cui ogni operazione successiva opera. Una decisione di tokenizzazione viaggia col modello per sempre, è quasi impossibile cambiarla a posteriori senza re-pretrain. Pensaci quando scegli un modello per una lingua o un dominio.
➡️ Prossima puntata: Positional encoding — RoPE, ALiBi, e perché senza posizioni l’attention è una nuvola.