Как LLM понимает текст: токены, контекст и генерация
· Пётр Евсиков
Модель - это по сути то, с чем в итоге работает пользователь. Проблема в том, что модель вообще не понимает текст как текст. Для неё это просто числа. Поэтому любой ввод сначала режется на токены - это такие числовые куски. Мы вводим фразу, например: "Я люблю чай", и токенизатор (алгоритм) разбивает этот текст на мелкие фрагменты. Это могут быть отдельные буквы, слоги, части слов или даже целые слова (включая знаки препинания и пробелы). Из текста получаются текстовые фрагменты.
Эти фрагменты ищутся в огромном встроенном словаре, который сопоставляет каждый текстовый фрагмент с уникальным числом (ID). Например, буква 'Я' превращается в ID 12, пробел в ID 1, а часть слова 'ЧАЙ' - в ID 988.

Токенизатор рубит текст перед отправкой в нейросеть, потому что модель видит только массивы целых чисел (ID токенов).
Английская буква весит 1 байт, кириллическая - 2 байта. Проблема кириллицы связана с кодировкой UTF-8 и датасетами. Токенизаторы тренируют в основном на английских текстах, поэтому они запоминают целые английские слова как один токен. Для русского языка места в словаре не остается, кириллица рубится буквально по слогам или парам байтов, отсюда перерасход токенов в 3-4 раза на один и тот же смысл.
Контекстное окно (Context Window)
Контекстное окно - это максимальный размер входной матрицы токенов, которую модель способна обработать за один проход. У самой модели нет памяти, она работает без состояния (stateless). Контекст - это не база данных, а тот объем вопросов и ответов, которые были заданы и отвечены. При каждом новом сообщении интерфейс берет всю историю диалога, приклеивает к ней новый вопрос и отправляет весь этот кусок в модель заново. Механизм внимания вычисляет математические связи между всеми токенами в окне, если в одном диалоге обсуждать архитектуру баз данных, а затем перепрыгнуть на рецепты, матрица засоряется. Сигнал размазывается по несвязанным токенам, и ответы становятся менее точными.

Почему модель может противоречить сама себе? Иногда возникают ситуации, когда модель дала один ответ, а в процессе диалога в чате начала отвечать иначе, противореча тому, что было сказано ранее. Так происходит потому, что модель не проверяет, что она говорила раньше, а каждый раз просто выбирает наиболее вероятное продолжение текста. Она не проверяет факты и в большинстве случаев не будет проверять весь контекст на предмет того, какие ответы она давала. Зачастую ближний контекст (последние 200 токенов) имеет больший математический вес, чем то, что обсуждалось 4000 токенов назад. Поэтому если локальная статистика требует сказать "А", модель скажет "А", проигнорировав свои же прошлые утверждения.
Обучение против Инференса
Это вообще два разных этапа.
Обучение (Training): Процесс формирования матрицы весов. Требует суперкомпьютерных кластеров на GPU, петабайты данных, месяцы работы и миллионы долларов. На выходе получается статический файл весов.
Инференс (Inference): Эксплуатация уже готовой модели и в этом случае матрица весов заморожена (read-only). Модель получает токены, умножает их на веса и выдает вероятности для следующего токена - это требует на порядки меньше вычислительной мощности и запускается локально.

На каждом шаге инференса нейросеть выдает массив "сырых" оценок для всего словаря токенов, и на это влияет несколько параметров:
- Temperature (Температура): Уж не знаю, кому в голову пришло так назвать этот параметр, но он отлично иллюстрирует суть происходящего. Чем ближе к нулю, тем меньше горячки порет модель. Вывод становится сухим и предсказуемым (идеально для кода и аналитики). Если выше единицы - вероятности сглаживаются, в этом случае даже мусорные токены получают шанс на выбор, что повышает
горячку креативность, но ведет к галлюцинациям.
- Top_k: Фильтр строгости - срезает длинный хвост полного бреда. Отсекает все варианты, кроме верхних K (например, топ-40 самых вероятных).
- Top_p (Nucleus sampling): Фильтр вариативности. Алгоритм сортирует токены от самых вероятных к наименее вероятным и складывает их вероятности по порядку до тех пор, пока сумма не достигнет заданного порога. Из сформированного пула модель случайным образом выбирает итоговый токен с учетом их весов.
- Repeat_penalty: Штраф на токены, которые уже есть в контексте. Принудительно занижает их вероятность, чтобы модель не застряла в бесконечном цикле повторений одного слова.
Часто возникает иллюзия: если у нас есть свои данные (базы, документы) и мы хотим заставить модель опираться на них без дорогого переобучения, достаточно просто покрутить настройки вроде Temperature или Top_k. Но это так не работает. Параметры семплирования не заставят модель приоритизировать ваши факты - они лишь управляют уровнем ее фантазии.
Почему вывод недетерминирован:
Нейросеть генерирует разный текст по двум причинам: из-за заложенной в алгоритм случайности и особенностей параллельных вычислений. С одной стороны, базовые настройки (температура) заставляют модель буквально «бросать кубики» при выборе каждого следующего слова, чтобы ответы получались более гибкими и разнообразными. С другой - из-за специфики работы с числами с плавающей точкой порядок их сложения каждый раз немного разный, а в такой математике от перемены мест слагаемых сумма меняется. Эти микроскопические погрешности округления накапливаются хаотично и в пограничных ситуациях могут перевесить чашу весов в пользу другого токена.