Эпоха 4 · Архитектуры и масштаб · 2017

32 Transformer ★

Attention Is All You Need · Vaswani, Shazeer, Parmar и др. (8 авторов, равный вклад) · Google · NeurIPS
🟥 читать целиком~3–4 ч (вдумчиво)оригинал ↗
Суть за 20 секунд. Архитектура целиком на внимании — без рекуррентности и свёрток. Каждый токен «смотрит» на все остальные через self-attention за один шаг. Полностью параллелится по позициям → отлично масштабируется. Это фундамент почти всех современных моделей: GPT, BERT, Claude, ViT.

Контекст

К 2017-му лучшие модели последовательностей — рекуррентные (LSTM, #11) с механизмом внимания (#22). Рекуррентность — узкое место: вычисления строго последовательны по времени (токен t ждёт t−1), поэтому плохо параллелятся, медленны на длинных входах, а длинные зависимости всё равно даются тяжело. Авторы задают дерзкий вопрос: а нужна ли рекуррентность вообще, если внимание и так связывает любые две позиции напрямую?

Идея и механизм

Каждый токен проецируется в три вектора: Query (что я ищу), Key (чем я являюсь), Value (что я несу). Внимание — это извлечение values, взвешенное по совпадению query с keys:

Attention(Q, K, V) = softmax(Q K⊤√ dk ) V

Матрица QK⊤ даёт оценку «насколько токен i релевантен токену j»; softmax превращает оценки в веса; умножение на V собирает взвешенную смесь значений. Каждый токен агрегирует информацию со всех остальных за один шаг.

Multi-head. Вместо одного внимания — несколько параллельных «голов», каждая со своими проекциями; одна ловит синтаксис, другая кореференцию и т.д. Результаты конкатенируются:

MultiHead = Concat(head1, …, headh) WO,   headi = Attention(Q WiQ, K WiK, V WiV)

Positional encodings. Внимание перестановочно-инвариантно (не знает порядка), поэтому к эмбеддингам добавляют позиционные сигналы (синусоиды разных частот). Блок трансформера: внимание → residual + LayerNorm → позиционный FFN → residual + LayerNorm. Оригинал — encoder-decoder (для перевода); позже разделились decoder-only (GPT) и encoder-only (BERT).

теория вероятностей Почему делим на √dk

Пусть компоненты Q и K — независимые случайные величины со средним 0 и дисперсией 1 (примерно так после нормализации). Оценка внимания — это скалярное произведение query и key размерности dk:

s = q·k = Σi=1dk qi ki

Среднее такой суммы — ноль, а дисперсия складывается из dk независимых слагаемых, у каждого из которых Var(qi ki) = E[qi2]·E[ki2] = 1:

E[s] = 0,    Var(s) = dk  ⟹   станд. отклонение ≈ √ dk 

То есть с ростом размерности оценки разрастаются как √ dk . Большие по модулю логиты загоняют softmax в «насыщение» — он становится почти one-hot, и его градиент в неактивных позициях обнуляется (обучение глохнет). Деление на √ dk  возвращает дисперсию оценок к 1, удерживая softmax в чувствительной зоне с живыми градиентами. Маленькая константа — но без неё глубокие трансформеры обучались бы плохо.

PyTorch Реализация: self-attention в ~12 строк
import torch
import torch.nn.functional as F

def attention(Q, K, V):
    # Q, K, V: (..., seq, d_k)
    d_k = Q.size(-1)
    scores  = Q @ K.transpose(-2, -1) / d_k ** 0.5   # оценки QKᵀ / √d_k
    weights = F.softmax(scores, dim=-1)              # строки суммируются в 1
    return weights @ V                               # взвешенная сумма values

def multi_head(x, Wq, Wk, Wv, Wo, h):
    Q, K, V = x @ Wq, x @ Wk, x @ Wv                 # проекции (seq, d_model)
    split = lambda t: t.view(t.size(0), h, -1).transpose(0, 1)
    out = attention(split(Q), split(K), split(V))    # h голов параллельно
    out = out.transpose(0, 1).reshape(x.size(0), -1)
    return out @ Wo                                  # склейка голов
токеныэмбеддинги Q K V QKᵀ / √dₖоценки softmax × Vвзвеш. сумма выход
Self-attention: из токенов получаем Q, K, V; оценки QKᵀ/√dₖ → softmax (веса) → взвешенная сумма значений V. Несколько таких «голов» работают параллельно.
Аналогия. Внимание — это мягкий поиск по базе данных. Каждый токен формирует запрос (query), сравнивает его с ключами (keys) всех записей и получает не одну запись, а взвешенную по релевантности смесь их значений (values). Никаких «строго по индексу» — это поиск «по смыслу», где близкие по содержанию записи вносят больший вклад.

Почему это важно

Transformer — фундаментальная архитектура почти всего современного ИИ: GPT, BERT, Claude, Vision Transformer (#39), мультимодальные модели. Главное инженерное свойство — параллелизуемость по позициям: нет рекуррентной зависимости, поэтому модель отлично ложится на GPU/TPU и масштабируется на длинные контексты и огромные данные. Связка «Transformer + scaling laws (#37)» и есть вся LLM-революция.

Цена — внимание стоит O(N2) по длине последовательности (каждый-с-каждым), что позже будут чинить FlashAttention (#48) и эффективные варианты.

Связи

← опирается на22. Attention (Багданау)

Внимание изобрели не здесь — Багданау (2014) добавил его в seq2seq как способ декодеру «смотреть» на нужные слова источника. Transformer довёл идею до предела: убрал рекуррентность вовсе и сделал self-attention единственным механизмом связи токенов. Self-attention = внимание последовательности самой на себя.

↔ заменяет11. LSTM

LSTM связывал далёкие элементы через рекуррентное состояние — последовательно и с трудом на больших дистанциях. Transformer связывает любые две позиции напрямую за один шаг и параллельно. Тот же класс задач (последовательности), но снято главное ограничение рекуррентности — скорость и длинные зависимости.

→ ведёт к34. BERT · 38. GPT-3

Две великие ветви растут прямо отсюда: encoder-only BERT (двунаправленное понимание) и decoder-only GPT (авторегрессионная генерация). Вся современная линейка LLM — это масштабированный Transformer плюс рецепты предобучения и выравнивания.

↔ чинится в48. FlashAttention

Квадратичная стоимость внимания O(N2) и его аппетит к памяти — узкое место длинных контекстов. FlashAttention пересчитывает то же самое внимание, но без материализации полной матрицы N×N, делая длинные контексты практичными.

Вопросы пытливого ума

Если attention уже связывает все токены, зачем нужны FFN и много слоёв — разве одного внимания не хватит?

Внимание только перемешивает и маршрутизирует информацию между позициями — по сути берёт линейные комбинации values. Вся пер-токенная нелинейная обработка и бо́льшая часть ёмкости модели сидят в FFN (двухслойный MLP, применяемый к каждому токену отдельно).

А стопка слоёв строит иерархию: ранние слои ловят локальные/синтаксические связи, поздние — абстрактные. Один attention-слой без FFN беспомощен: ему нечем нелинейно преобразовать содержимое, только усреднять чужое.

O(N²) — это фундаментально, или внимание можно сделать дешевле?

Полное внимание сравнивает каждую пару токенов — отсюда N². Но это не священно: есть линейные и разреженные приближения (Linformer, Performer, sparse attention), жертвующие точностью ради скорости.

FlashAttention (#48) идёт другим путём — оставляет внимание точным, но убирает квадратичную память через тайлинг. Для честного полного внимания N² по вычислениям неизбежно; вопрос лишь, готов ли ты на аппроксимацию.

Откуда модель знает порядок слов, если attention перестановочно-инвариантно?

Сама по себе — не знает; без позиционных сигналов трансформер видит «мешок токенов». Порядок инъецируют positional encodings (синусоиды в оригинале).

Это оказалось тонким местом: как кодировать позицию, влияет на обобщение на длинные последовательности — отсюда эволюция к обучаемым и особенно к rotary-эмбеддингам (RoPE), которые кодируют относительные позиции и лучше тянутся на длинный контекст.

Почему несколько голов лучше одной большой той же суммарной размерности?

Разные головы одновременно смотрят в разные подпространства и ловят разные типы связей: одна — согласование, другая — кореференцию, третья — позиционные паттерны. Одна большая голова усредняла бы всё в единый механизм сходства.

Эмпирически головы специализируются — хотя часть из них потом можно безболезненно отрезать (не все одинаково полезны), что породило отдельное направление по прунингу голов.

Что читать в оригинале

Читать целиком и перечитать секции про (multi-head) attention и positional encoding — это важнейший разбор во всём каноне. Сильно помогает один раз реализовать attention руками: это буквально несколько матричных операций (проекции → QKᵀ → масштаб → softmax → ×V).