Self-attention механизм: как токен смотрит на контекст

Self-attention механизм - это слой, в котором каждый токен последовательности пересобирает свой вектор, «глядя» на все остальные токены той же последовательности и решая, насколько каждый из них для него важен. Именно self-attention заменил рекуррентные связи в трансформере: вместо того чтобы пропускать информацию по цепочке слово за словом, слой сразу связывает любую пару позиций напрямую. Ниже разберём, чем self-attention отличается от обычного внимания, почему все три матрицы здесь происходят из одного входа и зачем такому слою отдельно подавать порядок слов. Чтобы сразу почувствовать, как меняется фокус токена при разных параметрах, соберите запрос в инструменте.
Что такое self-attention
Слово «self» (само-) в названии означает источник матриц. В общем механизме внимания attention запросы , ключи и значения могут приходить из разных мест. В self-attention они выводятся из одного и того же входа линейными проекциями:
где - матрица входных эмбеддингов ( токенов, каждый размерности ), а - обучаемые матрицы весов. Дальше всё считается по стандартной формуле scaled dot-product:
Ключевая мысль: один и тот же токен играет три роли одновременно. Как запрос он спрашивает «что мне важно в этом предложении?», как ключ он отвечает на запросы других токенов, как значение он отдаёт свой смысловой вклад в их новые векторы. Поэтому self-attention называют механизмом контекстуализации: на выходе вектор слова «банк» в фразе про реку и в фразе про деньги будет разным, хотя на входе эмбеддинг был один.

Почему Q, K и V из одного входа
В этом и состоит вся идея. Если бы и приходили из разных последовательностей, мы бы получили cross-attention - слой, через который декодер «читает» энкодер. А self-attention решает другую задачу: обогатить каждый элемент входа информацией о его собственном окружении. Поэтому источник один.
Разделение на три проекции при общем входе - не избыточность, а способ дать модели гибкость. Матрица учит, что искать, матрица - по какому признаку токен находится, матрица - что именно передать. Если убрать это разделение и считать веса прямо по эмбеддингам (как ), внимание станет симметричным: «кот» будет смотреть на «спал» ровно настолько, насколько «спал» смотрит на «кот». А язык асимметричен - у глагола и его подлежащего разные роли. Разные и ломают эту симметрию.
Self-attention против рекуррентных сетей
До трансформера контекст собирали рекуррентные сети (RNN, LSTM): скрытое состояние передавалось от токена к токену. У этого подхода два врождённых ограничения, которые self-attention снимает.
- Длина пути. В RNN, чтобы связать первое и сотое слово, сигнал проходит 99 шагов и по дороге размывается. В self-attention путь между любой парой позиций равен единице - слой связывает их одним скалярным произведением. Поэтому self-attention лучше ловит дальние зависимости.
- Параллелизм. RNN обязана обрабатывать токены по порядку: сотый зависит от девяносто девятого. Self-attention вычисляет все позиции одновременно - это перемножение матриц, которое идеально ложится на GPU. Отсюда и взрывной рост скорости обучения трансформеров.
Платой за это становится квадратичная сложность, о которой ниже.
Зачем нужно позиционное кодирование
У self-attention есть неочевидное свойство: он инвариантен к перестановке токенов. Слой считает скалярные произведения всех пар, но в самой формуле нет ни одного слагаемого, которое зависело бы от того, где стоит токен. Переставьте слова во входе - выходные векторы просто переставятся так же, а их содержимое не изменится. Для модели «собака укусила человека» и «человека укусила собака» без дополнительной информации выглядят одинаково.
Поэтому в трансформере к эмбеддингам перед self-attention добавляют позиционное кодирование - вектор, кодирующий номер позиции:
Синусоидальные кодировки из оригинальной статьи или обучаемые RoPE-вращения - оба способа возвращают слою чувство порядка. Без них self-attention остаётся «мешком слов», и пропуск этого шага - одна из классических ошибок реализации с нуля.

Матрица внимания и её чтение
Сердце слоя - квадратная матрица весов после softmax. Элемент - это доля внимания, которую токен отдаёт токену ; каждая строка суммируется в единицу:
Читать её надо построчно: строка показывает, на что смотрит токен . Диагональ обычно весомая (токен учитывает сам себя), но интересны именно внедиагональные пики - они и есть выученные связи: местоимение тянется к своему антецеденту, прилагательное к существительному. В декодерах GPT поверх этой матрицы накладывают каузальную маску: позиции справа от текущей зануляются через , чтобы токен не подсматривал будущее.
Квадратичная сложность
За универсальность связей self-attention платит памятью. Матрица имеет размер , поэтому и время, и память растут как по длине последовательности . Удвоили контекст - учетверили затраты. Именно этот квадрат ограничивает длину окна больших языковых моделей и породил целое семейство приближений: разреженное внимание, линейное внимание, FlashAttention (та же математика, но без материализации полной матрицы в памяти). Понимание этого квадрата важно на собеседованиях и в курсовых по архитектурам - самостоятельно близкий разбор есть в материале про архитектуру CNN, где сложность, наоборот, линейна по числу пикселей.
Частые ошибки
- Путать self-attention и cross-attention. Если , , из одного входа - это self-attention; если запросы из одной последовательности, а ключи и значения из другой - cross-attention. Это разные слои с разной ролью.
- Забыть позиционное кодирование. Без него слой инвариантен к перестановке и теряет порядок слов. Добавлять нужно до первого self-attention блока.
- Считать внимание симметричным. в общем случае, потому что . Симметрия появилась бы только при общей проекции.
- Игнорировать масштаб . При больших скалярные произведения раздуваются, softmax вырождается в почти one-hot, и градиент перестаёт течь через большинство позиций.
- Недооценивать память. Длинный контекст в наивной реализации упирается в по памяти задолго до того, как кончится точность.
FAQ
Чем self-attention отличается от обычного attention? Self-attention - это частный случай attention, где запросы, ключи и значения выводятся из одной и той же последовательности. Обычный (cross) attention допускает разные источники для и для . Механика подсчёта весов в обоих случаях одинакова - отличается только, откуда берутся матрицы.
Почему self-attention заменил рекуррентные сети? Из-за двух вещей: путь между любой парой токенов в self-attention равен единице (RNN передаёт сигнал по цепочке и теряет дальние связи), и все позиции считаются параллельно, а не по порядку. Это даёт лучшее качество на длинных зависимостях и кратно быстрее учится на GPU.
Можно ли применять self-attention без позиционного кодирования? Технически слой запустится, но будет инвариантен к перестановке слов и не различит порядок. Поэтому на практике перед self-attention всегда добавляют позиционные эмбеддинги (синусоидальные, обучаемые или RoPE).
Коротко
Self-attention механизм выводит запросы, ключи и значения из одного входа и для каждого токена строит взвешенную сумму контекста по формуле . Один источник трёх матриц делает слой инструментом контекстуализации: вектор слова перестраивается под смысл соседей. Прямой путь между любыми позициями и полный параллелизм позволили self-attention вытеснить рекуррентные сети, а цена за это - отдельное позиционное кодирование и квадратичная по длине сложность .
Читайте также

Архитектура трансформер: из чего собран Transformer
Архитектура трансформер по блокам: энкодер и декодер, multi-head self-attention, позиционное кодирование, FFN, residual и LayerNorm. Разбираем, как данные проходят сквозь сеть и где ошибаются.

Механизм внимания attention: формула и примеры
Механизм внимания attention: как работает scaled dot-product attention, формула softmax(QK^T/sqrt(d_k))V, матрица весов, multi-head и где чаще всего ошибаются.

Алгоритм обратного распространения ошибки: как учится сеть
Backpropagation простыми словами: как обратное распространение ошибки считает градиенты по цепному правилу, обновляет веса нейросети и при чём тут исчезающий градиент. С формулами и разбором.