Как токены проходят через слои LLM и превращаются в ответ
Токенизация уже случилась — на входе у нас последовательность токенов. Разбираем всё, что происходит дальше: как токен становится вектором, что именно меняется в этом векторе на каждом слое сети, и как после последнего слоя из чисел получается конкретное следующее слово.
Новый токен дописывается в конец последовательности, и весь путь повторяется заново для генерации следующего токена — так рождается ответ, слово за словом.
Точка входа: токен становится вектором
Каждый токен — это просто число (индекс в словаре). Первый шаг — превратить это число в вектор, с которым сеть может что-то делать математически.
x₀ = Embedding[token_id] # строка из таблицы Embedding размера (словарь × d_model)
x₀ = x₀ + PositionInfo(i) # добавляем информацию о позиции токена i в последовательности
Результат — не одно число, а вектор размерностью d_model (у реальных моделей — от нескольких сотен до десятков тысяч). Для последовательности из n токенов получается матрица [n, d_model]: у каждой позиции — свой вектор. Позиция нужна отдельно, потому что сам механизм внимания дальше не различает порядок токенов — без этой добавки «кот сидит на диване» и «диване на сидит кот» выглядели бы для сети одинаково.
Остаточный поток: данные, которые не заменяются, а накапливаются
Это центральная идея, без которой непонятно, что вообще происходит внутри слоёв.
Представьте вектор каждого токена как непрерывный поток данных, который течёт через всю сеть сверху вниз. Ни один слой не подменяет этот вектор целиком — каждый слой лишь читает из потока текущее состояние, что-то вычисляет, и добавляет результат обратно (складывает, а не перезаписывает). К концу сети вектор токена — это сумма исходного эмбеддинга и вкладов от каждого слоя.
x_после_слоя = x_до_слоя + вклад_слоя(x_до_слоя) # residual connection — суть всей архитектуры
Ниже голубая полоса — это и есть тот самый остаточный поток. Полые кружки — точки, где слой только читает из потока (нормализация). Закрашенные — точки, где в поток что-то добавляется.
Что происходит внутри одного слоя декодера
Каждый слой — это ровно шесть шагов, выполняемых по порядку. Дальше разбираем каждый шаг отдельно на одном слое — все остальные слои устроены идентично, отличаются только выученные веса.
Перед тем как что-то вычислять, вектор каждого токена независимо приводится к одному масштабу: вычитается среднее, делится на стандартное отклонение, затем применяется обучаемое масштабирование γ и сдвиг β. Это не меняет сам остаточный поток — нормализованная копия используется только как вход для следующего вычисления (attention), а исходный вектор в потоке остаётся нетронутым.
LN(x) = γ · (x − mean(x)) / std(x) + β
Смысл шага: по мере прохождения через слои вектор в остаточном потоке накапливает всё больше добавленных вкладов и может сильно вырасти по масштабу — нормализация каждый раз возвращает вход внимания и FFN к предсказуемому диапазону значений, что критично для стабильности обучения и инференса.
2
Self-Attention вычисление
Здесь токены обмениваются информацией друг с другом — это единственный шаг во всём слое, где вектор одной позиции может зависеть от векторов других позиций.
Q = x·W_Q, K = x·W_K, V = x·W_V
score(i,j) = (Qᵢ · Kⱼᵀ) / √d_head # насколько токену i важен токен j
score(i,j) = −∞, если j > i # причинная маска: будущее не видно
weights = softmax(score) # в сумме по j дают 1
outᵢ = Σⱼ weights(i,j) · Vⱼ # взвешенная сумма чужих V — это и есть «прочитанная» информация
В GPT-подобных (декодерных) моделях действует причинная маска: токен на позиции i может «смотреть» только на себя и на предыдущие позиции, но не на будущие — иначе предсказание следующего токена было бы попросту подсматриванием ответа.
Причинная маска: t5 видит t1..t5, но t1 видит только себя — треугольный, а не полный веер связей
В реальности этот процесс идёт параллельно в нескольких головах внимания (multi-head) — каждая голова считает свой набор Q/K/V и может научиться улавливать разные типы связей (согласование слов, кореференцию местоимений, синтаксические зависимости), после чего результаты всех голов склеиваются и проецируются обратно в размерность d_model.
+
Сложение с потоком запись
Результат внимания прибавляется к тому вектору, что был в потоке до этого шага (а не заменяет его): x = x + Attention(LN(x)). Исходная информация о токене никуда не девается — сверху просто добавляется «то, что токен узнал от других токенов».
3
Вторая нормализация чтение
Точно та же операция, что и в шаге 1, с собственными обучаемыми параметрами γ, β — снова просто готовит текущее состояние потока к следующему вычислению, не изменяя сам поток напрямую.
4
Feed-Forward сеть (FFN) вычисление
В отличие от внимания, FFN обрабатывает каждую позицию независимо — вектор токена t3 не видит и не влияет на вектор токена t5 на этом шаге. Это просто двухслойный (или трёхслойный при гейтинге) перцептрон, применяемый к каждому токену одинаково:
h = activation(x · W1) # проекция вверх, в более широкое пространство (обычно ×4)
out = h · W2 # проекция обратно в d_model
Именно здесь, по современным представлениям интерпретируемости, хранится и обрабатывается значительная часть «фактических знаний» модели — FFN часто описывают как ассоциативную память ключ-значение: первая матрица определяет, какой «паттерн» активировался во входе, вторая — что «вписать» в поток в ответ на этот паттерн.
+
Второе сложение с потоком запись
Выход FFN снова прибавляется к потоку: x = x + FFN(LN(x)). На этом слой заканчивается — итоговый вектор передаётся как вход следующему слою в точности той же формы [n, d_model], с которой всё начиналось.
Слой 1→Слой 2→…→Слой N
— шесть шагов выше повторяются в каждом слое с собственными весами
Внимание и FFN — два принципиально разных шага
Это стоит держать в голове отдельно от деталей формул: слой чередует ровно два типа операций, и они дополняют друг друга.
Attention — смешивание между токенами
Работает по оси последовательности. Единственное место, где информация вообще может «перетечь» от одной позиции к другой. Без attention каждый токен обрабатывался бы полностью изолированно, независимо от контекста.
FFN — обработка внутри токена
Работает по оси признаков, независимо для каждой позиции. Не связывает токены между собой, зато даёт сети «место» для нелинейной переработки того, что уже собрано через attention.
От слоя к слою: как меняется представление токена
Строгого универсального правила нет, но исследования интерпретируемости показывают достаточно устойчивую общую тенденцию в том, какая информация преобладает на разной глубине сети:
Ранние слоиПреобладает локальная, лексическая информация: сам токен, соседние токены, базовые синтаксические паттерны.
Средние слоиПоявляется более широкий контекст: связи между удалёнными частями предложения, согласование, разрешение неоднозначностей.
Поздние слоиПредставление смещается к тому, что непосредственно полезно для предсказания следующего токена — более «задаче-ориентированное».
Пример: во фразе «Кот прыгнул на диван, потому что он хотел спать» вектор токена «он» на входе первого слоя содержит только информацию о самом местоимении. К одному из средних слоёв attention этого токена, скорее всего, сильно «притянется» к вектору токена «кот» (единственный подходящий по роду и смыслу референт) — вектор «он» пополнится информацией о коте, и именно это обогащённое представление дальше участвует в предсказании последующих токенов.
После последнего слоя: как рождается ответ
К выходу слоя N остаточный поток для каждой позиции — это вектор размерности d_model, вобравший вклады всех слоёв. Нас интересует вектор последней позиции последовательности — именно он должен предсказать следующий токен.
x_final = FinalLayerNorm(x_N) # ещё одна нормализация, последняя
logits = x_final · W_Uᵀ # unembedding: вектор → одно число на каждое слово словаря
probs = softmax(logits / T) # T — температура, превращает логиты в вероятности
Матрица W_U часто буквально та же матрица, что использовалась для Embedding на входе, только транспонированная (tied weights) — вход и выход «говорят на одном языке» векторных представлений слов.
Результат — вектор длиной со весь словарь (десятки-сотни тысяч чисел), где каждое число после softmax — вероятность того, что именно это слово станет следующим. Дальше нужна стратегия, которая превратит это распределение вероятностей в один конкретный токен:
Стратегия
Как выбирает токен
Greedy
Берёт токен с максимальной вероятностью. Детерминированно, но склонно к повторам и предсказуемым ответам.
Temperature
Делит логиты на T перед softmax: T<1 — увереннее и предсказуемее, T>1 — более случайный, разнообразный выбор.
Top-k
Оставляет только k токенов с наибольшей вероятностью, сэмплирует среди них — отсекает длинный «хвост» маловероятных слов.
Top-p (nucleus)
Оставляет наименьший набор токенов, суммарная вероятность которых превышает p (например, 0.9) — набор адаптивно меняется в размере от шага к шагу.
Выбранный токен дописывается в конец последовательности — и весь путь, описанный выше, повторяется заново от начала для предсказания следующего токена, только теперь последовательность на один токен длиннее. Так, шаг за шагом, генерируется весь ответ — вплоть до специального токена конца последовательности или достижения лимита длины.
Два примера целиком: от запроса до ответа слой за слоем
Ниже — два намеренно упрощённых, иллюстративных прохода через сеть. Это не точная трассировка конкретной модели (заглянуть внутрь и увидеть буквально это — отдельная большая задача интерпретируемости), а наглядная схема того, какого рода информация в целом накапливается в остаточном потоке слой за слоем, пока на выходе не появится ответ.
Запрос: «Самое популярное имя кошки в этом году?»
Слой 1–2Токены получают базовые лексические векторы. Attention связывает соседние слова в устойчивые группы: «в» + «этом» + «году» склеиваются в единое временнóе выражение, «имя» + «кошки» — в понятие «кличка животного».
Слой 3–8Вектор последней позиции (токен «?») через attention подтягивает смысл всего вопроса целиком: категория ответа — имя кошки, критерий — «самое популярное» (ранжирование по частоте), ограничение — «в этом году».
Слой 9–20FFN-слои активируют связанные с именами кошек и рейтингами кличек знания, усвоенные при обучении. Вектор последней позиции всё сильнее «смещается» в область пространства, близкую к конкретным именам-кандидатам.
Слой 21–NПредставление сужается до формата ответа: одно слово, имя собственное. Итоговый вектор последней позиции почти полностью определяет, каким будет распределение вероятностей по словарю.
Кандидат на следующий токен
Вероятность (иллюстративно)
Барсик
0.41 ← выбран сэмплированием
Мурзик
0.18
Кекс
0.09
… остальной словарь
0.32
Ответ модели: «Барсик» — вероятно, самое популярное имя для кошек в этом году.
Важная оговорка: у модели нет доступа к реальной статистике имён кошек за текущий год — она выбирает наиболее вероятное по обучающим данным слово, а не проверенный факт. Токен «Барсик» дописывается в последовательность, и для генерации следующего токена (например, точки или конца ответа) весь путь — embedding → N слоёв → unembedding → softmax → сэмплирование — повторяется заново.
Запрос: «Напиши на питоне как записать слово в file.txt»
Слой 1–2Базовый лексический уровень: токены «питоне», «записать», «слово», «file.txt» получают начальные векторы. Attention склеивает «file» и «.txt» в единое имя файла, «на питоне» — в указание языка программирования.
Слой 3–8Собирается смысл задачи целиком: формат ответа — программный код, язык — Python, действие — запись в файл, цель — файл file.txt, содержимое — «слово».
Слой 9–20FFN-слои активируют знания синтаксиса Python, усвоенные при обучении на коде: конструкция open(), менеджер контекста with, метод .write() как стандартный способ записи в файл.
Слой 21–NПредставление сужается к конкретному первому токену кода. Дальше ответ строится не одним предсказанием, а последовательностью шагов — каждый новый токен кода снова требует полного прохода через всю сеть.
Шаг генерации
Сгенерированный токен
1
with
2
open(
3
'file.txt'
4
, 'w'
5
) as f:
6
\n f.write(
7
'слово'
8
)
with open('file.txt', 'w') as f:
f.write('слово')
На каждом из восьми шагов таблицы выше повторяется абсолютно весь путь, разобранный в этом документе: обновлённая (на один токен длиннее) последовательность заново проходит embedding → N слоёв декодера → финальную норму → unembedding → softmax → сэмплирование. Именно поэтому генерация длинного ответа — это не один проход сети, а столько проходов, сколько токенов в ответе.
Итоги: что нужно держать в голове
Вся сложность трансформера сводится к одной простой картине, повторённой N раз подряд с разными весами.
Токен → вектор. Embedding плюс информация о позиции превращают индекс токена в точку в многомерном пространстве.
Остаточный поток не заменяется, а накапливается. Каждый слой добавляет свой вклад поверх предыдущего состояния, ничего не стирая.
Внутри слоя ровно два типа работы. Attention смешивает информацию между позициями (единственное место, где токены «видят» друг друга), FFN независимо перерабатывает каждую позицию.
Причинная маска не даёт токену подсматривать в будущее — иначе предсказание следующего слова было бы тривиальным списыванием.
Ответ рождается только на последнем шаге: финальная нормализация → проекция в пространство словаря → softmax → сэмплирование одного токена → и весь проход повторяется заново для следующего.