Зачем нужны токены
Как текст превращается в числа, почему современные LLM режут слова на кусочки и почему один и тот же смысл на русском стоит больше токенов, чем на английском.
Зачем это нужно
Заголовок раздела «Зачем это нужно»Нейросеть умеет только умножать матрицы, поэтому фразу «Привет, мир!» она не видит. Она видит список чисел
[23881, 131903, 11, 37934, 0] — так эту фразу видит GPT-4o. Переводом текста в числа и обратно занимается
токенизатортокенизаторEN tokenizer, а кусочки текста, которым присвоены номера, называются токенамитокенEN tokenВ разговорной речи и статьях китайцы чаще всего пишут просто «token»..
В модуле 4.1 токеном был один символ: биграмма, MLP и LSTM (урок 4.1.5) читали текст по буквам. Для настоящих LLM это слишком мелко — сейчас увидим почему.
Токенизатор кажется скучной технической деталью, но через него проходит почти всё, что вы будете измерять в LLM:
- длина контекста: «128K» в описании модели — это 128 тысяч токенов, а не символов (контекстное окноконтекстное окноEN context window);
- цена: API считает деньги за токены, а не за слова;
- скорость: модель генерирует ответ по одному токену, и «токены в секунду» превращаются в разное число букв в секунду для разных языков;
- качество: модель, которая видит слово целиком, и модель, которая видит его по кусочкам, учатся по-разному.
Знаменитый вопрос «сколько букв r в слове strawberry» трудный в том числе потому, что модель видит не буквы, а токены.
В вопросе
How many r's in strawberry?словоstrawberry(с пробелом впереди) у GPT-4o — один токен, внутрь которого модель заглянуть не может. Без пробела то же слово режется иначе — на три токенаst+raw+berry.
flowchart LR A["«Привет, мир!»"] --> B["Токенизатор"] B --> C["id: 23881, 131903, 11, 37934, 0"] C --> D["LLM"] D --> E["id следующего токена"] E --> F["Детокенизатор"] F --> G["текст ответа"]
В модуле 4.1 биграммная модель работала с символами: каждая буква имени — отдельный токен. Для игрушечной модели это нормально, а для настоящей LLM — нет. Разберёмся почему.
Три способа нарезать текст
Заголовок раздела «Три способа нарезать текст»Возьмём фразу «Нейросети предсказывают следующий токен» (39 символов) и порежем её тремя способами.
| Уровень | Токены | Длина | Размер словаря |
|---|---|---|---|
| Символы | Н е й р о с … | 39 | сотня-другая для одного алфавита |
| Слова | Нейросети предсказывают следующий токен | 4 | сотни тысяч и больше |
| Подслова (DeepSeek-V3) | Н ей рос ети ␣пред ска зыва ют ␣следующи й ␣ток ен | 12 | 128 000 |
Здесь и дальше ␣ обозначает пробел: у современных токенизаторов он приклеен к началу следующего слова.
Символы
Заголовок раздела «Символы»Плюсы: словарь крошечный, почти любое слово можно записать. Минусы серьёзнее.
- Длинные последовательности. Внимание в трансформере стоит по длине (модуль 4.4): запись «O большое» (сложность алгоритмасложность алгоритма (O-нотация)EN time complexity, big-O notationКак растёт время работы с размером данных n. O(n) — пропорционально n (поиск в list), O(1) — не зависит от n (поиск в dict и set)., ступень 0) значит, что работа растёт как квадрат длины . Текст в 4 раза длиннее — в 16 раз больше работы на внимание и в 4 раза больше шагов генерации.
- Каждый токен почти ничего не значит. Буква «к» сама по себе пустая. Модели приходится тратить слои на то, чтобы собрать из букв слова, и только потом думать о смысле.
- Для китайского «символ» — это уже тысячи вариантов. По официальному «Списку часто употребляемых иероглифов современного китайского языка» 3500 знаков покрывают почти весь современный текст, но в именах, топонимах и старых текстах встречаются и редкие. Встретился знак, которого не было при обучении, — модель его не видит.
Плюсы: каждый токен осмыслен, последовательности короткие. Но:
- Словарь взрывается от морфологии. У одного русского существительного до десятка форм: стол, стола, столу, столом, столе, столы, столов, столам, столами, столах. У глагола — ещё больше: читаю, читаешь, читает, читали, читающий, прочитанный… Каждая форма — отдельная запись в словаре.
- Неизвестные слова. Опечатка «превет», новое «нейросетка», редкая фамилия — всё, чего не было в обучающем корпусе,
превращается в служебный токен
<unk>(слово вне словаряслово вне словаря (OOV)EN out-of-vocabulary word (OOV)Слово, которого нет в словаре; словарный токенизатор заменяет его на <unk>. Byte-level BPE этой проблемы не имеет.). Модель буквально не видит, что там было написано. - В китайском нет пробелов. Фраза «большая языковая модель предсказывает следующий токен» записывается по-китайски сплошной строкой из 12 иероглифов. Чтобы разрезать её на слова, нужен отдельный алгоритм сегментации словсегментация слов (в китайском тексте)EN word segmentationВ китайском письме нет пробелов, поэтому границы слов приходится находить алгоритмом. Поэтому по-китайски токенизатор часто так и называют — «сегментатор слов»., и он сам ошибается.
Подслова — компромисс
Заголовок раздела «Подслова — компромисс»Идея: частые слова хранить целиком, редкие собирать из кусочков. Это работает благодаря закону Ципфазакон ЦипфаEN Zipf's lawЧастота слова примерно обратно пропорциональна его месту в частотном списке, поэтому немногие частые слова покрывают большую часть любого текста. (вы встречали его в уроке 0.2.6): если отсортировать слова по частоте, второе по счёту встречается примерно вдвое реже первого, десятое — в десять раз реже, и так далее. Поэтому небольшое число слов покрывает большую часть любого текста. Несколько тысяч частых слов получают собственные токены, а длинный хвост редких слов собирается из подсловподсловоEN subwordКусок слова, который токенизатор считает отдельным токеном, например «ток» + «ени» + «зация». Компромисс между символами и целыми словами.: корней, суффиксов, окончаний.
Примеры из настоящих токенизаторов (проверено запуском):
tokenization→token+ization— одинаково у GPT-4o, Llama 3, Qwen3 и DeepSeek-V3;токенизация→ток+ени+зация(DeepSeek-V3) илит+ок+ен+изация(GPT-4o);- опечатка
превет→пре+вет(DeepSeek-V3): слово незнакомое, но записать его можно; интеллект(с пробелом впереди) → один токен у GPT-4o, три (␣ин+тел+лект) у Qwen3 и четыре у DeepSeek-V3.
Неизвестных слов больше нет: в худшем случае слово распадается на буквы — или даже на байты, об этом ниже. Как именно выбрать «хорошие» кусочки, решает алгоритм BPE — ему посвящён следующий урок.
Компромисс: размер словаря и длина последовательности
Заголовок раздела «Компромисс: размер словаря и длина последовательности»Чем больше словарьсловарь (токенизатора)EN vocabulary (vocab)Множество всех токенов с их номерами (id). Размер словаря V задаёт размер матрицы эмбеддингов и выходного слоя (V × d). , тем короче становится текст в токенах: больше смысла влезает в контекст, меньше шагов генерации. Но словарь не бесплатный.
1. Матрица эмбеддингов. Каждый токен — строка в таблице эмбеддинговэмбеддинг, векторное представлениеEN embedding размера , где — ширина модели (подробно в модуле 4.3). Выходной слой, который превращает скрытое состояние в логиты по всему словарю, имеет такой же размер — если веса не связаны (связывание весовсвязывание весов (общие эмбеддинги входа и выхода)EN weight tying (tied embeddings)Одна и та же матрица служит эмбеддингом токенов и LM head. Её градиент — сумма вкладов от обоих мест. В Hugging Face — флаг tie_word_embeddings., модуль 4.6), это ещё параметров.
| Модель | (в конфиге) | Доля от всей модели | ||
|---|---|---|---|---|
| MiniMind-3 | 6 400 | 768 | 4.9 млн | ≈ 8% от 64 млн |
| GPT-2 small | 50 257 | 768 | 38.6 млн | ≈ 31% от 124 млн |
| Qwen3-0.6B | 151 936 | 1024 | 155.6 млн | ≈ 26% от 0.6 млрд |
| DeepSeek-V3 | 129 280 | 7168 | 926.7 млн | ≈ 0.14% от 671 млрд |
Для гиганта вроде DeepSeek-V3 словарь почти ничего не стоит. Для маленькой модели — наоборот. Если бы MiniMind взяла словарь Qwen ( млн), одни эмбеддинги весили бы почти вдвое больше, чем вся нынешняя модель (64 млн). Поэтому авторы MiniMind сознательно держат словарь в 6400 токенов.
2. Softmax по словарю. На каждом шаге модель умножает скрытый вектор длины на выходную матрицу и получает логитов, а потом считает по ним softmax. Это умножений на каждый токен: у Qwen3-0.6B млн — около четверти всех умножений, которые модель делает на один токен. (Заодно растёт и начальный loss свежей модели — из урока 1.3.2 вы помните, что он ≈ : угадать один из вариантов тем труднее, чем их больше.)
3. Редкие токены плохо обучены. Если токен встретился в корпусе сотню раз, его эмбеддинг почти не обучится. Такие «недоученные» токены — источник странного поведения моделей (о «глючных токенах» — в уроке 3).
Маленький словарь бьёт по другой стороне: последовательности длиннее, внимание дороже, KV-кэш больше (модуль 4.6), генерация медленнее. Отсюда практическое правило: современные модели на миллиарды параметров обычно берут словари на 100–200 тысяч токенов, крошечные учебные модели — несколько тысяч.
Байты и Unicode
Заголовок раздела «Байты и Unicode»Компьютер хранит текст как байты. Стандарт ЮникодЮникодEN UnicodeЕдиная таблица символов всех письменностей; каждому символу соответствует номер (code point), например «я» = U+044F, «λ» = U+03BB. даёт каждому символу номер, а кодировка UTF-8 (кодировкакодировка (символов)EN character encodingСпособ записать символы байтами. UTF-8: латиница — 1 байт, кириллица — 2, китайские иероглифы — 3.) записывает этот номер от 1 до 4 байтами:
| Символ | Байтов UTF-8 | Пример |
|---|---|---|
| Латиница, цифры | 1 | t → 116 |
| Кириллица | 2 | т → 209, 130 |
| Знак евро, китайские иероглифы | 3 | € → 226, 130, 172 |
| Эмодзи | 4 | 🙂 → 240, 159, 153, 130 |
Отсюда сильная идея: начать словарь с 256 байтов. Любой текст на любом языке — это последовательность байтов, значит,
любой текст кодируется без <unk>. Поверх байтов алгоритм выучивает «склейки» — частые пары байтов, буквы, слоги, слова.
Так устроены токенизаторы GPT-2, GPT-4o, Llama 3, Qwen и DeepSeek (это байтовый BPEбайтовый BPE (byte-level BPE)EN byte-level BPE (BBPE)BPE, у которого базовый словарь — 256 байтов UTF-8. Любой текст кодируется без <unk>. Используется в GPT-2, GPT-4o, Llama 3, Qwen, DeepSeek., урок 2).
У этой идеи есть обратная сторона. Если язык плохо представлен в словаре, его слова распадаются до байтов.
GPT-2 учили почти только на английском, и слово «Привет» он режет на 7 токенов: буква П разваливается на два отдельных байта,
остальные буквы идут по одной. С китайским не лучше: перевод фразы «Привет, мир!» — всего шесть символов вместе
с пунктуацией — у GPT-2 превращается в 14 токенов.
Почему русский текст «дороже»
Заголовок раздела «Почему русский текст «дороже»»Проверим на параллельном тексте: один и тот же абзац о токенизаторах на английском (277 символов) и русском (278 символов). Числа — количество токенов, посчитанное настоящими токенизаторами.
| Токенизатор | EN | RU | RU / EN |
|---|---|---|---|
| GPT-2 (2019) | 53 | 303 | 5.7× |
| GPT-4o (o200k_base) | 52 | 69 | 1.33× |
| Qwen3 | 52 | 85 | 1.63× |
| DeepSeek-V3 | 53 | 74 | 1.40× |
Английский у всех почти одинаков: около 5.3 символа на токен. С русским разница огромная. Причин три.
- Корпус, на котором учили токенизатор. Словарь выучивается на данных: самые частые сочетания получают свои токены. Если русского в корпусе 1–2%, русские слова проигрывают конкуренцию за места в словаре. В словаре GPT-2 всего 17 токенов, состоящих только из кириллических букв (с пробелом впереди или без), — отсюда 303 токена на 278 символов, то есть больше одного токена на букву. В словаре GPT-4o таких токенов уже около 14 тысяч, и тот же абзац стоит 69 токенов.
- Морфология. Английскому глаголу хватает форм read, reads, reading. Русскому нужны читаю, читает, читают, читала, читающий… Словарю приходится тратить места на окончания.
- Байты. Кириллическая буква — 2 байта. Когда слово распадается до байтов, русский платит вдвое больше английского.
Что это значит на практике:
- при одинаковой цене за токен русский запрос к API стоит в 1.3–1.6 раза дороже английского (у современных моделей);
- в контекст на 128K токенов влезает меньше русских страниц, чем английских;
- русский ответ генерируется медленнее в «буквах в секунду», хотя в токенах в секунду скорость та же.
С китайским картина другая. Мы прогнали через те же токенизаторы китайский перевод абзаца. Он занимает всего 76 символов — в 3.6 раза меньше английского: иероглиф несёт больше смысла, чем буква. Токенизаторы Qwen и DeepSeek, обученные на большом китайском корпусе, кодируют его в 47–53 токена — не хуже английского. GPT-4o тратит 64 токена, а GPT-2 — 156, в среднем по два токена на иероглиф.
Особые токены
Заголовок раздела «Особые токены»Кроме кусочков текста, в словаре есть специальные токеныспециальный токен (спецтокен)EN special tokenУправляющий токен, а не кусок текста — начало/конец текста (BOS/EOS), заполнитель для батча (PAD), разметка ролей в чате (например, <|im_start|> у Qwen).. Это не текст, а управляющие сигналы.
BPE никогда не собирает их из обычного текста: токенизатор вставляет их отдельно и не даёт разбить на части.
(Обратная проблема — когда строка вида <|im_end|> из пользовательского текста превращается в настоящий спецтокен, —
разобрана в уроке 3.)
- BOS (begin of sequence) — начало текста:
<|begin_of_text|>у Llama 3,<|begin▁of▁sentence|>у DeepSeek-V3 (в настоящем маркере DeepSeek вместо|стоит полноширинная вертикальная черта, подробнее в уроке 3). - EOS (end of sequence) — конец текста:
<|endoftext|>у GPT-2 и Qwen. В претрейне он разделяет документы, склеенные в один длинный поток. Модель учится его предсказывать, и при генерации EOS означает «я закончила». У чат-моделей конец ответа часто обозначает отдельный токен конца реплики, например<|im_end|>у Qwen (урок 3). - PAD — заполнитель: дополняет короткие тексты в батче до общей длины, loss на нём не считается.
- UNK — «неизвестное». Байтовым токенизаторам он не нужен.
- Разметка чата и инструментов:
<|im_start|>и<|im_end|>отмечают реплики у Qwen,<think>и</think>— рассуждения Qwen3,<tool_call>— вызов инструмента.
У Qwen3 151 643 обычных токена и 26 служебных, всего 151 669. Как спецтокены собираются в chat-шаблоны и почему их нельзя пропускать из пользовательского текста как есть — в уроке 3.
Практика
Заголовок раздела «Практика»Запустите код локально (нужен только Python). Он строит словарь из слов и из символов на крошечном корпусе и показывает, где каждый из них ломается.
corpus = "кот сидит на столе . кошка сидит на стуле . кот спит на столе ."
# 1. Словарь из словwords = sorted(set(corpus.split())) # все разные слова по алфавитуw2id = {w: i for i, w in enumerate(words)} # слово → номер: {'.': 0, 'кот': 1, …}UNK = len(words) # id для «неизвестного слова»print(words)# dict.get(ключ, запасное): если слова нет в словаре, вернуть UNK вместо ошибки KeyErrorprint([w2id.get(w, UNK) for w in "кот спит на стуле".split()])print([w2id.get(w, UNK) for w in "коты спят на стульях".split()])
# 2. Словарь из символовchars = sorted(set(corpus))c2id = {c: i for i, c in enumerate(chars)}print(len(chars), "символов в словаре")print([c2id.get(c, -1) for c in "коты спят"]) # -1 = символа нет в словаре
# 3. Байты UTF-8: словарь из 256 значений покрывает любой текстfor s in ["token", "токен", "€", "🙂"]: b = list(s.encode("utf-8")) # строка → байты UTF-8 → список чисел 0…255 print(f"{s}: символов {len(s)}, байтов {len(b)} -> {b}")Ожидаемый вывод:
['.', 'кот', 'кошка', 'на', 'сидит', 'спит', 'столе', 'стуле'][1, 5, 3, 7][8, 8, 3, 8]15 символов в словаре[6, 9, 12, -1, 0, 11, 10, -1, 12]token: символов 5, байтов 5 -> [116, 111, 107, 101, 110]токен: символов 5, байтов 10 -> [209, 130, 208, 190, 208, 186, 208, 181, 208, 189]€: символов 1, байтов 3 -> [226, 130, 172]🙂: символов 1, байтов 4 -> [240, 159, 153, 130]Обратите внимание на три вещи:
- словарь из слов не узнал ни «коты», ни «спят», ни «стульях» — три
<unk>(id 8) из четырёх слов; - даже словарь из символов не знает букв
ыия— их просто не было в корпусе; - байтов всего 256, и они покрывают всё, включая эмодзи. Платить за это приходится длиной: «токен» — 10 байтов.
Если хотите посмотреть на настоящий токенизатор, установите pip install tiktoken:
import tiktoken
gpt2 = tiktoken.get_encoding("gpt2") # GPT-2, 2019gpt4o = tiktoken.get_encoding("o200k_base") # GPT-4o, 2024for s in ["Hello, world!", "Привет, мир!", "👍🙂🚀"]: # encode: текст → список id токенов; len — сколько токенов получилось print(f"{s:14} GPT-2: {len(gpt2.encode(s)):2d} GPT-4o: {len(gpt4o.encode(s)):2d}")
ids = gpt4o.encode("Привет, мир!")print(ids)print([gpt4o.decode([i]) for i in ids]) # decode каждого id по отдельности — видно, как резалиHello, world! GPT-2: 4 GPT-4o: 4Привет, мир! GPT-2: 13 GPT-4o: 5👍🙂🚀 GPT-2: 7 GPT-4o: 4[23881, 131903, 11, 37934, 0]['Пр', 'ивет', ',', ' мир', '!']Пробел приклеен к началу слова: мир — отдельный токен, не равный мир. Об этом подробнее в следующем уроке.
А три эмодзи (12 байтов) GPT-2 режет на 7 токенов-кусков байтов. У GPT-4o 👍 и 🙂 — отдельные токены, но 🚀 даже у него
распадается на два куска.
Проверь себя
Заголовок раздела «Проверь себя»- Модель видит не текст, а номера токенов. Длина контекста, цена API и скорость генерации измеряются в токенах.
- Символы дают слишком длинные последовательности, слова — гигантский словарь и неизвестные слова. Подслова — компромисс.
- Размер словаря — это торговля: большой сокращает текст, но стоит параметров в эмбеддингах (и столько же в выходном слое, если веса не связаны). Для DeepSeek-V3 матрица эмбеддингов — 0.14% модели, для GPT-2 small — почти треть.
- Byte-level токенизаторы стартуют с 256 байтов и никогда не выдают
<unk>, но плохо представленный язык распадается на байты. Поэтому русский у GPT-2 стоил в 5.7 раза дороже английского, а у GPT-4o — в 1.3 раза. - Спецтокены (BOS, EOS, PAD, разметка чата) — управляющие сигналы, а не текст.
Дальше: алгоритм Byte Pair Encoding — напишем обучение словаря, encode и decode с нуля: Byte Pair Encoding с нуля.