Это четвёртая статья серии «AI без магии». В первой разобрали, откуда вообще взялись LLM. Во второй — как модель «думает» по одному токену и почему галлюцинирует. В третьей — как текст превращается в векторы. Теперь разберём механизм, который сделал трансформер тем, чем он стал: attention.

Зачем токенам вообще разговаривать

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

Классический пример из 3Blue1Brown: возьмите слово «mole». В фразе «American shrew mole» это вид крота. В «one mole of carbon dioxide» — единица количества вещества. В «take a biopsy of the mole» — родинка. Три совершенно разных смысла, а статический эмбеддинг — один.

Или пример попроще: слово «банк». «Сбербанк» — финансовая организация. «На банке реки» — берег. «Открыл стеклянную банку» — ёмкость. Эмбеддинг-слой, который мы построили в прошлой статье, выдаст один и тот же вектор во всех трёх случаях.

Чтобы понять, какое из значений сейчас в игре, токену нужно посмотреть на соседей. «Стеклянную» рядом — значит ёмкость. «Реки» — значит берег. «Сбер» — финансы.

Механизм, который позволяет токенам «смотреть друг на друга» и обновлять свои векторы с учётом контекста, называется attention. И в этой статье мы разберём, как он устроен.

Soft search: общая идея

В первой статье мы уже коротко упоминали attention — как «soft search». Идея пришла из машинного перевода в 2015 году: вместо того чтобы один токен жёстко выбирал один связанный с ним токен из контекста, давайте получать смесь всех с разными весами.

Это похоже на поиск в библиотечном каталоге, только мягкий. Вы приходите с запросом — например, «что-то про нейросети». Каталог не выдаёт вам одну книгу. Он выдаёт смесь: 60% — книга про машинное обучение, 30% — про оптимизацию, 10% — про статистику. Чем лучше книга совпадает с запросом, тем больше её доля в смеси.

В attention происходит ровно это. Каждый токен задаёт запрос. Все остальные токены показывают свои ключи. Мы считаем, насколько запрос совпадает с каждым ключом, нормализуем эти совпадения в проценты, и собираем взвешенную сумму значений этих токенов.

Query, Key, Value

Каждый токен в attention играет три роли:

Query (запрос): «что я ищу в контексте?» — Key (ключ): «вот что я могу предложить, если меня спросят» — Value (значение): «вот что я передам, если со мной согласятся»

Query«что я ищу?»q = x · W_qKey«что я предлагаю?»k = x · W_kValue«что я передаю?»v = x · W_vсравнитьq · kвзвеситьsoftmaxx — исходный вектор токена (после эмбеддинга)W_q, W_k, W_v — три обучаемые матрицы (одни на всю модель)для каждого токена считаем все три проекции независимоAttention(Q, K, V) = softmax( Q · Kᵀ / √d_k ) · V
Query, key и value — три разные «роли», в которых выступает каждый токен. Это просто три проекции исходного вектора через обучаемые матрицы Wq, Wk, Wv. Знаменитая формула в нижней рамке — это и есть весь scaled dot-product attention из статьи Vaswani et al. 2017.

Все три — это просто разные проекции исходного эмбеддинга через три обучаемые матрицы Wq, Wk, Wv. Эти матрицы — те самые «параметры», которые модель учит во время тренировки. Ничего больше в attention нет.

Зачем вообще нужны три разные роли? Почему нельзя просто сравнивать векторы напрямую? Потому что одно и то же слово в разных контекстах хочет разного. Слово «который» как query ищет существительное-антецедент. Как key — оно ничем особо не интересно. А как value — передаёт «здесь начинается придаточное». Три проекции позволяют модели выучить, что искать, как себя показывать и что отдавать — независимо друг от друга.

Self-attention пошагово

Слово «self» в self-attention означает, что все query, key и value берутся из одной и той же последовательности — той, которую модель сейчас обрабатывает. Не как в машинном переводе, где запросы шли от декодера, а ключи от энкодера. В GPT-моделях query, key и value — это всё разные роли тех же входных токенов.

Возьмём конкретный пример из учебника Раски — фразу «Your journey starts with one step». Шесть токенов, считаем self-attention.

Шаг 1. Для каждого токена вычисляем три вектора: q, k, v (умножая эмбеддинг на Wq, Wk, Wv).

Шаг 2. Считаем матрицу совпадений 6×6: для каждой пары (i, j) берём скалярное произведение q_i · k_j. Это attention score — насколько вопрос i-го токена «совпадает» с предложением j-го.

Шаг 3. Делим всё на √d_k (где d_k — размерность ключей). Это нужно, чтобы scores не разбегались на больших размерностях и softmax не уходил в насыщение.

Шаг 4. К каждой строке применяем softmax. Получаем attention weights — нормализованные веса, сумма которых по строке равна 1.

Шаг 5. Для каждого токена i считаем взвешенную сумму всех значений: z_i = Σ w_ij · v_j. Это и есть контекстный вектор — обновлённый эмбеддинг, в котором уже учтён контекст.

attention weights для «Your journey starts with one step»key →Yourjourneystartswithonestep↓ queryYour.42.20.10.08.08.12journey.10.38.32.08.07.05starts.08.30.36.10.08.08with.06.18.16.32.18.10one.06.10.10.20.40.14step.05.16.18.10.16.35суммапо строке= 1.0= 1.0= 1.0= 1.0= 1.0тёмная клетка = высокий вес: токен в строке «смотрит» на токен в столбцедля «journey» главные соседи — она сама и «starts»; для «one» — «step»матрица заполнена целиком: каждый токен видит все остальные(значения иллюстративные, реальные веса зависят от обученных Wq, Wk)
Матрица attention weights 6×6 для фразы «Your journey starts with one step». Каждая строка показывает, на какие токены смотрит токен из этой строки (через softmax все веса в строке суммируются в 1). Тёмные клетки — высокие веса. «journey» сильнее всего смотрит на себя и на «starts», «one» — на «step». Каждый токен видит все остальные.

Главное, что стоит понять: эта матрица полностью заполнена. Любой токен может «достать» информацию о любом другом за один проход. В RNN, чтобы передать сигнал от первого токена к десятому, нужно было пройти десять шагов рекурсии — на каждом из них сигнал затухал. В attention это происходит за одно умножение матриц.

Causal mask: почему GPT не видит будущее

В GPT-моделях есть тонкость. Когда модель учится предсказывать следующий токен, она не должна подсматривать в правильный ответ.

Представьте: мы скармливаем модели «Your journey starts with one», и она должна предсказать «step». Если бы при подсчёте attention для слова «one» модель могла видеть «step», то задача стала бы тривиальной: смотри на следующий токен и его же выдавай. Модель ничему не научится, потому что на инференсе следующего токена ещё нет — его как раз надо предсказать.

Решение — causal mask, причинная маска. Перед softmax все клетки в верхнем треугольнике матрицы attention scores заменяются на минус бесконечность. После softmax они становятся нулями: токен i может смотреть только на токены 1…i, но никогда на i+1, i+2 и так далее.

causal mask: верхний треугольник занулёнYourjourneystartswithonestepYour1.000000journey.30.700000starts.15.40.45000with.10.22.20.4800one.08.12.12.25.430step.05.16.18.10.16.35пунктир — занулённые клетки: токен не может смотреть в будущее
Causal mask. Та же матрица, но верхний треугольник занулён: «Your» (первый токен) видит только себя, «journey» — только себя и «Your», и так далее. Это критично при обучении: модель не подсматривает следующий токен, потому что иначе на инференсе она бы беспомощно сломалась.

Маска применяется только в декодер-моделях (GPT, LLaMA, Claude). В энкодерах (BERT, classification-модели) её не нужно — там задача другая, и токены спокойно смотрят и вперёд, и назад. Но GPT — авторегрессивная модель, она генерирует слева направо, поэтому маска нужна.

Multi-head: почему head несколько

Одна head attention — это одна «точка зрения». Она учится связывать токены каким-то одним способом. Но в языке связей много: подлежащее ↔ сказуемое, прилагательное ↔ существительное, местоимение ↔ антецедент, артикль ↔ слово, к которому он относится.

Если бы у нас была только одна head, модель должна была бы запихнуть все эти типы связей в одну матрицу — что почти невозможно. Решение: запустить несколько head параллельно. У каждой свои Wq, Wk, Wv, и каждая учится своему типу связей.

Хороший пример из 3Blue1Brown: фраза «The glass ball fell on the steel table, and it shattered». Чтобы понять, к чему относится «it» и что значит «shattered», нужно несколько разных attention-связей одновременно: «ball» ↔ «glass» (свойство), «table» ↔ «steel» (свойство), «shattered» ↔ что-то хрупкое. Одна head не справится — нужно несколько, каждая со своим узором весов.

несколько head — несколько узоров связейhead 1диагональ + соседиhead 2первый токен → всеhead 3далёкие связикаждая head видит свою матрицу связей; результаты конкатенируются и идут дальше
Multi-head attention. Несколько head работают параллельно, у каждой своя матрица attention weights и свои Wq, Wk, Wv. Одна может ловить связи с ближайшими соседями, другая — с первым токеном (часто это «attention sink»), третья — длинные связи через всю последовательность. У GPT-3 в каждом блоке 96 head.

В оригинальной статье «Attention Is All You Need» 2017 года было 8 head. У GPT-2 — 12 в маленькой версии, 25 в самой большой. У GPT-3 — 96 head в каждом блоке. И блоков много: 12, 24, 96 в зависимости от размера модели. Каждая комбинация (блок × head) учится своему типу связей.

После того как все head посчитали свои контекстные векторы, результаты конкатенируются в один длинный вектор и пропускаются через ещё одну линейную проекцию. Это и есть multi-head attention. На выходе — обновлённый вектор для каждого токена, в котором сжата информация со всей последовательности, увиденная под несколькими углами одновременно.

Что мы теперь знаем

Статические эмбеддинги дают слову один вектор. Но смысл слова зависит от контекста — нужно, чтобы токены «поговорили друг с другом».

Attention — это soft search. Каждый токен задаёт запрос, все остальные показывают ключи, мы получаем взвешенную смесь значений.

Q, K, V — три проекции исходного эмбеддинга через обучаемые матрицы Wq, Wk, Wv. В этом весь механизм — три матрицы, скалярное произведение, softmax.

Causal mask запрещает токену смотреть в будущее. Без неё GPT не научилась бы предсказывать следующий токен.

Multi-head — несколько head параллельно, каждая со своими Wq/Wk/Wv, каждая учится своему типу связей. У GPT-3 — 96 head в каждом блоке.

Attention превратил статические эмбеддинги в контекстные. Каждый токен теперь «знает» про окружение, и его вектор сдвинут в нужную сторону: «mole» в «shrew mole» теперь ближе к «крот», а в «one mole of CO2» — к «количество вещества».

Но attention — это только половина блока трансформера. После него идёт обычная feed-forward сеть, нормализация, residual connection. И таких блоков в стопке десятки или сотни. В следующей, последней статье серии, соберём всё вместе и посмотрим, как из этих кубиков получается работающая GPT.

По мотивам Build a Large Language Model (From Scratch) Sebastian Raschka · manning.com Открыть оригинал →
Что ещё прочитать
  • Attention Is All You Need Vaswani et al. · 2017 Та самая статья, в которой transformer впервые появился — без RNN, только attention.
  • Attention in transformers, step-by-step Grant Sanderson (3Blue1Brown) · 2024 Лучшее визуальное объяснение, что делает одна head attention. Примеры с «mole» и «Eiffel tower».
  • Causal attention, explained Sebastian Raschka · 2026 Короткий FAQ про то, почему GPT-моделям нужна маска и где именно она применяется.
  • Query, Key, Value: The Foundation of Transformer Attention Michael Brenndoerfer · 2025 Аналогия с библиотечным каталогом, которая хорошо ложится на Q/K/V.

← В архив