EssayAI
Блог
Блог
Математика и алгоритмы

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

19 июня 2026Время чтения: 7 минут
#self-attention#трансформер#нейронные сети#позиционное кодирование#контекст
Self-attention механизм: как токен смотрит на контекст

Self-attention механизм - это слой, в котором каждый токен последовательности пересобирает свой вектор, «глядя» на все остальные токены той же последовательности и решая, насколько каждый из них для него важен. Именно self-attention заменил рекуррентные связи в трансформере: вместо того чтобы пропускать информацию по цепочке слово за словом, слой сразу связывает любую пару позиций напрямую. Ниже разберём, чем self-attention отличается от обычного внимания, почему все три матрицы здесь происходят из одного входа и зачем такому слою отдельно подавать порядок слов. Чтобы сразу почувствовать, как меняется фокус токена при разных параметрах, соберите запрос в инструменте.

Что такое self-attention

Слово «self» (само-) в названии означает источник матриц. В общем механизме внимания attention запросы QQ, ключи KK и значения VV могут приходить из разных мест. В self-attention они выводятся из одного и того же входа XX линейными проекциями:

Q=XWQ,K=XWK,V=XWV,Q = X W_Q, \quad K = X W_K, \quad V = X W_V,

где XRn×dmodelX \in \mathbb{R}^{n \times d_{\text{model}}} - матрица входных эмбеддингов (nn токенов, каждый размерности dmodeld_{\text{model}}), а WQ,WK,WVW_Q, W_K, W_V - обучаемые матрицы весов. Дальше всё считается по стандартной формуле scaled dot-product:

SelfAttention(X)=softmax ⁣(QKdk)V\text{SelfAttention}(X) = \text{softmax}\!\left(\frac{QK^{\top}}{\sqrt{d_k}}\right)V

Ключевая мысль: один и тот же токен играет три роли одновременно. Как запрос он спрашивает «что мне важно в этом предложении?», как ключ он отвечает на запросы других токенов, как значение он отдаёт свой смысловой вклад в их новые векторы. Поэтому self-attention называют механизмом контекстуализации: на выходе вектор слова «банк» в фразе про реку и в фразе про деньги будет разным, хотя на входе эмбеддинг был один.

Схема self-attention: из одного входа X три проекции дают запрос, ключ и значение, токен взвешивает контекст
Схема self-attention: из одного входа X три проекции дают запрос, ключ и значение, токен взвешивает контекст

Почему Q, K и V из одного входа

В этом и состоит вся идея. Если бы QQ и KK приходили из разных последовательностей, мы бы получили cross-attention - слой, через который декодер «читает» энкодер. А self-attention решает другую задачу: обогатить каждый элемент входа информацией о его собственном окружении. Поэтому источник один.

Разделение на три проекции при общем входе - не избыточность, а способ дать модели гибкость. Матрица WQW_Q учит, что искать, матрица WKW_K - по какому признаку токен находится, матрица WVW_V - что именно передать. Если убрать это разделение и считать веса прямо по эмбеддингам (как XXX X^{\top}), внимание станет симметричным: «кот» будет смотреть на «спал» ровно настолько, насколько «спал» смотрит на «кот». А язык асимметричен - у глагола и его подлежащего разные роли. Разные WQW_Q и WKW_K ломают эту симметрию.

Self-attention против рекуррентных сетей

До трансформера контекст собирали рекуррентные сети (RNN, LSTM): скрытое состояние передавалось от токена к токену. У этого подхода два врождённых ограничения, которые self-attention снимает.

  • Длина пути. В RNN, чтобы связать первое и сотое слово, сигнал проходит 99 шагов и по дороге размывается. В self-attention путь между любой парой позиций равен единице - слой связывает их одним скалярным произведением. Поэтому self-attention лучше ловит дальние зависимости.
  • Параллелизм. RNN обязана обрабатывать токены по порядку: сотый зависит от девяносто девятого. Self-attention вычисляет все позиции одновременно - это перемножение матриц, которое идеально ложится на GPU. Отсюда и взрывной рост скорости обучения трансформеров.

Платой за это становится квадратичная сложность, о которой ниже.

Зачем нужно позиционное кодирование

У self-attention есть неочевидное свойство: он инвариантен к перестановке токенов. Слой считает скалярные произведения всех пар, но в самой формуле нет ни одного слагаемого, которое зависело бы от того, где стоит токен. Переставьте слова во входе - выходные векторы просто переставятся так же, а их содержимое не изменится. Для модели «собака укусила человека» и «человека укусила собака» без дополнительной информации выглядят одинаково.

Поэтому в трансформере к эмбеддингам перед self-attention добавляют позиционное кодирование - вектор, кодирующий номер позиции:

X~=X+P,P(pos,2i)=sin ⁣(pos100002i/dmodel)\tilde{X} = X + P, \quad P_{(pos,\,2i)} = \sin\!\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right)

Синусоидальные кодировки из оригинальной статьи или обучаемые RoPE-вращения - оба способа возвращают слою чувство порядка. Без них self-attention остаётся «мешком слов», и пропуск этого шага - одна из классических ошибок реализации с нуля.

Без позиционного кодирования self-attention видит мешок слов, с кодированием порядок восстановлен
Без позиционного кодирования self-attention видит мешок слов, с кодированием порядок восстановлен

Матрица внимания и её чтение

Сердце слоя - квадратная матрица весов αRn×n\alpha \in \mathbb{R}^{n \times n} после softmax. Элемент αij\alpha_{ij} - это доля внимания, которую токен ii отдаёт токену jj; каждая строка суммируется в единицу:

αij=exp ⁣(qikj/dk)jexp ⁣(qikj/dk)\alpha_{ij} = \frac{\exp\!\left(\mathbf{q}_i \cdot \mathbf{k}_j / \sqrt{d_k}\right)}{\sum_{j'} \exp\!\left(\mathbf{q}_i \cdot \mathbf{k}_{j'} / \sqrt{d_k}\right)}

Читать её надо построчно: строка ii показывает, на что смотрит токен ii. Диагональ обычно весомая (токен учитывает сам себя), но интересны именно внедиагональные пики - они и есть выученные связи: местоимение тянется к своему антецеденту, прилагательное к существительному. В декодерах GPT поверх этой матрицы накладывают каузальную маску: позиции справа от текущей зануляются через -\infty, чтобы токен не подсматривал будущее.

Квадратичная сложность

За универсальность связей self-attention платит памятью. Матрица QKQK^{\top} имеет размер n×nn \times n, поэтому и время, и память растут как O(n2d)O(n^2 \cdot d) по длине последовательности nn. Удвоили контекст - учетверили затраты. Именно этот квадрат ограничивает длину окна больших языковых моделей и породил целое семейство приближений: разреженное внимание, линейное внимание, FlashAttention (та же математика, но без материализации полной матрицы в памяти). Понимание этого квадрата важно на собеседованиях и в курсовых по архитектурам - самостоятельно близкий разбор есть в материале про архитектуру CNN, где сложность, наоборот, линейна по числу пикселей.

Частые ошибки

  • Путать self-attention и cross-attention. Если QQ, KK, VV из одного входа - это self-attention; если запросы из одной последовательности, а ключи и значения из другой - cross-attention. Это разные слои с разной ролью.
  • Забыть позиционное кодирование. Без него слой инвариантен к перестановке и теряет порядок слов. Добавлять PP нужно до первого self-attention блока.
  • Считать внимание симметричным. αijαji\alpha_{ij} \ne \alpha_{ji} в общем случае, потому что WQWKW_Q \ne W_K. Симметрия появилась бы только при общей проекции.
  • Игнорировать масштаб 1/dk1/\sqrt{d_k}. При больших dkd_k скалярные произведения раздуваются, softmax вырождается в почти one-hot, и градиент перестаёт течь через большинство позиций.
  • Недооценивать память. Длинный контекст в наивной реализации упирается в O(n2)O(n^2) по памяти задолго до того, как кончится точность.

FAQ

Чем self-attention отличается от обычного attention? Self-attention - это частный случай attention, где запросы, ключи и значения выводятся из одной и той же последовательности. Обычный (cross) attention допускает разные источники для QQ и для K,VK, V. Механика подсчёта весов в обоих случаях одинакова - отличается только, откуда берутся матрицы.

Почему self-attention заменил рекуррентные сети? Из-за двух вещей: путь между любой парой токенов в self-attention равен единице (RNN передаёт сигнал по цепочке и теряет дальние связи), и все позиции считаются параллельно, а не по порядку. Это даёт лучшее качество на длинных зависимостях и кратно быстрее учится на GPU.

Можно ли применять self-attention без позиционного кодирования? Технически слой запустится, но будет инвариантен к перестановке слов и не различит порядок. Поэтому на практике перед self-attention всегда добавляют позиционные эмбеддинги (синусоидальные, обучаемые или RoPE).

Коротко

Self-attention механизм выводит запросы, ключи и значения из одного входа XX и для каждого токена строит взвешенную сумму контекста по формуле softmax(QK/dk)V\text{softmax}(QK^{\top}/\sqrt{d_k})V. Один источник трёх матриц делает слой инструментом контекстуализации: вектор слова перестраивается под смысл соседей. Прямой путь между любыми позициями и полный параллелизм позволили self-attention вытеснить рекуррентные сети, а цена за это - отдельное позиционное кодирование и квадратичная по длине сложность O(n2)O(n^2).

Доверьте текст нейросети EssayAI

Открыть EssayAI

Бесплатно, на русском языке и без VPN

Читайте также