Трансформер это архитектура почти всех современных языковых, а всё чаще и визуальных моделей. На собесе почти гарантированно попросят объяснить attention, иногда написать его и рассказать, почему длинный контекст дорог.
Scaled dot-product attention
Каждый токен через обученные матрицы превращается в запрос q, ключ k и значение v.
Attention(Q, K, V) = softmax(QKᵀ / √d_k) · V
- QKᵀ даёт попарные похожести запросов и ключей.
- Деление на √d_k: дисперсия скалярного произведения растёт как d_k, без масштаба softmax насыщается и градиенты исчезают.
- softmax по ключам даёт веса, сумма 1.
- Выход токена это взвешенная сумма значений.
import math
def attention(Q, K, V):
d = len(Q[0])
out = []
for q in Q:
s = [sum(a * b for a, b in zip(q, k)) / math.sqrt(d) for k in K]
m = max(s)
w = [math.exp(x - m) for x in s]
z = sum(w)
out.append([sum(w[j] / z * V[j][c] for j in range(len(V))) for c in range(len(V[0]))])
return out
Маски
- Причинная маска в декодере: позиция i видит только j ≤ i, иначе модель подсмотрит следующий токен. Реализуется как -∞ в логитах над диагональю.
- Маска паддинга: не смотреть на пустые позиции.
Multi-head
d_model делится на h голов, у каждой свои проекции и своё распределение внимания. Головы ловят разные связи (синтаксис, кореференцию, позиции), стоимость примерно как у одной большой головы.
Позиции
Attention инвариантен к перестановке токенов. Порядок добавляют:
- синусоидальными или обучаемыми векторами (исходный трансформер, BERT);
- RoPE: поворот q и k на угол, зависящий от позиции, стандарт современных LLM;
- ALiBi: штраф к логитам по расстоянию.
Блок трансформера
x = x + Attention(Norm(x))
x = x + MLP(Norm(x))
Это pre-norm вариант, обычный для LLM. MLP расширяет размерность примерно в 4 раза или использует SwiGLU.
Три семейства
- Encoder-only (BERT): двунаправленный attention, обучение на восстановлении замаскированных токенов. Классификация, NER, эмбеддинги.
- Decoder-only (GPT-подобные): причинная маска, предсказание следующего токена. Генерация, современные LLM.
- Encoder-decoder (T5): «текст в текст», cross-attention декодера к кодировщику.
Сложность и длинный контекст
Attention стоит O(n²·d) по времени и O(n²) по памяти на матрицу внимания: удвоение контекста учетверяет её.
- FlashAttention считает attention блоками в быстрой памяти и не хранит матрицу n×n: то же значение, гораздо быстрее и с линейной памятью.
- Скользящее окно, разреженный и линейный attention, модели пространства состояний.
- Для длинного контекста масштабируют RoPE и дообучают на длинных текстах.
KV-кеш
На генерации ключи и значения прошлых токенов сохраняются, и на каждом шаге считается только новый токен. Память кеша растёт с длиной контекста, числом слоёв и батчем. Её уменьшают multi-query и grouped-query attention (меньше голов K и V), квантование кеша и PagedAttention.
Частые ошибки
- Забыть масштаб √d_k или причинную маску.
- Путать роли K и V.
- Применять маску после softmax, обнуляя веса: тогда сумма весов уже не 1.
- Считать, что FlashAttention меняет результат или асимптотику вычислений.
Что спросят на собесе
- Формула attention, зачем Q, K, V и деление на √d_k.
- Зачем несколько голов, маски и позиционное кодирование.
- BERT против GPT.
- Сложность attention, как живут с длинным контекстом, что такое KV-кеш и GQA.