Multi-head attention: зачем несколько голов (запускаемый пример)

Одного механизма внимания мало: трансформер запускает несколько «голов» параллельно. Разберём, зачем, и увидим, как две головы смотрят в разные стороны.

Multi-head attention — несколько параллельных механизмов внимания со своими матрицами Q/K/V; их результаты объединяются, чтобы модель улавливала разные типы связей одновременно.

Проблема одной головы

Одно внимание выдаёт для каждого токена одно распределение весов. Но между словами существует много разных связей сразу: синтаксическая (кто к чему относится), смысловая (что чему близко), кореферентная (какое местоимение к кому). Заставлять одну голову совмещать всё это — всё равно что одним взглядом одновременно следить за грамматикой, темой и стилем. Что-то обязательно потеряется.

Решение: много голов параллельно

Поэтому внимание запускают несколько раз параллельно, и у каждой «головы» свои матрицы W_Q, W_K, W_V. Значит, каждая голова строит свои query/key/value и формирует свою картину внимания. Одна может специализироваться на «к какому существительному относится прилагательное», другая — на «какое слово продолжает мысль». Покажем на двух головах, как они расходятся.

import math

tokens = ["банк", "у", "реки"]

def softmax(xs):
    m = max(xs); e = [math.exp(x - m) for x in xs]; s = sum(e)
    return [v / s for v in e]
def dot(a, b): return sum(x*y for x, y in zip(a, b))

# Две "головы" внимания используют РАЗНЫЕ проекции -> разные Q и K,
# поэтому фокусируются на разных токенах. Здесь зададим их вручную.
Q1 = [[3.0, 0.0]]                              # запрос токена "банк" в голове 1
K1 = [[3.0, 0.0], [0.0, 1.0], [0.0, 1.0]]      # голова 1: "банк" тянется к себе
Q2 = [[0.0, 3.0]]                              # запрос токена "банк" в голове 2
K2 = [[0.0, 0.0], [0.0, 1.0], [0.0, 3.0]]      # голова 2: "банк" тянется к "реки"

def head_weights(Q, K):
    scores = [dot(Q[0], K[j]) / math.sqrt(2) for j in range(len(tokens))]
    return softmax(scores)

w1 = head_weights(Q1, K1)
w2 = head_weights(Q2, K2)
print("Токен 'банк', куда смотрит каждая голова:")
print("  голова 1:", ", ".join(f"{tokens[j]}={w1[j]:.2f}" for j in range(len(tokens))))
print("  голова 2:", ", ".join(f"{tokens[j]}={w2[j]:.2f}" for j in range(len(tokens))))
print()
print("Разные головы фокусируются на разных токенах -> модель улавливает")
print("несколько типов связей сразу. Выходы голов затем склеиваются в один.")

Вывод:

Токен 'банк', куда смотрит каждая голова:
  голова 1: банк=1.00, у=0.00, реки=0.00
  голова 2: банк=0.00, у=0.01, реки=0.98

Разные головы фокусируются на разных токенах -> модель улавливает
несколько типов связей сразу. Выходы голов затем склеиваются в один.

Для одного и того же токена «банк» голова 1 смотрит на сам токен, а голова 2 — почти целиком на «реки». В реальной модели таких голов 8, 16 или больше, и каждая по-своему «нарезает» контекст. Это даёт модели богатый, многогранный взгляд на одну и ту же последовательность.

Как головы собираются обратно

Каждая голова работает в своём подпространстве меньшей размерности (общая размерность делится на число голов). Их выходы склеиваются (конкатенируются) в один вектор и пропускаются через ещё одну обучаемую матрицу W_O, которая смешивает информацию всех голов в итоговый результат. Схематично:

head_1 = attention(Q1, K1, V1)   # своя картина внимания
head_2 = attention(Q2, K2, V2)
...
head_h = attention(Qh, Kh, Vh)

multi = concat(head_1, head_2, ..., head_h) @ W_O

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

Что показывают исследования

Если посмотреть на реальные обученные модели, разные головы действительно специализируются: одни отслеживают согласование, другие — связь местоимения с существительным, третьи — позицию относительно знаков препинания. Это не запрограммировано — модель сама распределила «обязанности» между головами в ходе обучения.

Итог

  • Одна голова внимания даёт лишь одну картину связей — этого мало для языка.
  • Multi-head запускает несколько вниманий параллельно, у каждого свои Q/K/V.
  • Разные головы специализируются на разных типах связей (синтаксис, смысл, кореференция).
  • Выходы голов склеиваются и смешиваются матрицей W_O; стоимость почти не растёт.
Проверьте себя
1. Почему одной головы внимания недостаточно?
AОна работает слишком быстро
BМежду словами много разных связей сразу, и одна голова не может уловить их все
CОдна голова не использует softmax
DОдна голова не умеет считать веса
2. Чем головы в multi-head attention отличаются друг от друга?
AРазмером словаря
BУ каждой свои матрицы W_Q, W_K, W_V, поэтому своя картина внимания
CОни идентичны
DКаждая работает с отдельным языком
3. Как объединяются выходы нескольких голов?
AБерётся максимум
BОни конкатенируются и пропускаются через обучаемую матрицу W_O
CОни складываются и делятся на число голов
DИспользуется только первая голова
Поддержать проект