Мощный блог

Токенизация в больших языковых моделях: как текст становится математикой

11 августа 2026 · LLM
Токенизация в больших языковых моделях: как текст становится математикой

Токенизация — это процесс преобразования текста в последовательность числовых идентификаторов токенов.

Это логично, так как LLM можно упрощённо рассматривать как математическую функцию, а функция — это отображение пространства аргументов в пространство значений. Однако в случае LLM на вход подаются не произвольные числа, а дискретные идентификаторы токенов, которые затем преобразуются в векторы.

Наивный подход: пословная токенизация

Возникает задача: как из текста получить числа? Самое простое и очевидное решение — разбить корпус текста на слова и каждому слову присвоить номер в словаре.

Например:

«кот лежит на диване и там же лежит пес» -> 0 1 2 3 4 5 6 1 7

где повторное слово «лежит» получает тот же идентификатор.

Проблемы такого подхода

  • Огромный размер словаря. Если кодировать каждое слово, словарь может разрастись до очень больших размеров.
  • Морфология. Слова с одинаковым корнем могут получать разные идентификаторы, хотя несут близкий смысл.
  • Мультиязычность. Словарь оказывается привязан к конкретному языку, и для нового языка его нужно увеличивать.
  • Неизвестные слова. Если в новых данных встретится слово, отсутствующее в словаре, его придётся заменять заранее заведённым токеном [UNK], теряя при этом информацию.

Символьная токенизация

При символьной токенизации текст разбивается на отдельные символы.

Преимущества:

  • словарь мал: алфавит + пунктуация;
  • нет проблемы [UNK].

Недостатки:

  • последовательности становятся в 4–10 раз длиннее;
  • увеличиваются вычислительные затраты;
  • затрудняется выявление морфологических и смысловых закономерностей.

Подсловная токенизация

Идея субсловной токенизации заключается в том, чтобы разбивать слова на значимые части — корни, приставки, суффиксы — или на часто встречающиеся последовательности символов, а не хранить в словаре каждое уникальное слово целиком.

Важно: вышеизложенные подходы не снимают полностью концептуальные проблемы токенизации. Но можно пойти другим путём — работать не с символами, а с байтами, которыми кодируются эти символы.

Кодирование пар байтов (Byte Pair Encoding, или BPE)

Рассмотрим подробно способ токенизации по парам байтов.
Работу алгоритма BPE проще понять на конкретном примере:
пусть на вход подаётся строка - "abababab".

Замечание 1: "a" и "b" — символы ASCII; в кодировке UTF-8 они занимают 1 байт.
Например, если были бы кириллические символы "а" и "б", то каждый из них занимал бы по 2 байта.

Замечание 2: строка "abababab" берётся для показательного примера. Конечно, корпус текста — это набор строк, разделённых пробелами или специальными символами.

Алгоритм BPE

  1. Происходит преобразование символов в байты - для простоты возьмём десятичный вид:
"a" -> 97
"b" -> 98

Создаём словарь для базовых байтовых токенов:

tokenizator = {
    "vocab": {
        # базовые байтовые токены 0..255
        "a": 97,
        "b": 98,
        # ...
    },
    "merges": []  # список правил слияния пуст при инициализации
}
  1. Производится замена символов на значения базовых байтовых токенов:
"abababab" = 97 98 97 98 97 98 97 98
  1. Последовательно просматриваем пары байтов окном i + 1:
97 98, 98 97, 97 98, 98 97, 97 98, 98 97, 97 98
  1. Считаем частоту пар байтов:
(97, 98) = 4
(98, 97) = 3
  1. 97 объединяем с 98 (конкатенируем "a" c "b") и заменяем на 256 (первый свободный ID, так как базовые байтовые токены уже занимают 0...255).

  2. Сохраняем в словаре {"ab": 256}; формируется первое правило слияния:

tokenizator = {
    "vocab": {
        # базовые байтовые токены 0..255
        "a": 97,
        "b": 98,
        # ...
        "ab": 256,
    },
    "merges": [
        ["a", "b"]  # -> a + b = ab, 1-е правило слияния
    ]
}
  1. Проводим замену:
"abababab" = 256 256 256 256
  1. Последовательно просматриваем пары токенов окном i + 1:
256 256, 256 256, 256 256
  1. Считаем частоту пар токенов:
(256, 256) = 3
  1. 256 объединяем с 256 (конкатенируем "ab" c "ab") и заменяем на 257.

  2. Сохраняем в словаре {"abab": 257}; формируется второе правило слияния:

tokenizator = {
    "vocab": {
        # базовые байтовые токены 0..255
        "a": 97,
        "b": 98,
        # ...
        "ab": 256,
        "abab": 257
    },
    "merges": [
        ["a", "b"],   # -> a + b = ab, 1-е правило слияния
        ["ab", "ab"]  # -> ab + ab = abab, 2-е правило слияния
    ]
}
  1. Проводим замену:
"abababab" = 257 257
  1. Алгоритм завершается, когда не осталось пар с частотой больше 1 или когда достигнут заранее заданный размер словаря / число слияний.
tokenizator = {
    "vocab": {
        # базовые байтовые токены 0..255
        "a": 97,
        "b": 98,
        # ...
        "ab": 256,
        "abab": 257
    },
    "merges": [
        ["a", "b"],   # -> a + b = ab, 1-е правило слияния
        ["ab", "ab"]  # -> ab + ab = abab, 2-е правило слияния
    ]
}

Примеры реализации токенизаторов - Tiktoken (от OpenAI) https://github.com/openai/tiktoken?spm=a2ty_o01.29997173.0.0.40a755fb2zhmZX.
Это самый популярный среди токенизаторов поддерживает большинство моделей от OpenAI.

Важно для простоты реализации объединил vocab и правила в один словарь - на самом деле обычно эти две сущность разделяются на отдельные файлы.

Токенизация строки "ab" после работы алгоритма: 256; применяется 1-е правило слияния.
Токенизация строки "b" после работы алгоритма: 98.
Токенизация строки "abababab" после работы алгоритма: 257 257; сначала применяется 1-е правило слияния, затем 2-е.

Важно! Обратите внимание: базовые байтовые токены тоже сохраняются в словаре токенизатора, чтобы кодирование всегда можно было начать с базовых байтов, а затем применить выученные правила слияния. То есть в случае "ab" сначала строка представляется как базовые байтовые токены 97 98; затем, если выучено правило слияния ("a", "b") -> "ab", пара 97 98 заменяется на 256. Поэтому алгоритм BPE часто называют обучаемым алгоритмом, основанным на правилах слияния пар токенов.

Обобщённый алгоритм BPE в псевдокоде может выглядеть в упрощённом виде так (это процесс обучения токенизатора):

# Представляем весь корпус как список последовательностей базовых токенов
tokens = разбить_корпус_на_базовые_символы_или_байты()

for step in range(нужное_число_слияний):
    # Считаем частоты всех соседних пар во всём корпусе
    pairs = посчитать_частоты_всех_соседних_пар(tokens)

    if pairs пустые:
        break

    best_pair = найти_самую_частую_пару(pairs)
    new_token = best_pair[0] + best_pair[1]

    # Заменяем все вхождения этой пары на новый токен во всём корпусе
    tokens = заменить_все_вхождения(best_pair, new_token, tokens)

    # Сохраняем правило слияния в словарь (merges)
    сохранить_правило_слияния(best_pair  new_token)

Важно: В реальном BPE частоты пар считаются не по одной строке, а агрегируются по всему большому обучающему корпусу текстов. На каждом шаге алгоритм ищет самую частую пару глобально по всему корпусу.

Примечание: Попробовать и визуально сравнить работу токенизаторов для популярных LLM (GPT-4, LLaMA, Claude и др.) можно на интерактивной площадке Tiktokenizer.


Как BPE решает проблемы вышеизложенных алгоритмов токенизации

1. Огромный размер словаря

BPE разбивает редкие и длинные слова на часто встречающиеся куски (подслова). Вместо того чтобы хранить в словаре миллионы целых слов, словарь хранит компактный набор базовых частей (обычно от 32 000 до 100 000 токенов), из которых можно собрать любое слово. Это экономит память, уменьшает размер матрицы эмбеддингов и ускоряет обучение.

2. Морфология

Алгоритм автоматически находит частые корни, приставки и суффиксы (например, un-, -ing, -er, -ость, -ние) и делает их отдельными токенами. Благодаря этому модель лучше понимает связь между однокоренными словами (например, run, running, runner), так как они состоят из одних и тех же базовых токенов и имеют пересекающиеся векторные представления.

3. Мультиязычность

На уровне байтов (Byte-level BPE) алгоритму не нужно заранее знать алфавиты всех языков мира. Он просто ищет частые последовательности байтов в текстах. Это позволяет одной модели одинаково эффективно токенизировать английский, русский, китайский, эмодзи или любой другой язык, не раздувая базовый словарь.

4. Неизвестные слова (OOV — Out Of Vocabulary)

Проблема токена «неизвестное слово» (<unk>) полностью исчезает. Любое новое слово, неологизм, редкая фамилия или опечатка просто разбиваются на известные подслова или, в крайнем случае, на базовые байты. Токенизатор гарантированно закодирует любой текст, а модель сможет извлечь из него смысл на основе знакомых частей.


BPE и его главный конкурент — Unigram

На сегодняшний день BPE — это де-факто промышленный стандарт, который используется во множестве LLM (GPT, LLaMA, Mistral и др.). Однако у него есть серьёзный конкурент — алгоритм Unigram (Unigram Language Model), который используется, например, в токенизаторах T5 и некоторых моделях от Google.

Главное концептуальное отличие заключается в направлении работы:

  • BPE работает «снизу вверх»: начинает с базового алфавита (или байтов), ищет самые частые пары и итеративно склеивает их, пока не достигнет нужного размера словаря.
  • Unigram работает «сверху вниз»: начинает с огромного словаря (включая все возможные слова и N-граммы из корпуса) и итеративно отбрасывает токены. Удаляются те токены, которые встречаются настолько редко, что их удаление почти не влияет на общую вероятность (статистику) корпуса. В итоге остаётся оптимальный с точки зрения теории информации набор токенов.

📚 Где почитать: Отличный и подробный материал про алгоритм Unigram и его отличия от BPE есть в бесплатном курсе от Hugging Face:
Hugging Face LLM Course: Unigram Tokenization (на русском)

После понимания работы BPE можно прийти к важным и интересным выводам:

  • Токен ≠ слово. Токен — это не лингвистическая единица, а частотная последовательность байтов. Он может совпадать со словом, но не обязан. Частые слова («the», «и», «не») действительно становятся одним токеном, но это побочный эффект статистики, а не цель алгоритма. Редкие слова разбиваются на фрагменты, которые могут не иметь никакого отношения к морфемам.
    Замечание 3: Это кстати одна из причин почему LLM не умеет считать слова или количество символов. Для нее это слово это два три смысловых токена, не говоря о символах.

  • Несемантичность. BPE не знает, что такое «смысл», «корень» или «суффикс». Он оперирует исключительно частотой пар байтов. Однако если определённая последовательность символов (например, корень -бег-) часто встречается в корпусе, её байты будут склеены в один токен. Алгоритм не стремится к морфемам — он просто находит частотные закономерности, которые иногда совпадают с морфологической структурой.

  • Регистрозависимость. TOY и toy — разные последовательности байтов, а значит, разные токены с разными ID и разными начальными эмбеддингами. На этапе токенизации они никак не связаны. Однако в процессе обучения модель видит оба варианта в разных контекстах и через механизм внимания учится находить между ними семантическую близость. Для токенизатора они различны; для обученной модели — связаны через контекст.

  • Многобайтовые кодировки. Кириллические символы в UTF-8 кодируются двумя байтами. Например, «п» — это байты D0 BF, а «р» — байты D1 80. Это два разных байта, которые вместе образуют один символ. На начальных этапах BPE склеивает именно эти два байта между собой, чтобы восстановить символ как единый токен. Таким образом, прежде чем начать работать с привычными нам буквами, алгоритму нужно «собрать» каждый многобайтовый символ из его составляющих.

  • Неочевидный символизм пробела. « лес» (с пробелом) и «лес» (без пробела) — разные последовательности байтов, а значит, потенциально разные токены. Пробел в BPE — не «пустота», а полноправный байт, который кодирует границу слова и позицию в тексте. Если в обучающем корпусе « лес» и «лес» встречались в разных контекстах, токенизатор зафиксирует эту разницу, и модель может реагировать на них по-разному. Это неочевидное, но важное следствие байтовой токенизации.

  • Жадность алгоритма. BPE на каждом шаге выбирает самую частую пару — это жадная стратегия. Она не гарантирует глобально оптимальную токенизацию, потому что выбор одной пары блокирует возможность других слияний. Именно поэтому существуют альтернативы вроде Unigram, который работает через вероятностную модель и может находить более эффективные разбиения.

5. Особенности токенизации кириллицы в BPE

  • Многобайтовое кодирование. Кириллические символы в UTF-8 занимают 2 байта, а латинские — 1. Byte-level BPE должен сначала склеить пару байтов в одну букву, и только потом искать более крупные слияния из-за чего русские слова могут чаще оставаться разбитыми на части (например, калачк | ала | ч).

  • Дисбаланс обучающего корпуса. Мультиязычные LLM обучаются в основном на английском. Русского текста в корпусе значительно меньше, поэтому BPE не выучивает столько же частотных слияний, сколько для английского. У русскоязычных моделей (например, YandexGPT) токенизация эффективнее именно благодаря большей доле русского текста при обучении.

Русский текст обычно занимает в 2–3 раза больше токенов, чем аналогичный по смыслу английский. Это не «природа языка», а результат инженерных решений: размера словаря, доли русского в корпусе и многобайтового UTF-8. Для примерной наглядной оценки объемов токенов.

Токенов Объем текста (примерно)
10 5–7 слов (короткая фраза, например: «Привет, как дела?»)
100 1 абзац (5–7 предложений)
1 000 1,5–2 страницы A4
10 000 15–20 страниц A4 (небольшая статья или глава книги)
100 000 150–200 страниц A4 (небольшая книга)
1 000 000 Крупная книга или несколько томов

6. Служебные токены для чего нужны

Служебные токены — это метаинформация для модели. Они не несут семантического содержания сами по себе, но определяют:
где начинается и заканчивается текст;
кто говорит (роли в диалоге);
какую задачу решать (классификация, генерация, заполнение пропусков);
как обрабатывать технические аспекты (padding, unknown words).
И как нетрудно догадаться без них современная LLM не смогла бы работать как диалоговая система.

Главное проблема в том, что нет единого стандарта какие токены как обозначать, поэтому разные провайдеры маркируют такие токены
по-разному. Для понимания достаточно рассмотреть типовой набор наиболее распространенных служебных токенов для моделей семейства gpt

Токен Название Назначение
<\|endoftext\|> End Of Text Конец последовательности
<\|im_start\|> Message Start Начало сообщения
<\|im_end\|> Message End Конец сообщения
system System Role Системный промпт
user User Role Вопрос пользователя
assistant Assistant Role Ответ модели
tool Tool Role Вызов инструмента / function calling
<\|fim_middle\|> Fill-in-Middle Middle Место вставки кода

Выводы

  1. Статистика, а не смысл: BPE работает «снизу вверх», жадно склеивая самые частые пары байтов. Алгоритм не понимает лингвистику или морфологию, он опирается исключительно на частоту.
  2. Дисбаланс языков: Русский текст занимает в 2–3 раза больше токенов, чем английский. Это следствие двухбайтовой кодировки кириллицы в UTF-8 и исторического перевеса англоязычных данных в обучающих корпусах.
  3. Структура диалога: Служебные токены (маркеры ролей и границ) критически важны для превращения потока текста в осмысленный чат, но в индустрии отсутствует единый стандарт их наименований.
0,0
0 оценок
5★
0
4★
0
3★
0
2★
0
1★
0

Оставить отзыв

Нажмите на звезду для оценки от 1 до 5
Необязательно. Используется только для связи
0/2000

Комментарии

Все С ответами Проверенные Только 4-5★