Перейти к содержанию

Токенизация в LLM

~7 минут чтения

Предварительно: Позиционное кодирование | Реализация внимания с нуля


Зачем токенизация

Токенизация -- мост между текстом и числами. LLM не видит буквы: она оперирует токенами -- подсловами, которые определяют что модель "понимает" и сколько стоит обработка.

Ключевой инсайт: выбор токенизатора определяет не только качество модели, но и её экономику. Byte-Level BPE (GPT-4, LLaMA 3) стал стандартом: нулевой OOV, универсальное покрытие языков и кода, предсказуемое поведение.

Почему токенизация критична

Аспект Влияние
Стоимость API Больше токенов = выше цена
Compute Длина последовательности -> \(O(n^2)\) attention
Мультиязычность Плохой tokenizer = 2-5x больше токенов
Код Неправильное разбиение -> баги
Возможности модели Токены определяют что модель "видит"

Алгоритмы токенизации

Byte-Pair Encoding (BPE)

Used by: GPT-2, GPT-3, GPT-4, Llama, Mistral

Algorithm

  1. Start with character-level vocabulary
  2. Count frequency of adjacent pairs
  3. Merge most frequent pair into new token
  4. Repeat until target vocab size
Iteration 1: "t h e" → "th" + "e" (merge "t"+"h")
Iteration 2: "th e" → "the" (merge "th"+"e")

Формула BPE

\[ \text{Score}(pair) = \text{Frequency}(pair) \]

На каждой итерации мерджится пара с максимальным score.

Unigram Language Model

Used by: T5, ALBERT, some multilingual models

Algorithm

  1. Start with large vocabulary
  2. Compute probability of each token
  3. Remove tokens with lowest impact
  4. Use Viterbi for optimal segmentation

Вероятность Unigram

\[ P(x) = \prod_{t \in \text{tokenize}(x)} p(t) \]

Где \(p(t)\) оценивается EM-алгоритмом.

WordPiece

Used by: BERT, ELECTRA

Аналогичен BPE, но использует scoring на основе правдоподобия:

\[ \text{Score}(pair) = \frac{P(pair)}{P(first) \times P(second)} \]

Byte-Level BPE

Used by: GPT-2+, modern LLMs

Ключевая идея: работать с UTF-8 байтами, а не символами

  • Нулевой OOV (out-of-vocabulary)
  • Универсальное покрытие (эмодзи, любой язык)
  • GPT-4 cl100k_base использует этот подход

Токенизация -- скрытый множитель стоимости API

Один и тот же текст на разных языках = разное количество tokens. Английский: ~4 символа/token. Русский: ~1.5-2 символа/token. Китайский: ~1 символ = 2-3 tokens. Это значит: русскоязычный prompt стоит в 2x дороже английского при том же объёме текста. При выборе модели для мультиязычных приложений проверяй tokenizer efficiency на целевом языке.


Сравнение: BPE vs Unigram vs Byte-Level

Aspect BPE Unigram Byte-Level
Vocab building Greedy merge Probabilistic prune Bytes → merge
Segmentation Deterministic Probabilistic Deterministic
OOV handling Fallback to chars Probabilistic Never (bytes)
Multilingual Medium Good Best
Speed Fast Slower Fast
Used by GPT, Llama T5, XLNet GPT-2+

Когда что использовать

Сценарий Рекомендация
Английский текст BPE или Unigram
Мультиязычный Byte-Level BPE или Unigram
Много кода Byte-Level BPE
Морфологически богатый язык Unigram
Production LLM Byte-Level BPE (стандарт)

Размер словаря: trade-offs

Влияние размера

Vocab Size Pros Cons
10K Smaller embeddings Longer sequences
32K Balanced
100K Shorter sequences Larger model
250K+ Very efficient Overfitting risk

Размеры словарей современных LLM

Модель Размер словаря Тип
GPT-2 50K Byte-BPE
GPT-4 100K Byte-BPE
Llama 2 32K BPE
Llama 3 128K BPE
Qwen 2 152K BPE
Mistral 32K BPE

Формула: память на эмбеддинги

\[ \text{Memory}_{emb} = V \times d \times \text{bytes\_per\_param} \]

Где \(V\) -- размер словаря, \(d\) -- размерность эмбеддинга

Example: Llama 3 (128K vocab, 4096 dim, FP16) $$ 128000 \times 4096 \times 2 = 1.05\text{GB} $$


Мультиязычная токенизация

Проблемы

Проблема Влияние
Дисбаланс скриптов Латиница: 1 токен, остальные: 3-5 токенов
Морфология Агглютинативные языки требуют больше токенов
Покрытие словаря Low-resource языки недопредставлены

Исследование Frontiers AI (2025)

Статья: "Tokenization efficiency of current foundational LLMs"

Выводы:

  • Мультиязычные BPE tokenizer'ы с пересекающимися словарями ухудшают качество
  • Особенно страдают token-level задачи (POS tagging)

Переосмысление дизайна мультиязычных токенизаторов (OpenReview 2025)

Ключевые выводы:

  1. Балансировать корпус между языками
  2. Использовать language-aware сэмплирование
  3. Byte-level fallback для редких скриптов

Лучшие практики для мультиязычности

# Corpus balancing
corpus_weights = {
    "en": 0.3,  # Reduce English dominance
    "zh": 0.15,
    "es": 0.1,
    "other": 0.45  # Distributed across rest
}

# Vocabulary augmentation
for lang in target_languages:
    add_common_tokens(lang, count=500)

Дизайн токенизатора

Специальные токены

Токен Назначение
<|begin_of_text|> Начало документа
<|end_of_text|> Конец документа
<|start_header_id|> Заголовок сообщения
<|eot_id|> Конец хода
[PAD] Паддинг
[CLS] Классификация
[SEP] Разделитель

Regex-паттерны (стиль GPT-4)

import regex

pattern = regex.compile(
    r"""'(?i:[sdmt]|ll|ve|re)|[^\r\n\p{L}\p{N}]?+\p{L}+|\p{N}{1,3}| ?[^\s\p{L}\p{N}]++[\r\n]*|\s*[\r\n]|\s++|\S"""
)

Назначение: предотвратить слияние через границы слов.

Пре-токенизация

# Normalization before tokenization
def pre_tokenize(text):
    text = normalize_unicode(text)  # NFC normalization
    text = text.replace("\t", "    ")
    return text

Теоретико-информационный взгляд (2026)

Статья: "An Information-Theoretic Perspective on LLM Tokenizers" (arXiv 2601.09039, Jan 2026)

Ключевые выводы

Домен Влияние токенизатора
Код Высокое -- структурированный синтаксис
Математика Среднее -- символы важны
Английский Низкое -- многие варианты работают
Мультиязычный Высокое -- различия скриптов

Эффективность сжатия

\[ \text{Compression Ratio} = \frac{\text{Characters}}{\text{Tokens}} \]

Целевое значение: 3-5 символов на токен (английский)


Токенизация кода

Проблемы

  • Имена переменных: getUserById vs get_user_by_id
  • Спецсимволы: { } [ ] ( ) < >
  • Значимость пробелов в Python

Лучшие практики

# Code-aware tokenization
code_patterns = [
    r"[A-Za-z_][A-Za-z0-9_]*",  # Identifiers
    r"[0-9]+",                   # Numbers
    r"[+\-*/=<>!&|]+",          # Operators
    r"[(){}\[\],.;:]",          # Punctuation
    r"\s+",                      # Whitespace
]

Расширение словаря

# Add common code tokens
code_tokens = [
    "def", "class", "import", "return",
    "if", "else", "for", "while",
    "True", "False", "None",
    "function", "const", "let", "var"
]

Vocab size 32K vs 128K: больше != лучше

Llama 2 (32K vocab) -> Llama 3 (128K vocab) улучшил multilingual, но embedding layer вырос с 0.26 GB до 1.05 GB. Для fine-tuning маленьких моделей (<1B) большой vocab -- значительная доля параметров. Для English-only задач 32K достаточно. 128K+ оправдан только для multilingual/code. Embedding memory: \(V \times d \times 2\) bytes (FP16).


Руководство по реализации

Обучение токенизатора (HuggingFace)

from tokenizers import Tokenizer, models, trainers, pre_tokenizers

# Initialize
tokenizer = Tokenizer(models.BPE())
tokenizer.pre_tokenizer = pre_tokenizers.ByteLevel()

# Train
trainer = trainers.BpeTrainer(
    vocab_size=32000,
    special_tokens=["<|endoftext|>", "<|pad|>"]
)
tokenizer.train(files=["corpus.txt"], trainer=trainer)

# Save
tokenizer.save("tokenizer.json")

Использование SentencePiece

import sentencepiece as spm

spm.SentencePieceTrainer.train(
    input="corpus.txt",
    model_prefix="tokenizer",
    vocab_size=32000,
    model_type="unigram",  # or "bpe"
    character_coverage=0.9995
)

Проверка эффективности токенизации

def check_efficiency(tokenizer, texts):
    total_chars = sum(len(t) for t in texts)
    total_tokens = sum(len(tokenizer.encode(t)) for t in texts)
    ratio = total_chars / total_tokens
    print(f"Chars/Tokens: {ratio:.2f}")
    # Target: 3-5 for English, 1-2 for CJK

Богатая морфология

Статья: "Rethinking Tokenization for Rich Morphology: The Dominance of Unigram" (arXiv 2508.08424)

Выводы

Тип языка Лучший алгоритм
Аналитический (английский) BPE
Агглютинативный (турецкий, финский) Unigram
Фузионный (русский, немецкий) Unigram или BPE

Почему Unigram для морфологии

  • Вероятностная сегментация уважает границы морфем
  • Лучше обработка редких словоформ
  • Более консистентная cross-morpheme токенизация

Interview Questions

1. Чем BPE отличается от Unigram?

❌ Red flag: "BPE мерджит пары, Unigram -- нет"

✅ Strong answer: "BPE строит словарь снизу вверх: начинает с символов, мерджит самую частую пару на каждом шаге (greedy). Unigram -- сверху вниз: начинает с большого словаря, убирает токены с минимальным impact на likelihood. BPE дает детерминированную сегментацию, Unigram -- вероятностную (можно сэмплировать разные разбиения). Для морфологически богатых языков (русский, турецкий) Unigram лучше, потому что уважает границы морфем."

2. Почему Byte-Level BPE стал стандартом?

❌ Red flag: "Потому что работает с любым текстом"

✅ Strong answer: "Byte-Level BPE оперирует UTF-8 байтами вместо символов. Три ключевых преимущества: (1) нулевой OOV -- любой текст, эмодзи, любой скрипт представляется через 256 базовых байтов, (2) не нужен огромный базовый алфавит для мультиязычности, (3) единый tokenizer для всех языков и модальностей (текст + код). Недостаток: один символ CJK = 3 байта = потенциально 3 токена. GPT-4 (cl100k_base), LLaMA 3 используют этот подход."

3. Vocab 32K vs 128K: когда что использовать?

❌ Red flag: "Больше всегда лучше"

✅ Strong answer: "128K+ оправдан для мультиязычных и code моделей: LLaMA 3 увеличил с 32K до 128K ради лучшего покрытия не-латинских скриптов. Но embedding layer вырос с 0.26 GB до 1.05 GB (FP16). Для English-only задач 32K достаточно. Для маленьких моделей (<1B) большой словарь = значительная доля параметров уходит в embeddings. Формула: \(V \times d \times 2\) байт."

4. Спроектируйте tokenizer для мультиязычного чат-бота

❌ Red flag: "Возьмем стандартный BPE"

✅ Strong answer: "1) Byte-Level BPE для нулевого OOV. 2) Балансировка корпуса: не 80% English, а пропорционально целевым языкам. 3) Vocab ~100K+ для покрытия всех скриптов. 4) Augmentation: добавить частые токены целевых языков (500-1000 на язык). 5) Метрика: characters-per-token ratio по языкам, цель -- минимизировать разброс между языками. 6) Тестирование: проверить что 'Привет, как дела?' не стоит в 3x больше токенов чем 'Hello, how are you?'"


Сводка формул

Формулы токенизации

BPE Merge Score: $\(\text{Score}(pair) = \text{Frequency}(pair)\)$

Unigram Probability: $\(P(x) = \prod_{t \in \text{tokenize}(x)} p(t)\)$

WordPiece Score: $\(\text{Score}(pair) = \frac{P(pair)}{P(first) \times P(second)}\)$

Compression Ratio: $\(\text{Ratio} = \frac{\text{Total Characters}}{\text{Total Tokens}}\)$

Embedding Memory: $\(\text{Memory} = V \times d \times \text{bytes\_per\_param}\)$


Самопроверка

  1. Возьмите строку "машинное обучение" и пройдите 3 итерации BPE вручную (начиная с символов). Какие пары будут мерджиться первыми?
  2. Рассчитайте memory embedding layer для модели с vocab=64K, dim=2048, FP16. Сравните с vocab=128K.
  3. Текст на русском: "Привет, мир!" занимает 8 токенов в GPT-4 (cl100k_base). Тот же текст на английском "Hello, world!" -- 4 токена. Объясните почему и предложите, как уменьшить разрыв.

Источники и литература

Статьи

  1. [Rich Morphology] "Rethinking Tokenization for Rich Morphology" (arXiv 2508.08424, 2025)
  2. [Info Theory] "An Information-Theoretic Perspective on LLM Tokenizers" (arXiv 2601.09039, 2026)
  3. [Efficiency] "Tokenization efficiency of foundational LLMs" (Frontiers AI, 2025)
  4. [Multilingual] "The Art of Breaking Words: Multilingual Tokenizer Design" (OpenReview, 2025)

Библиотеки

  • HuggingFace Tokenizers: https://github.com/huggingface/tokenizers
  • SentencePiece: https://github.com/google/sentencepiece
  • tiktoken: https://github.com/openai/tiktoken

Руководства

  • "Tokenizer Design: Choosing BPE, Unigram, and Vocabulary Size" (Oct 2025)
  • "How LLM Tokenization Actually Works Under the Hood" (LetsDataScience)

See Also

  • BPE Implementation -- пошаговая реализация BPE tokenizer с нуля на Python
  • Tokenization Comparison -- сводная таблица BPE vs Unigram vs WordPiece vs Byte-Level
  • LLM API Pricing -- tokenizer efficiency напрямую влияет на стоимость API
  • Efficient Transformers -- sequence length (определяемая tokenizer) влияет на \(O(n^2)\) attention
  • Open-Source LLMs -- vocab sizes и tokenizer choices разных моделей