Как языковая модель видит текст
Модель не читает ни буквы, ни слова — она работает с токенами. Разбираемся, откуда они берутся и почему русский текст обычно обходится дороже английского.
Когда вы отправляете модели запрос, первым делом его разрезают на куски и заменяют каждый кусок числом. Эти куски называются токенами, а программа, которая режет, — токенизатором. Всё, что модель «знает» о вашем тексте, она знает через последовательность этих чисел.
Токен — это не слово и не буква. Частое слово может целиком уместиться в один токен, редкое — рассыпаться на три-четыре фрагмента, а пробел обычно прилипает к началу следующего слова. Словосочетание «контекстное окно», например, могло бы разрезаться так: «кон», «текст», «ное», « ок», «но».
Откуда берётся словарь #
Большинство современных токенизаторов строят словарь алгоритмом BPE (byte pair encoding) или его родственниками. Идея простая:
- Начинаем с минимальных единиц — байтов или символов.
- Считаем, какая пара соседних единиц чаще всего встречается в обучающем корпусе.
- Сливаем эту пару в новую единицу и добавляем её в словарь.
- Повторяем, пока словарь не достигнет нужного размера — обычно десятков или сотен тысяч элементов.
Поскольку всё начинается с байтов, токенизатор может закодировать любой текст: даже незнакомое слово в худшем случае разложится на отдельные байты. Цена за это — длина: чем реже фрагмент встречался при обучении словаря, тем больше токенов на него уйдёт.
Почему русский дороже #
Словарь отражает корпус, на котором его учили. Если в корпусе преобладал английский, частые английские слова получают собственные токены, а русские чаще собираются из нескольких частей. Кроме того, в UTF-8 каждая кириллическая буква занимает два байта против одного у латиницы, так что при нехватке готовых фрагментов разбиение получается ещё мельче.
В новых токенизаторах с большими многоязычными словарями разрыв заметно меньше, но полностью он обычно не исчезает. Проверить это на своих текстах несложно:
import tiktoken
enc = tiktoken.get_encoding("o200k_base")
for text in ["context window", "контекстное окно"]:
ids = enc.encode(text)
pieces = [enc.decode([i]) for i in ids]
print(len(ids), pieces)
У каждой модели свой токенизатор, поэтому точные цифры стоит смотреть в документации или в инструментах конкретного провайдера.
Что из этого следует на практике #
| Что | Почему это связано с токенами |
|---|---|
| Цена запроса | Провайдеры считают входные и выходные токены, а не символы |
| Лимит контекста | Окно измеряется в токенах, и один и тот же документ на разных языках занимает разный объём |
| Скорость ответа | Модель генерирует текст по одному токену за шаг |
| Посимвольные задачи | Модель не видит отдельных букв внутри токена, поэтому считать буквы или переворачивать слова ей трудно |
Прежде чем оценивать бюджет или размер контекста, посчитайте токены на реальных документах. Оценка на глаз по числу символов легко ошибается в полтора-два раза.
Последняя строка таблицы объясняет многие «глупые» ошибки моделей. Вопрос «сколько букв р в слове» кажется тривиальным, но модель получает не буквы, а пару чисел, за которыми эти буквы спрятаны. Если задача требует работы с символами, надёжнее попросить модель написать и выполнить код. Например, так:
Если задача требует точного подсчёта символов, пиши и выполняй код.
Сколько раз буква «р» встречается в слове «прерогатива»?
Посчитаю кодом, чтобы не ошибиться: "прерогатива".count("р") возвращает 2.
Ответ: буква «р» встречается два раза.