Условие
Объясните BPE, WordPiece, SentencePiece. Почему subword tokenization лучше word-level?
Решение
Подход
Word-level проблемы: out-of-vocabulary (OOV) слова, huge vocabulary (миллионы), редкие формы слов (рус. падежи), невозможность handle опечатки.
Subword: разделяем слова на части, на которых учим vocabulary. «токенизация» → «токен» + «иза» + «ция».
BPE (Byte Pair Encoding)
Алгоритм:
- Initial vocab = все символы.
- Подсчитаем частоту bigram пар в корпусе.
- Самую частую merge в новый token.
- Повтор до vocab_size.
def learn_bpe(corpus, n_merges=10000):
vocab = list(corpus_to_chars(corpus)) # initial
pairs = count_pairs(corpus)
for _ in range(n_merges):
best = max(pairs, key=pairs.get)
vocab.append(''.join(best))
corpus = merge_pair(corpus, best)
pairs = count_pairs(corpus)
return vocabИспользуется в GPT-2, GPT-3, RoBERTa.
WordPiece
Похож на BPE, но выбирает merge максимизирующий likelihood корпуса:
score(pair) = freq(pair) / (freq(left) · freq(right))
Используется в BERT.
SentencePiece
Не предполагает pre-tokenization (нет split по пробелу). Работает на raw bytes, что важно для языков без пробелов (CJK).
Поддерживает 2 алгоритма:
- BPE: как выше.
- Unigram: вероятностная модель, выбирает best segmentation через Viterbi.
Используется в T5, mBART, ALBERT.
Byte-level BPE
GPT-2 / GPT-3: работает на bytes (256 символов), а не Unicode. Никогда не OOV.
Сравнение
| BPE | WordPiece | SentencePiece (Unigram) | |
|---|---|---|---|
| Pre-tokenization | По пробелу | По пробелу | Нет |
| Merge critirea | Freq | Likelihood | Likelihood global |
| Decode | Concat | Concat (## prefix) | Concat |
| Reversible | Не всегда | Не всегда | Lossless |
Реализация (huggingface tokenizers)
from tokenizers import Tokenizer, models, trainers, pre_tokenizers
tokenizer = Tokenizer(models.BPE())
tokenizer.pre_tokenizer = pre_tokenizers.Whitespace()
trainer = trainers.BpeTrainer(vocab_size=30000, special_tokens=['<pad>','<unk>','<s>','</s>'])
tokenizer.train(files=['corpus.txt'], trainer=trainer)Эффект на multilingual
Англоязычный BERT-Tokenizer на русском split на много мелких частей → длинные последовательности → плохое модели и большой compute. Для русского лучше rubert-tokenizer или multilingual model (XLM-R) с большим vocab.
Подводные камни
- Vocab size: малый → длинные последовательности, медленно. Большой → больше параметров embedding, overfit на rare tokens. Стандарт 30k-50k.
- Pre-tokenization влияет: BPE на пробелах не справляется с китайским; нужен SentencePiece.
- Numbers: tokenizer часто разбивает «12345» на «12» «345» → плохо для math reasoning. Решение — special number tokens или digit-level split.
- Token economics: payment по tokens. Языки с плохим vocab (русский) платят больше — счёт может быть в 1.5-2× выше английского.
- Truncation max_len: BERT 512, GPT-3 4k, GPT-4o 128k. Длинные документы — chunking.
Эталонный ответ
Subword: BPE (greedy merge по частоте, GPT), WordPiece (merge по likelihood, BERT), SentencePiece + Unigram (без pre-tokenization, T5/mBART). Byte-level BPE никогда не OOV. Vocab 30-50k стандарт. Для русского — выбрать tokenizer обученный на русском, иначе плохой compression.