Tokenizace — BPE, WordPiece a zpracování textu
Tokenizace je klíčový proces přípravy textu pro AI modely, který rozděluje text na menší jednotky. Seznamte se s algoritmy BPE a WordPiece a pochopte, jak funguje zpracování textu v moderních jazykových modelech.
Tokenizace v moderním NLP: od slov k subwords
Tokenizace je základním krokem každého NLP systému. Zatímco tradiční přístupy rozdělují text na slova podle mezer, moderní modely jako GPT nebo BERT využívají pokročilejší techniky jako Byte-Pair Encoding (BPE) a WordPiece. Tyto algoritmy dokážou elegantně řešit problém out-of-vocabulary (OOV) slov a efektivně reprezentovat rozsáhlé slovníky.
Proč klasická tokenizace nestačí
Představte si, že trénujete model na anglických textech a narazíte na slovo "unhappiness". Klasický word-based tokenizer by toto slovo buď zařadil do slovníku (pokud se vyskytuje dostatečně často), nebo by ho označil jako neznámé token <UNK>. Oba přístupy mají zásadní nevýhody:
- Velký slovník zabírá více paměti a zpomaluje trénink
- Neznámé tokeny způsobují ztrátu informace
- Model se nedokáže naučit morfologii a slovotvorbu
Moderní subword tokenizace řeší tyto problémy rozdělením slov na menší významové jednotky.
Byte-Pair Encoding (BPE)
BPE původně vzniklo jako kompresní algoritmus, ale našlo uplatnění v NLP. Algoritmus funguje následovně:
# Jednoduchá implementace BPE
def get_pairs(vocab):
"""Získá všechny páry sousedních symbolů"""
pairs = {}
for word, freq in vocab.items():
symbols = word.split()
for i in range(len(symbols) - 1):
pair = (symbols[i], symbols[i + 1])
pairs[pair] = pairs.get(pair, 0) + freq
return pairs
def merge_vocab(pair, vocab):
"""Sloučí nejčastější pár symbolů"""
new_vocab = {}
bigram = ' '.join(pair)
replacement = ''.join(pair)
for word in vocab:
new_word = word.replace(bigram, replacement)
new_vocab[new_word] = vocab[word]
return new_vocab
BPE začíná s vocabulářem jednotlivých znaků a iterativně slučuje nejčastější páry. Pro slovo "unhappiness" by proces mohl vypadat takto:
# Původní stav "u n h a p p i n e s s" # Po několika iteracích "un hap p i ness" # Finální tokenizace ["un", "hap", "pi", "ness"]
WordPiece algoritmus
WordPiece, používané v BERT, je podobné BPE, ale s důležitým rozdílem. Místo výběru nejčastějších párů vybírá páry, které maximalizují likelihood trénovacích dat. Používá také speciální prefix "##" pro označení sub-tokenů, které neznačují začátek slova:
# WordPiece tokenizace "unhappiness" → ["un", "##hap", "##pi", "##ness"] "playing" → ["play", "##ing"] "hello" → ["hello"] # časté slovo zůstane celé
Praktická implementace s Hugging Face
V praxi většinou využijeme hotové implementace. Hugging Face Transformers poskytuje jednoduché API:
from transformers import AutoTokenizer
# GPT-2 používá BPE
gpt2_tokenizer = AutoTokenizer.from_pretrained("gpt2")
text = "Tokenizace je důležitá pro NLP"
tokens = gpt2_tokenizer.tokenize(text)
print(tokens)
# ['Token', 'iz', 'ace', ' je', ' d', 'ů', 'ležitá', ' pro', ' NL', 'P']
# BERT používá WordPiece
bert_tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
tokens = bert_tokenizer.tokenize("unhappiness")
print(tokens)
# ['un', '##hap', '##pi', '##ness']
Trénování vlastního BPE tokenizeru
Pro specializované domény často potřebujeme vlastní tokenizer. SentencePiece je populární knihovna pro tento účel:
import sentencepiece as spm
# Trénování BPE modelu
spm.SentencePieceTrainer.train(
input='training_data.txt',
model_prefix='my_bpe',
vocab_size=32000,
model_type='bpe',
character_coverage=0.9995,
split_by_unicode_script=True,
split_by_number=True
)
# Načtení a použití
sp = spm.SentencePieceProcessor(model_file='my_bpe.model')
tokens = sp.encode('Vlastní tokenizer pro česká data')
print(tokens)
print(sp.decode(tokens))
Optimalizace pro konkrétní jazyky
Čeština má specifika, která ovlivňují tokenizaci. Bohatá morfologie, diakritika a relativně volný slovosled vyžadují pozornost:
# Příklad problémů s češtinou text = "Programování, programuju, naprogramoval" # Špatně nastavený tokenizer může vytvořit: # ["Program", "ování", ",", " program", "uju", ",", " na", "program", "oval"] # Lepší tokenizace by rozpoznala kořen: # ["programo", "##vání", ",", " programo", "##ju", ",", " na", "##programo", "##val"]
Pro lepší výsledky s češtinou doporučujeme:
- Vyšší character_coverage (0.9999) kvůli diakritice
- Předtrénované modely jako czert-b-base-cased
- Preprocessing pro normalizaci diakritiky pokud to úkol dovoluje
Výkon a paměťové nároky
Volba velikosti slovníku je kompromis mezi výkonem a kvalitou. Větší slovník znamená:
- Kratší sekvence tokenů → rychlejší inference
- Větší embedding matice → vyšší paměťové nároky
- Více parametrů → pomalejší trénink
# Analýza tokenizace
def analyze_tokenization(tokenizer, texts):
total_tokens = 0
total_chars = 0
for text in texts:
tokens = tokenizer.tokenize(text)
total_tokens += len(tokens)
total_chars += len(text)
compression_ratio = total_chars / total_tokens
print(f"Kompresní poměr: {compression_ratio:.2f} znaků/token")
return compression_ratio
# Porovnání různých tokenizérů
gpt2_ratio = analyze_tokenization(gpt2_tokenizer, sample_texts)
bert_ratio = analyze_tokenization(bert_tokenizer, sample_texts)
Shrnutí
Tokenizace je kritický první krok každého NLP pipeline. BPE a WordPiece algoritmy elegantně řeší problém OOV slov a umožňují efektivní reprezentaci textů. Při výběru tokenizeru je třeba zvážit specifika cílového jazyka, velikost dat a výkonnostní požadavky. Pro češtinu doporučujeme využít předtrénované modely nebo pečlivě nastavit parametry při trénování vlastního tokenizeru.