Когда чат-бот отвечает на вопрос, кажется, будто ответ у него готов целиком. На деле процесс устроен проще и строже. Авторегрессионная языковая модель на каждом шаге получает уже имеющийся текст и вычисляет распределение вероятностей для следующего фрагмента, токена. Документация Hugging Face называет такой режим каузальным языковым моделированием: модель предсказывает следующий токен и видит только то, что стоит слева от него.

Сразу о границах статьи. Речь пойдёт о самой модели, а не об агентах, поиске в интернете или корпоративных системах, которые строят вокруг неё. Описание относится прежде всего к GPT-подобной модели с каузальным вниманием. Не всякая нейросеть, которую называют языковой моделью, работает так же: в той же документации каузальный вариант отделён от masked language modeling, где модель восстанавливает скрытые фрагменты, видя контекст с обеих сторон.

Маршрут целиком

Прежде чем разбирать детали, полезно увидеть весь путь:

текст → токены → ID → векторы токенов + сведения о позиции
      → повторяющиеся блоки Transformer
      → оценки и вероятности следующего токена
      → выбор токена → добавление к контексту → повтор

Это учебная схема. Она опускает различия между реализациями и последующие этапы обучения, поэтому её нельзя считать спецификацией каждой современной языковой модели. Основу схемы задают две работы: статья Attention Is All You Need, где описан Transformer, и работа OpenAI о генеративном предобучении, где используется вариант из одного декодера. Важная оговорка: исходный Transformer был архитектурой encoder-decoder для преобразования одной последовательности в другую. Энкодер в нём был; GPT-подобные модели обходятся без него, и дальше мы следуем именно их маршруту.

Шаг 1. Текст превращается в числа

Нейросеть не работает с буквами напрямую. Сначала токенизатор режет текст на фрагменты и сопоставляет каждому числовой идентификатор (ID). В руководстве OpenAI How to count tokens with Tiktoken есть пример, который удобно взять сквозным. Сам рецепт помечен как архивный и может содержать устаревшие сведения, но приведённое в нём разбиение остаётся удобной иллюстрацией. Для кодировки r50k_base строка «2 + 2 = 4» раскладывается так:

Байтовый фрагмент ID
b'2' 17
b' +' 1343
b' 2' 362
b' =' 796
b' 4' 604

Итого ID [17, 1343, 362, 796, 604] и фрагменты [b'2', b' +', b' 2', b' =', b' 4']. Пробел здесь входит в токен: « 2» с пробелом и «2» без него получили разные номера. Отсюда главный вывод о токенах: токен не обязательно совпадает со словом, а его границы и номер зависят от кодировки. В том же руководстве другая кодировка разбивает ту же строку иначе. Поэтому числовые ID без названия кодировки ничего не значат, и переносить их на другую модель нельзя.

Дальше в примере возьмём первые четыре токена, то есть вход «2 + 2 =» с ID [17, 1343, 362, 796]. Токен « 4» с ID 604 будем рассматривать лишь как возможное продолжение. Это иллюстрация того, как устроена задача, а не измеренное предсказание какой-либо модели.

Шаг 2. От ID к вектору: модели нужно учитывать позицию

ID сам по себе ничего не говорит о смысле. Это адрес в таблице, где каждому токену соответствует обучаемый вектор, список чисел, значения которых подбираются во время обучения. Удобна аналогия с карточками: номер карточки не равен её содержанию, он лишь помогает её найти.

Однако одних векторов токенов мало. Сравним «2 + 2 =» и «= 2 + 2». Символы те же, но перестановка меняет и сами токены: знак равенства в начале строки идёт без ведущего пробела и потому кодируется не токеном b' =' с ID 796, а первая «2», оказавшись после пробела, превращается в токен b' 2' (362). Но даже там, где набор карточек совпадает, таблица векторов не помогает различить порядок: один и тот же ID всегда получает один и тот же вектор, на какой бы позиции он ни стоял. Чтобы вычисления учитывали порядок, архитектуре нужен способ кодировать или учитывать позицию токена. В исходном Transformer позиционные кодировки складывались с представлениями токенов; в описанной GPT-модели OpenAI использовались обучаемые позиционные представления. Это конкретные варианты: в других архитектурах позиция может учитываться иначе, поэтому позиционный вектор не всегда прибавляется к вектору токена.

К аналогии с карточками вернёмся ещё раз ниже, но с оговоркой заранее: она помогает представить поток данных и ничего не говорит о мышлении. Модель не «читает» карточки, а выполняет над векторами матричные вычисления.

Шаг 3. Внимание: как позиции обмениваются сведениями

В центре Transformer стоит механизм внимания. Из текущих представлений с помощью обучаемых проекций вычисляют три матрицы: запросы Q, ключи K и значения V. Затем, согласно исходной статье, считают:

Attention(Q,K,V) = softmax(QKᵀ/√d_k)V

Обозначения:

  • Q — матрица запросов;
  • K — матрица ключей, Kᵀ — транспонированная K;
  • V — матрица значений;
  • d_k — размерность ключа;
  • softmax — преобразование оценок в неотрицательные веса, которые в сумме дают единицу.

Смысл формулы такой. Произведение QKᵀ даёт оценку того, насколько каждая позиция «соответствует» каждой другой; деление на √d_k масштабирует эти оценки; softmax превращает их в веса; наконец, веса смешивают значения V. В терминах карточек: для текущей позиции внимание временно взвешивает сведения с других карточек и собирает из них новое представление.

Теперь о каузальной маске. Она нужна, чтобы позиция не могла использовать будущие токены. Маску применяют к оценкам до softmax: оценки запрещённых будущих позиций заменяют, в исходной работе на −∞. Поскольку exp(−∞) = 0, после softmax веса этих позиций оказываются равными нулю, а веса разрешённых позиций остаются положительными. Так при обработке «2 + 2 =» позиция токена « +» не получает сведений о токенах, стоящих правее.

Формула описывает один механизм внимания. Многоголовое внимание повторяет тот же расчёт параллельно с разными обучаемыми проекциями, и каждая «голова» может взвешивать позиции по-своему.

Ещё одна важная оговорка. Внимание — это вычисление весов для смешивания представлений, а не человеческое размышление. Соблазнительно смотреть на большие веса как на объяснение ответа, но исследование Attention is not Explanation показало, что веса внимания сами по себе не дают надёжного объяснения того, почему модель выдала конкретный результат. Этот вывод относится к рассмотренным там стандартным модулям и задачам; теоремой обо всех архитектурах он не является, но служит хорошим поводом для осторожности.

Шаг 4. Блок Transformer и его повторение

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

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

Шаг 5. От вероятностей к выбранному токену

После последнего блока представление последней позиции преобразуют в оценки для каждого токена словаря, а softmax превращает их в вероятности. Это делается и в исходном Transformer, и в GPT. При генерации нужны только оценки последней позиции; при авторегрессионном предобучении, о котором ниже, их вычисляют сразу для всех позиций и сравнивают с той же последовательностью, сдвинутой на один токен. В нашем примере на выходе получилось бы распределение по всем токенам кодировки, и « 4» с ID 604 был бы одним из кандидатов.

Но распределение ещё не ответ. Какой токен взять, определяет отдельное правило декодирования, которое описывает руководство Hugging Face по стратегиям генерации:

  • жадное декодирование выбирает токен с наибольшей вероятностью;
  • сэмплирование выбирает токен случайно, согласно распределению.

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

Обучение и вывод: когда меняются веса

Все обучаемые векторы, проекции и веса MLP вместе называют параметрами. Они подбираются во время обучения. На этапе авторегрессионного предобучения, как описано в работе о GPT, цель — предсказание следующего токена. Однако это не единственный этап: в той же работе за предобучением следует дообучение с учителем, где целевая задача зависит от конкретного задания, и последующие этапы дообучения вообще могут использовать другие цели. Сам цикл обновления параметров выглядит так, как его показывает учебник PyTorch Optimizing Model Parameters:

  1. модель делает предсказания;
  2. их сравнивают с целевыми значениями (при предобучении — со следующими токенами) и вычисляют функцию потерь;
  3. обратное распространение даёт градиенты;
  4. оптимизатор изменяет параметры.

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

Практические границы

Контекстное окно. Число токенов, которые модель использует в одном запросе, ограничено. В руководстве OpenAI Conversation state для описанного API в лимит входят входные и выходные токены, а для соответствующих моделей — и токены рассуждения. Конкретный лимит зависит от модели и способа её использования, а правило обработки его превышения нельзя обобщать на все реализации. И главное: окно — это ограничение текущего запроса, а не свидетельство долговременной памяти.

Параметры — не база данных. Параметры способны неявно содержать сведения, но, как подчёркивают авторы работы о Retrieval-Augmented Generation, они не дают надёжного механизма адресного поиска, проверки происхождения и точечного обновления фактов, как явное внешнее хранилище. При этом «не база данных» не значит «ничего не запоминает»: в исследовании Extracting Training Data from Large Language Models из GPT-2 удалось извлечь отдельные дословно запомненные обучающие фрагменты.

Правдоподобие не равно истине. Модель может уверенно выдать гладкое, но ложное утверждение. В материале OpenAI Why language models hallucinate отмечается, что особенно трудно выводить из текстовых закономерностей произвольные редкие факты. Источник разбирает возможные причины ошибок, а не неизбежность каждой конкретной ошибки, но вывод для пользователя прямой: уверенный тон не гарантирует точности, и фактические ответы стоит проверять независимо.

Архитектуры различаются. Маршрут, прослеженный выше, — упрощение. Исходный Transformer был encoder-decoder, GPT — только декодер, а современные модели отличаются способами учёта позиции, устройством блоков и дополнительными этапами обучения. Схема помогает понять, что происходит с текстом между вводом и ответом, но не заменяет документацию конкретной модели.