От прогнозирования следующего токена к рабочему коду: как большие языковые модели генерируют Python
По порядку объясняются токены, алгоритм softmax, параметр температуры, механизм самовнимания и слои с передачей сигнала вперед, приводится пример пошаговой сортировки, а также жесткие ограничения, вытекающие из процесса прогнозирования.
Источники: How LLMs Actually Work: From Predicting the Next Word to Writing Code.
Инструменты вроде ChatGPT и Copilot пишут функции, исправляют ошибки и объясняют алгоритмы, и может показаться, что происходит нечто большее, чем просто завершение текста. Приведённая ниже модель работы начинается с основ: прогнозирование токенов, механизм внимания, а также с объяснения того, почему механизм, завершающий предложение, одновременно генерирует корректный код на Python.
Один вопрос, задаваемый снова и снова
Независимо от того, является ли результат хайку, маркетинговым письмом или алгоритмом сортировки слиянием, языковая модель постоянно отвечает на один и тот же вопрос: исходя из всего увиденного до сих пор, какой токен наиболее вероятен дальше?
Не существует семантического понимания в традиционном смысле, и нет этапа, на котором модель решает написать функцию до того, как вывести слово def. Она предсказывает токены по одному. Благодаря миллиардам параметров и тексту для обучения, насчитывающему сотни миллиардов токенов, эти предсказания в конечном итоге выглядят очень похоже на процесс рассуждения.
Что такое токен
Точность помогает ещё до начала процесса предсказания. Модели работают не с символами или словами, а с токенами — подсловными единицами, создаваемыми алгоритмами вроде кодирования пар символов. В общих чертах:
- Обычные слова обычно превращаются в отдельные токены, такие как
"the","def"и"return". - Редкие слова делятся на части, поэтому
"unhappiness"превращается в["un", "happiness"].
"(", ":", "==" и "->".Для GPT-4 один токен соответствует примерно 0,75 английского слова, а в коде он обычно содержит 3–4 символа, поскольку код состоит из однозначных токенов вроде : и (.
У модели также есть словарь — фиксированный список известных токенов, который обычно насчитывает от 50 000 до 100 000 элементов. На каждом шаге модель присваивает каждому элементу вероятность и либо выбирает наиболее вероятный вариант, либо берет значение из распределения, причем выбор определяется параметром температуры.
Цикл прогнозирования
Для последовательности «The cat sat on the» необходимо вычислить условную вероятность:
P(next_token | "The", "cat", "sat", "on", "the")
Это распределение по всему словарю. Прохождение сигнала через сеть генерирует вектор размером с словарь, который функция softmax преобразует в допустимое вероятностное распределение:
P(token_i) = exp(logit_i) / Σⱼ exp(logit_j)
При жадном декодировании выбирается токен с наибольшей вероятностью. В противном случае токен выбирается пропорционально распределению, причем параметр temperature контролирует степень его распространения:
temperature = 0всегда выбирает токен с наивысшей вероятностью, поэтому результат является детерминированным.temperature = 1позволяет выбирать токены из исходного распределения, что придает результатам разнообразие и креативность.- При значении выше 1 распределение становится более плоским, и результаты становятся более случайными.
- При значении ниже 1 распределение становится более острым, и результаты становятся более консервативными.
Именно поэтому один и тот же запрос может давать разные ответы при разных запусках.
Transformer
Функция, преобразующая последовательность токенов в распределение следующего токена, — это Transformer, а именно версия, предназначенная исключительно для декодирования; именно такая архитектура лежит в основе GPT, Llama, Mistral и Claude. Общий алгоритм работы:
Token IDs → Embedding layer → [Attention + FFN] × N layers → Linear head → Softmax → P(next token)
Элементы в порядке:
- Слой встраивания. Каждый целочисленный идентификатор токена преобразуется в плотный вектор размером
d_model; например, 4096 в Llama-3 8B. Позиционные встраивания, или RoPE в современных моделях, добавляют информацию о порядке. - Слои Transformer, повторяющиеся N раз. Каждый из них содержит многоголовую самоаттенцию, сеть с передачей вперед, нормализацию слоя, применяемую перед каждым подслоем в стиле «pre-norm», и остаточные соединения.
- Линейный слой и функция softmax. Итоговое скрытое состояние преобразуется в логиты формы
[|V|], а функция softmax формирует распределение.
Самоаттенция
Механизм самообратной связи позволяет токену учитывать другие токены в последовательности, чтобы получить более полное представление о себе. Именно это даёт трансформерам преимущество перед RNN: каждый токен может в одном шаге взаимодействовать со всеми остальными токенами, без снижения интенсивности градиента с увеличением расстояния.
При наличии n токенов каждый из них проецируется тремя способами:
Q = XWᴬ_Q (Query: what am I looking for?)
K = XWᴬ_K (Key: what do I contain?)
V = XWᴬ_V (Value: what do I contribute?)
Здесь X — это входная матрица формы [n, d_model], а три матрицы W представляют собой выученные веса. Оценка между токеном i, действующим в качестве запроса, и токеном j, действующим в качестве ключа, равна:
score(i, j) = qᵢ · kⱼᵀ / √dₖ
Деление на квадратный корень ключевого измерения предотвращает слишком большое увеличение скалярных произведений, когда dₖ велик, что могло бы привести к тому, что функция softmax окажется в областях с крайне малыми градиентами. Полная формула вычисления следующая:
Attention(Q, K, V) = softmax(QKᵀ / √dₖ) · V
В моделях, предназначенных исключительно для декодирования, таких как GPT, внимание носит причинно-следственный характер или является замаскированным: токен i может обращать внимание только на позиции до i. Позиции в будущем получают оценку в виде отрицательного бесконечности перед применением функции softmax, поэтому их веса равны нулю.
Многоголовое внимание выполняет h независимых функций внимания параллельно, каждая из которых имеет меньшую размерность d_model / h, после чего объединяет полученные результаты:
MultiHead(Q, K, V) = Concat(head₁, ..., headₕ) · W_O
headᵢ = Attention(QWᵢ_Q, KWᵢ_K, VWᵢ_V)
Разные механизмы внимания обрабатывают разные связи между элементами. Один может опираться на синтаксические зависимости, другой — на кореференцию, а третий — на близость расположения.
Сеть с передачей сигнала вперед
После формирования внимания представление каждого токена проходит через функцию FFN, зависящую от его позиции:
FFN(x) = max(0, xW₁ + b₁)W₂ + b₂
Современные архитектуры, такие как Llama и Mistral, вместо этого используют SwiGLU:
FFN(x) = (SiLU(xW_gate) ⊙ xW_up) · W_down
Большая часть запомненных знаний модели хранится в FFN. Он функционирует как хранилище типа «ключ-значение», обрабатывающее фактические связи. Механизм внимания передает информацию между токенами, а FFN находит сохраненные факты и шаблоны. В крупных моделях ширина FFN примерно в четыре раза превышает значение d_model. У Llama-3 70B d_model = 8192, а средний размер составляет 28672, и именно там находится большинство параметров.
Почему модели могут писать код
Код — это последовательность токенов, такая же, как и естественный язык. Для модели функция на Python не представляет ничего особенного — это просто набор токенов вроде def, sort, _list, (, arr, ), : и т. д.
Синтаксически корректный вывод является чисто следствием процесса обучения. Рассмотрим следующие условные вероятности:
P("(" | ..., "def", " quicksort") ≈ 1.0
P(":" | ..., "def", " quicksort", "(", "arr", ")") ≈ 1.0
P("return" | context inside function body) >> P("import" | same context)
Они имеют резкий пик, поскольку модель ознакомилась с миллионами файлов на Python, все из которых соответствуют одинаковым правилам. Грамматика Python нигде не закодирована жестко в сети — она формируется на основе статистики корпуса данных.
Это также объясняет, почему модели иногда пишут код, который формально корректен, но логически неверен. Они максимизируют вероятность следующего токена с учетом распределения данных обучения. Они не запускают код и не проверяют его.
Функция сортировки, шаг за шагом
Предположим, запрос звучит как «Напишите функцию на Python для сортировки списка». Модель рассматривает его как последовательность токенов и добавляет результат по одному токену за раз. Примерно так:
- Шаг 1, после запроса:
defс вероятностью около 0,91. - Шаг 2, после
def:sortс вероятностью около 0,78. - Шаг 3:
_listили(с вероятностью около 0,65. - Шаг 4: после
sort(—arrилиlstпримерно в 0,71. - Шаги 5–7:
), затем:, затем перенос строки; каждый элемент примерно в 0,99. - Шаг 8: отступ в четыре пробела примерно в 0,97.
На шагах 5–8 модель достигает высокой степени уверенности в синтаксических токенах. Однако это не означает понимание Python — речь идет лишь о простом заполнении шаблонов, выученном на корпусе, в котором все определения функций выглядят одинаково.
Настоящий выбор происходит раньше, на шагах 1–4, когда определяются имя функции, имена параметров и общая структура. Эти выборы формируются под влиянием содержимого запроса через механизм внимания. Токен sort в запросе имеет высокий вес внимания при генерации имени функции.
Температура при генерации кода
Инструменты вроде Copilot обычно работают при низкой температуре, около 0,1–0,3, для обработки кода:
- Синтаксические токены должны быть почти детерминированными.
- Часто существует только один правильный вариант реализации.
- Изобретенные API становятся причиной серьезных сбоев при высокой температуре.
Для творческих элементов, таких как имена переменных, документация и комментарии, допустима немного более высокая температура. Некоторые инструменты также используют метод поиска лучшего потока, который одновременно сохраняет кратчайшие последовательности из k элементов и в конце выбирает ту, у которой наибольшая совместная вероятность.
Почему более крупные модели ведут себя иначе
Модели объемом 7 миллиардов и 70 миллиардов элементов имеют одинаковую архитектуру. Разница заключается в масштабе: больше слоев, более большое значение d_model и больше голов внимания. Тем не менее более крупные модели демонстрируют качественные улучшения, включая многошаговое рассуждение, более корректный код и способность к обучению в контексте.
В масштабном режиме модель учится не только статистике поверхностных токенов. Она формирует сжатые представления более глубоких закономерностей, таких как алгоритмическая структура, логические зависимости и ограничения типов. Модель объемом 70 миллиардов параметров имела достаточно кода с ошибками наряду с примерами неудачных тестов, что позволило ей косвенно связать определенные закономерности с корректностью работы. Это и является эмпирической основой гипотезы масштабирования: способности растут по плавной кривой в зависимости от вычислительных мощностей, объема данных и количества параметров, причем качество кода развивается так же надежно, как и любой другой навык.
Ограничения, вытекающие из конструкции
Знание механизма работы модели позволяет выявить некоторые строгие ограничения:
- Выполнение. Внутри модели отсутствует механизм выполнения. То, что она генерирует, напоминает результат корректного выполнения, но на самом деле ничего не выполнялось.
- Гарантированная точность. Прогноз основан на максимизации вероятности в рамках обучающего набора данных, а не на логической корректности.
- Настоящая новизна. Алгоритм без статистических аналогов в обучающих данных невозможен к созданию. Модель может комбинировать и адаптировать, но не создавать что-то новое.
- Точное воспроизведение. Запасных таблиц нет; вся память распределена между весами, поэтому API, сигнатуры функций и версии библиотек иногда могут быть искажены.
Ни один из этих факторов не является ошибкой, требующей исправления. Они напрямую вытекают из архитектуры, и знание их позволяет любому пользователю лучше использовать помощников по программированию.
Краткий обзор всей картины
- Токен — это подсловная единица, соответствующая примерно 0,75 английского слова.
- Прогноз формируется с помощью функции softmax над словарем на каждом шаге.
- Механизм самообращения позволяет каждому токену учитывать все предыдущие токены с помощью масштабированных произведений запроса и ключа.
- Многоголовой механизм обращения выполняет несколько таких процессов параллельно для захвата различных связей.
- Функция FFN выполняет роль памяти вида «ключ-значение», хранящей фактические ассоциации.
- Генерация кода происходит аналогично генерации текста, причем синтаксис формируется на основе статистики обучения.
- Параметр температуры определяет степень «остроты» распределения выборки.
- Ключевые ограничения заключаются в отсутствии гарантированной корректности выполнения и настоящей новизны.
Все компоненты этой системы — от механизма обращения и функции FFN до остатков и нормы слоя — служат одной цели: сделать условную вероятность появления следующего токена при наличии всех предыдущих токенов максимально точной. Все остальное возникает автоматически.
Связанные материалы
- Внутри LLM: инференс на чистом Python для Qwen3-0.6B с использованием MLX — пошаговая реализация процесса инференса LLM примерно из 200 строк кода на Python с применением MLX: токенизатор, RMSNorm, RoPE, группированное внимание к запросам, SwiGLU, кэш KV и механизм выборки.
- От прогнозирования следующего токена к выполнению инструкций: тонкая настройка модели GPT — как заранее обученная модель GPT учится выполнять инструкции: форматирование записей, сдвиг меток, маски заполнения, цикл обучения, ограничения оценки и шаги к реализации принципов DPO.