Tokenizacja i podstawy NLP: BPE, SentencePiece i WordPiece
Tokenizacja to proces dzielenia tekstu na podjednostki (tokeny), które mogą być przetwarzane przez model. Ten proces, stanowiący fundament nowoczesnych LLM, bezpośrednio wpływa na wydajność modelu.
Czym jest tokenizacja?
Tokenizacja to pierwszy krok w przekształcaniu surowego tekstu w reprezentacje numeryczne:
"Hello world!" → ["Hello", "world", "!"] → [1234, 5678, 99]
Poziomy tokenizacji
- Poziom znaków: Każdy znak jest tokenem.
- Poziom słów: Każde słowo jest tokenem.
- Poziom subword: Słowa są dzielone na mniejsze podjednostki (podejście współczesne).
Tokenizacja na poziomie słów
Proste podejście
1def word_tokenize(text): 2 return text.split() 3 4# Example 5text = "Artificial intelligence is shaping the future" 6tokens = word_tokenize(text) 7# ['Artificial', 'intelligence', 'is', 'shaping', 'the', 'future']
Problemy
- OOV (Out of Vocabulary): Napotkanie słów niewidzianych podczas trenowania.
- Duży słownik: Zarządzanie setkami tysięcy słów jest nieefektywne.
- Bogata morfologia: W językach takich jak turecki liczba odmian wyrazów jest ogromna.
- Złożenia wyrazowe: Trudność w określeniu, czy „Artificial intelligence” to jeden koncept czy dwa.
Tokenizacja na poziomie znaków
1def char_tokenize(text): 2 return list(text) 3 4# Example 5text = "Hello" 6tokens = char_tokenize(text) 7# ['H', 'e', 'l', 'l', 'o']
Zalety
- Brak problemu OOV.
- Mały rozmiar słownika (~100 znaków).
Wady
- Bardzo długie sekwencje wynikowe.
- Utrata kontekstowego znaczenia na poziomie tokenów.
- Wyższy koszt obliczeniowy dla modelu.
Tokenizacja subword
Wybór nowoczesnych LLM: balans między poziomem słów a znaków.
"tokenization" → ["token", "ization"] "unhappiness" → ["un", "happiness"] lub ["un", "happy", "ness"]
BPE (Byte Pair Encoding)
Najpowszechniej stosowany algorytm tokenizacji subword.
Algorytm BPE
- Podziel tekst na pojedyncze znaki.
- Znajdź najczęściej występującą parę sąsiadujących znaków.
- Połącz tę parę w nowy token.
- Powtarzaj aż do osiągnięcia pożądanego rozmiaru słownika.
Przykład BPE
1Starting vocabulary: ['l', 'o', 'w', 'e', 'r', 'n', 's', 't', 'i', 'd'] 2Corpus: "low lower newest lowest widest" 3 4Step 1: Most frequent pair 'e' + 's' → 'es' 5Step 2: Most frequent pair 'es' + 't' → 'est' 6Step 3: Most frequent pair 'l' + 'o' → 'lo' 7Step 4: Most frequent pair 'lo' + 'w' → 'low' 8... 9 10Final Result: ['low', 'est', 'er', 'new', 'wid', ...]
Implementacja BPE
1def get_stats(vocab): 2 pairs = {} 3 for word, freq in vocab.items(): 4 symbols = word.split() 5 for i in range(len(symbols) - 1): 6 pair = (symbols[i], symbols[i + 1]) 7 pairs[pair] = pairs.get(pair, 0) + freq 8 return pairs 9 10def merge_vocab(pair, vocab): 11 new_vocab = {} 12 bigram = ' '.join(pair) 13 replacement = ''.join(pair) 14 for word in vocab: 15 new_word = word.replace(bigram, replacement) 16 new_vocab[new_word] = vocab[word] 17 return new_vocab 18 19def train_bpe(corpus, num_merges): 20 vocab = get_initial_vocab(corpus) 21 22 for i in range(num_merges): 23 pairs = get_stats(vocab) 24 if not pairs: 25 break 26 best_pair = max(pairs, key=pairs.get) 27 vocab = merge_vocab(best_pair, vocab) 28 29 return vocab
WordPiece
Algorytm opracowany przez Google i wykorzystywany w modelach takich jak BERT.
BPE vs WordPiece
| Feature | BPE | WordPiece |
|---|---|---|
| Merge Criterion | Frequency | Likelihood |
| Prefix | None | ## (for mid-word tokens) |
| Used In | GPT, LLaMA | BERT, DistilBERT |
Przykład WordPiece
1"tokenization" → ["token", "##ization"] 2"playing" → ["play", "##ing"] 3## SentencePiece 4 5Tokenizer niezależny od języka, również opracowany przez Google. 6 7### Features 8 9- **Language Independent:** Nie zakłada, że spacja jest separatorem słów. 10- **Byte-level:** Działa bezpośrednio na surowym tekście. 11- **BPE + Unigram:** Obsługuje wiele algorytmów. 12- **Reversible:** Możliwa jest idealna detokenizacja. 13 14### SentencePiece Usage 15 16```python 17import sentencepiece as spm 18 19# Training the model 20spm.SentencePieceTrainer.train( 21 input='corpus.txt', 22 model_prefix='tokenizer', 23 vocab_size=32000, 24 model_type='bpe' # or 'unigram' 25) 26 27# Loading and using the model 28sp = spm.SentencePieceProcessor() 29sp.load('tokenizer.model') 30 31# Encode 32tokens = sp.encode('Hello world', out_type=str) 33# ['▁Hello', '▁world'] 34 35ids = sp.encode('Hello world', out_type=int) 36# [1234, 5678, 9012] 37 38# Decode 39text = sp.decode(ids) 40# 'Hello world'
▁ (Underscore) Symbol
SentencePiece oznacza początek słów za pomocą ▁:
"Hello world" → ["▁Hello", "▁world"] "New York" → ["▁New", "▁York"]
Tiktoken (OpenAI)
Specjalizowana implementacja BPE używana przez OpenAI.
1import tiktoken 2 3# Loading the encoder 4enc = tiktoken.encoding_for_model("gpt-4") 5 6# Encode 7tokens = enc.encode("Hello world!") 8# [12345, 67890, 999] 9 10# Decode 11text = enc.decode(tokens) 12# "Hello world!" 13 14# Check token count 15print(len(tokens)) # 3
Model-Encoder Mappings
| Model | Encoder | Vocab Size |
|---|---|---|
| GPT-4 | cl100k_base | 100,277 |
| GPT-3.5 | cl100k_base | 100,277 |
| GPT-3 | p50k_base | 50,281 |
| Codex | p50k_edit | 50,281 |
Hugging Face Tokenizers
1from transformers import AutoTokenizer 2 3# Loading the tokenizer 4tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased") 5 6# Encode 7encoded = tokenizer("Hello, world!", return_tensors="pt") 8# { 9# 'input_ids': tensor([[101, 7592, 1010, 2088, 999, 102]]), 10# 'attention_mask': tensor([[1, 1, 1, 1, 1, 1]]) 11# } 12 13# Decode 14text = tokenizer.decode(encoded['input_ids'][0]) 15# "[CLS] hello, world! [SEP]" 16 17# Token List 18tokens = tokenizer.tokenize("Hello, world!") 19# ['hello', ',', 'world', '!']
Fast Tokenizers
1from tokenizers import Tokenizer, models, trainers, pre_tokenizers 2 3# Creating a new tokenizer 4tokenizer = Tokenizer(models.BPE()) 5tokenizer.pre_tokenizer = pre_tokenizers.Whitespace() 6 7trainer = trainers.BpeTrainer( 8 vocab_size=30000, 9 special_tokens=["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]"] 10) 11 12tokenizer.train(files=["corpus.txt"], trainer=trainer) 13tokenizer.save("my_tokenizer.json")
Special Tokens
Common Special Tokens
| Token | Description | Use Case |
|---|---|---|
| [CLS] | Początek sekwencji | Zadania klasyfikacyjne BERT |
| [SEP] | Separator segmentów | Oddzielanie par zdań |
| [PAD] | Padding | Wyrównywanie batchy |
| [UNK] | Nieznany token | Obsługa słów spoza słownika |
| [MASK] | Maskowanie | Masked Language Modeling (MLM) |
| <|endoftext|> | Koniec sekwencji | Zadania generacyjne GPT |
Chat Tokens
1<|system|>You are a helpful assistant<|end|> 2<|user|>Hello!<|end|> 3<|assistant|>Hello! How can I help you today?<|end|>
Tokenization Challenges in Turkish
Morphological Richness
1"gelebileceklermiş" (they were said to be able to come) → A single word but complex structure 2gel (come) + ebil (can) + ecek (will) + ler (they) + miş (reportedly) 3 4Tokenization: 5- Poor: ["gelebileceklermiş"] (Single token, very rare) 6- Good: ["gel", "ebil", "ecek", "ler", "miş"]
Solutions
- Turkish-optimized tokenizer training.
- Integration of morphological analysis.
- Suffix-aware BPE application.
Limity i zarządzanie tokenami
Okno kontekstu
| Model | Długość kontekstu (tokeny) | ~Odpowiednik słów |
|---|---|---|
| GPT-3.5 | 16K | ~12 000 |
| GPT-4 | 128K | ~96 000 |
| Claude 3 | 200K | ~150 000 |
Szacowanie liczby tokenów
1def estimate_tokens(text): 2 # Rough estimate: 1 token ≈ 4 characters (English) 3 # For Turkish: 1 token ≈ 3 characters 4 return len(text) // 3 5 6# More accurate calculation 7def count_tokens(text, model="gpt-4"): 8 enc = tiktoken.encoding_for_model(model) 9 return len(enc.encode(text))
Podsumowanie
Tokenizacja jest fundamentalnym elementem NLP i LLM. Metody subword, takie jak BPE, WordPiece i SentencePiece, odgrywają kluczową rolę w sukcesie nowoczesnych modeli językowych. Wybór i konfiguracja odpowiedniego tokenizera bezpośrednio wpływa na końcową wydajność modelu.
W Veni AI zapewniamy strategie tokenizacji specjalizowane pod rozwiązania NLP dla języka tureckiego.
