Модуль 1. Токены и эмбеддинги: текст как числа

Цель модуля: понять, что модель на самом деле «читает» — не буквы и не слова, а токены, — и как текст превращается в векторы, с которыми можно считать. После модуля вы объясните, почему модель путает опечатки, сколько «стоит» ваш текст в контексте и как искать похожие тикеты без нейросети.

Перед началом: ничего из ML не нужно — модуль с нуля. Инструменты: песочница сайта (чистый Python), два сниппета ниже. Трудоёмкость: ~1–1.5 ч (чтение 40 мин · сниппеты и практика 30 мин).

🔥 «Модель не заметила опечатку — и это не баг»

Вы делаете автоответчик поддержки. Пользователь пишет «верните денги за курс» — модель отвечает идеально. Пользователь пишет «верните деньги за курс» — и та же модель вдруг спрашивает уточнение. Дебаг по «промптам» ничего не даёт: тексты почти одинаковые.

Причина в слое ниже промптов: модель не видит ни «денги», ни «деньги» как слова. Она видит последовательность чисел, и два этих слова после токенизации оказались почти одинаковыми числами — а «курс» (обучение) и «курс» (валюта) — одинаковым токеном. Пока вы не понимаете этот слой, половина поведения LLM выглядит как необъяснимая магия: почему модель не может сосчитать буквы в слове «строфа», почему лимит контекста именно «в токенах», почему API берёт деньги «за токены», почему «суперкалифрагилистический» съедает в разы больше места, чем «очень».

Этот слой — первые две ступени любого LLM-пайплайна: токенизация (текст → последовательность токенов) и эмбеддинг (токен → вектор). Разберём обе.

📖 1.1. Токен — атом текста: BPE на пальцах

Токенизатор — это словарь «кусочек текста → номер». Хочется резать по словам, но словарь взорвётся: язык живой, есть опечатки, морфология («низкий», «низкая», «низким» — три слова?). Резать по буквам — словарь крошечный, но последовательности длинные и дорогие.

Компромисс нашёл BPE (Byte Pair Encoding): начинаем с символов и жадно склеиваем самые частые пары. Идея наивна до безобразия — «что часто встречается рядом, тому один токен», — но именно так устроены словари GPT, Llama и YandexGPT. Запустите первый сниппет: он обучает мини-BPE на девяти словах и пять раз выполняет цикл «посчитай частые пары → склей самую частую».

Что вы увидите: после пары шагов «выс» и «низ» станут токенами, потом «ость» и «скор». Отсюда три наблюдения, которые объясняют поведение больших моделей:

  • Частое — коротко, редкое — длинно. «Деньги» — один-два токена, «суперкалифрагилистический» — восемь. Редкие слова, имена, GUID режутся в лоскуты, и модель «узнаёт» их хуже.
  • Опечатка меняет разбиение чуть-чуть. «Денги» режется иначе, чем «деньги», но куски почти те же — поэтому модель часто понимает опечатки и в то же время путает похоже выглядящие разные слова.
  • Морфология живёт внутри токенов. «Низкий» и «низкая» делят общий токен «низ» — модель «видит» родство форм не правилом, а устройством словаря.

Разные семейства моделей — разные токенизаторы. «1000 токенов» у YandexGPT и у Llama — это немного разные объёмы текста; при переносе промптов между моделями количество токенов (и цена запроса) меняется.

📖 1.2. Практические следствия: контекст, цена, счёт букв

Токены — валюта всего мира LLM. Три места, где вы это ощутите первым делом:

Лимит контекста измеряется в токенах. «Контекст 8k/32k/128k» — это не символы. Русский текст в среднем ~2–3 символа на токен (англ. удобнее для токенизатора — исторически словари обучали в основном на английском). Правило для прикидки: страница А4 ≈ 500 слов ≈ 900–1300 токенов русского текста.

Цена API — за токены. И запрос, и ответ тарифицируются по токенам; ответ на 500 токенов «стоит» и в деньгах, и в latency. Стало быть, «сделайте ответ короче» — это инженерное требование к декодеру и шаблону, а не пожелание модели.

Модель не умеет считать буквы. Сколько букв «о» в слове «молоко»? Для вас пять, для модели «молоко» — два-три токена, и внутри токена букв уже нет. Отсюда классические провалы LLM в «элементарных» задачах на посимвольную работу — и простое решение: считать должен код, а не модель.

📖 1.3. Эмбеддинг: у смысла есть адрес

Токен — номер в словаре. Но «номер» не передаёт близость: токены 48271 и 48272 могут быть «кот» и «транзистор». Чтобы модель могла оперировать смыслом, каждый токен превращается в вектор — список чисел фиксированной длины (сотни измерений). Это и есть эмбеддинг: «адрес слова в пространстве смыслов».

Ключевое свойство: близкие по смыслу слова получают близкие векторы — не потому что кто-то так расписал, а потому что векторы обучаются из контекстов употребления: слова, встречающиеся в похожих окружениях, «стекаются» в одно место пространства. Запустите второй сниппет: в игрушечном шестимерном пространстве он считает ближайших соседей слова и показывает знаменитый эффект — арифметику смыслов, «король − мужчина + женщина ≈ королева».

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

Важно: эмбеддинги бывают не только у слов. Модель-эмбеддер (например, эмбеддинги AI Studio) превращает в вектор целый текст — тикет, абзац, документ. Тогда «похожие тикеты» — это «близкие векторы», и поиск соседей становится школьной геометрией.

📖 1.4. Где это уже работает у вас

Три применения, которые окупают этот модуль уже до всякого дообучения:

  • Поиск похожих. Входящий тикет → вектор → ближайшие соседи по базе прошлых тикетов → «такое уже было, вот решение». Никакой генерации, детерминированно, мгновенно.
  • Дедупликация датасета. Готовя датасет для дообучения (модуль 5), near-дубликаты ищут той же косинусной близостью — в сниппете модуля 5 вы сделаете это шинглами, для больших корпусов — эмбеддингами.
  • RAG. Поиск релевантных документов для подкладывания в контекст — это поиск ближайших векторов (модуль 4). Вся модная технология — «эмбеддинги + ближайшие соседи + генерация».

📖 1.5. Компромиссы

  • Словарь больше → последовательность короче, но embedding-таблица больше. 30k токенов — короткие последовательности и тяжёлая таблица; 200k — наоборот. Вендоры балансируют.
  • Символьный vs словарный токенизатор. Символьный не режет слово в лоскуты и лучше для редких языков/кода-в-коде; словарный BPE в разы дешевле по контексту. Побеждает BPE почти всегда.
  • Размерность эмбеддинга. Больше измерений — тоньше смыслы, дороже хранение и медленнее поиск. Для поиска по базе тикетов редко нужно больше 1024 измерений.
  • Один эмбеддер для всего vs под задачу. Универсальный мультиязычный эмбеддер удобен, но на узком домене (жаргон вашей поддержки) специализированный часто точнее — и это тоже аргумент в пользу дообучения.

💻 Практика

  1. BPE руками (15 мин). Запустите bpe-merge-stats.py. Добавьте в корпус своё слово с частотой 10 — убедитесь, что после перезапуска именно его пары мержатся первыми. Объясните, почему «высокий» и «высокая» делят токен, а «высокое» — нет (проверьте).
  2. Соседи смыслов (10 мин). Запустите cosine-similarity.py. Добавьте слово «принцесса» с осмысленным вектором и проверьте соседей. Измените одну ось у «сервера» — как меняются соседи?
  3. Прикидка токенов (10 мин). Возьмите любой свой текст на страницу. Посчитайте слова, умножьте на 2–2.5 — это оценка токенов для русского. Сравните с лимитом контекста модели, с которой работаете: сколько таких страниц влезает?
  4. Счётчик букв (5 мин). Напишите однострочник на Python len("молоко") против токенизации «на глаз» — и сформулируйте правило: какие текстовые задачи никогда не отдавать модели, а решать кодом.

🧩 Quiz "Проверь себя"

1. Что такое токен в LLM?
2. Почему «суперкалифрагилистический» «дороже» слова «очень»?
3. Почему модель часто понимает «верните денги» так же, как «верните деньги»?
4. Поиск похожих тикетов эмбеддингами — это…
5. Косинусная близость сравнивает векторы по…

✅ Итоги модуля

  • Объяснить коллеге BPE за две минуты без формул.
  • Назвать три следствия токенизации: опечатки, счёт букв, цена контекста.
  • Оценить токены страницы русского текста без токенизатора.
  • Считать косинусную близость и находить ближайших соседей по сниппету.
  • Привести два применения эмбеддингов без генерации (поиск, дедуп).

🔗 Мостик к Модулю 2

Теперь у модели есть токены и их векторы. Но статичный вектор слова «ключ» одинаков и в «ключе от серверной», и в «ключе API» — а модель различает эти слова безошибочно. Механизм, который делает вектор контекстным, — attention, сердце трансформера. В следующем модуле соберём его из трёх умножений и softmax — и заодно поймём, что же такое «7B параметров».