Tokenizery: BPE, modele na poziomie bajtów i ile kosztuje token
Jak BPE buduje słownik ze scaleń, dlaczego BPE na poziomie bajtów nigdy nie zawodzi na nieznanym tekście, ile rozmiar słownika kosztuje w parametrach, a ile oszczędza na długości sekwencji, dlaczego polski i kod kosztują więcej tokenów niż angielska proza i na jakim etapie są modele na poziomie bajtów bez tokenizera.
W LLM wszystko liczy się w tokenach: długość kontekstu, rozmiar cache KV, cena, szybkość. Ale „token” nie jest stałą jednostką. Jest tym, co za token uznał tokenizer, a ta decyzja ma swoje koszty na każdym kolejnym etapie.
BPE: słownik zbudowany ze scaleń
Kodowanie par bajtów (byte-pair encoding) w wersji, którą Sennrich i in. (2016) zaadaptowali do tłumaczenia neuronowego, zaczyna od pojedynczych symboli i raz po raz scala najczęstszą parę sąsiednich symboli w nowy symbol, aż słownik osiągnie docelowy rozmiar. Częste słowa stają się pojedynczymi tokenami. Rzadkie słowa są składane z mniejszych kawałków. Kodowanie nowego tekstu odtwarza wyuczone scalenia po kolei.
BPE na poziomie bajtów (GPT-2, Radford i in., 2019) zaczyna scalenia od 256 możliwych bajtów zamiast od znaków. Da się zakodować każdy ciąg, czy to tekst w dowolnym języku, emoji, czy binarne śmieci, bo w najgorszym razie kodowanie wraca do surowych bajtów. Nie ma „nieznanego tokenu”. Większość obecnych otwartych modeli używa jakiegoś wariantu BPE na poziomie bajtów. Druga popularna rodzina to SentencePiece z modelami BPE albo Unigram (Kudo, 2018).
Rozmiar słownika to kompromis
Większy słownik oznacza:
- Mniej tokenów na tekst. Każdy token obejmuje więcej znaków, więc ten sam dokument to krótsza sekwencja: mniejszy cache KV, mniej pracy dla atencji i więcej tekstu w oknie kontekstu.
- Więcej parametrów na obu końcach. Zarówno macierz embeddingów, jak i projekcja wyjściowa mają rozmiar
V × d. Przy słowniku o rozmiarze 152 064 i wymiarze ukrytym 4096 to 623 mln parametrów na macierz; przy wymiarze ukrytym 5120 to 779 mln. Jeśli te dwie macierze nie są współdzielone (tied), model płaci tę cenę dwa razy. W małym modelu to duża część całego budżetu. - Mniej treningu na token. Rzadkie tokeny model widzi rzadziej, więc ich embeddingi są uczone na mniejszej ilości danych.
Dlatego słowniki urosły z około 32K tokenów we wczesnych modelach Llama do dzisiejszych 128K–152K: dłuższe konteksty i tekst wielojęzyczny sprawiły, że droższą stroną stała się długość sekwencji.
Nie każdy tekst kosztuje tyle samo
Tokenizer wytrenowany głównie na angielskim pakuje angielski wydajnie, a wszystko inne słabiej. Dobrym przykładem jest polski. Znaki diakrytyczne zajmują w UTF-8 po dwa bajty: „Zażółć gęślą jaźń” ma 17 znaków, ale 26 bajtów, podczas gdy „The quick brown fox” ma 19 znaków i 19 bajtów. Polska fleksja tworzy też wiele form każdego słowa, więc mniej z nich staje się pojedynczymi tokenami. Ta sama treść kosztuje więcej tokenów, a to oznacza więcej pieniędzy, większy cache KV i krótszy efektywny kontekst niż ten sam tekst po angielsku.
Kod ma własne dziwactwa. Wcięcia, długie identyfikatory i składnia pełna symboli tokenizują się nierówno, a to, jak tokenizer traktuje ciągi spacji, mocno zmienia liczbę tokenów w kodzie. Gdy planujesz budżet kontekstu dla agenta, który pracuje na kodzie, mierz tokeny na swoich prawdziwych plikach, a nie na prozie.
Modele bez tokenizera
Modele na poziomie bajtów rezygnują z wyuczonego słownika i pracują na surowych bajtach:
- ByT5 (Xue i in., 2022): bajty od początku do końca, przez co sekwencje są dużo dłuższe.
- MegaByte (Yu i in., 2023): grupuje bajty we fragmenty (patches) o stałym rozmiarze.
- Byte Latent Transformer (Pagnoni i in., 2024): dobiera rozmiar fragmentów dynamicznie, zależnie od tego, jak przewidywalny jest następny bajt. Trudne miejsca dostają więcej obliczeń, łatwe mniej.
Zaletą jest odporność: brak artefaktów tokenizacji, jednakowe traktowanie każdego języka i brak słownika, który trzeba doczepiać do modelu. Ceną jest długość sekwencji i praca inżynierska potrzebna, żeby podział na fragmenty był wydajny. Napisałem blt, wydajny tokenizer na poziomie bajtów w Ruście. To w tej warstwie dużo wydajności zyskuje się albo traci, zanim model w ogóle zobaczy wejście.
Zasady, których się trzymam
- Mierz tokeny na własnych danych, zanim dobierzesz rozmiar kontekstu albo oszacujesz koszt. Proporcje zmierzone na angielskiej prozie nie przenoszą się na polski ani na kod.
- Nigdy nie mieszaj tokenizerów między treningiem a serwowaniem. Model jest zdefiniowany tylko razem ze swoim słownikiem.
- Wliczaj embeddingi, gdy porównujesz małe modele. Duży słownik może stanowić jedną trzecią parametrów małego modelu.
- Pilnuj krawędzi w wywołaniach narzędzi. Błędy tokenizacji najpierw pojawiają się w białych znakach i tokenach specjalnych wokół JSON-a.
Źródła: Sennrich, Haddow i Birch, Neural Machine Translation of Rare Words with Subword Units (2016). Radford i in., Language Models are Unsupervised Multitask Learners (GPT-2, 2019). Kudo, Subword Regularization (2018). Xue i in., ByT5 (2022). Yu i in., MegaByte (2023). Pagnoni i in., Byte Latent Transformer (2024). Liczby bajtów policzono dla przytoczonych ciągów.