Содержание курса 16 / 37

16 / 37 · Трансформеры

Self-attention

Последовательность обращает внимание на саму себя: каждый токен строит запрос, ключ и значение тремя линейными отображениями, оценивает все токены и берёт взвешенное среднее значений. В заметке выводится матричная форма с размерами матриц, вручную разбирается пример из трёх токенов, объясняются деление на √d_k, маски и потеря порядка слов, строится многоголовое внимание и обсуждается его квадратичная стоимость.

На этой странице 16

Предыдущая заметка позволила декодеру рекуррентного переводчика оценивать каждое состояние энкодера и считывать их взвешенное среднее. Сам энкодер по-прежнему рекуррентный: nn токенов требуют nn последовательных шагов, а связь между двумя далёкими словами проходит через каждый шаг между ними. В этой заметке тот же поиск применяется внутри одной последовательности: каждая позиция оценивает каждую позицию, включая себя, и считывает взвешенное среднее по всем ним — одним матричным произведением. Эта операция называется self-attention (внимание последовательности к самой себе); в виде нескольких параллельных голов (heads) это единственная операция трансформера, которая смешивает позиции (Vaswani et al., 2017). Матричные иллюстрации того же вычисления есть у Alammar (2018) и Jurafsky and Martin.

Обозначения. Последовательность из nn токенов с dd-мерными векторами — это матрица X∈Rn×dX \in \mathbb R^{n \times d}; её строка xix_i — вектор токена ii: эмбеддинг (embedding) или выход предыдущего слоя. Векторы — строки, как в PyTorch. Обучаемые матрицы WQ,WK∈Rd×dkW_Q, W_K \in \mathbb R^{d \times d_k} и WV∈Rd×dvW_V \in \mathbb R^{d \times d_v} дают запрос (query) qi=xiWQq_i = x_i W_Q, ключ (key) ki=xiWKk_i = x_i W_K и значение (value) vi=xiWVv_i = x_i W_V. Оценки внимания (attention scores) обозначаются sijs_{ij}, веса внимания — αij\alpha_{ij}, выходы — yiy_i; hh — число голов. Softmax от матрицы берётся по строкам.

Задача: смысл слова зависит от других слов#

В предложении «Кубок не помещается в коричневый чемодан, потому что он слишком большой» он — это кубок; если заменить большой на маленький, то он — это чемодан. В этой схеме Винограда (Winograd schema) (Levesque, Davis and Morgenstern, 2012) статический эмбеддинг даёт слову он один и тот же вектор в обоих предложениях. Нам нужен слой, который переписывает вектор каждого токена с учётом связанных с ним токенов, выбранных по их содержанию и стоящих в любом месте предложения.

Базовый подход: рекуррентность и внимание поверх неё#

Рекуррентный энкодер (заметка об LSTM) смешивает контекст, передавая состояние вдоль последовательности в одном или в обоих направлениях. Внимание (attention) из предыдущей заметки стоит между декодером и энкодером; энкодер остаётся рекуррентным.

Где этот подход не справляется:

  • Последовательные вычисления. Шагу tt нужно состояние шага t−1t-1, поэтому nn шагов нельзя выполнить параллельно.
  • Длинные пути. Сигнал и градиент между позициями ii и jj проходят через ∣i−j∣|i - j| нелинейных обновлений одного состояния фиксированной ширины. Вентили (gates) делают этот путь обучаемым, но не коротким.
  • Смешивание — не сравнение. Сколько от токена jj доходит до токена ii, решают гейты по мере продвижения состояния, а не сравнение ii с jj.

Внимание как мягкий поиск по словарю#

Словарь сравнивает запрос с хранимыми ключами и возвращает значение того ключа, который совпал в точности. Внимание делает совпадение мягким: softmax превращает оценки совпадения в веса, дающие в сумме единицу, а результат — взвешенное среднее всех значений. Поскольку веса гладко зависят от оценок, все три проекции обучаются обратным распространением ошибки.

В предыдущей заметке запросом служит состояние декодера, а состояния энкодера — одновременно ключами и значениями (Bahdanau, Cho and Bengio, 2015). Self-attention берёт все три из одной и той же последовательности (Cheng, Dong and Lapata, 2016, ещё до трансформера). Cross-attention (внимание к выходам энкодера) берёт запросы из одной последовательности, а ключи и значения — из другой, с матрицей весов m×nm \times n для mm запросов и nn ключей; заметка о трансформере помещает его в декодер.

От эмбеддингов к Q, K и V#

Для одного токена ii слой делает четыре вещи (пошаговый разбор в этом же порядке — у Starmer, 2023):

  1. Проекция. qi=xiWQq_i = x_i W_Q, и для каждого токена jj: kj=xjWKk_j = x_j W_K, vj=xjWVv_j = x_j W_V. Каждая из них — линейный слой без смещения.
  2. Оценка. sij=qi⋅kj/dks_{ij} = q_i \cdot k_j / \sqrt{d_k} для каждого jj, включая j=ij = i.
  3. Нормировка. αij=exp⁡(sij)/∑l=1nexp⁡(sil)\alpha_{ij} = \exp(s_{ij}) / \sum_{l=1}^{n} \exp(s_{il}). Веса положительны и ∑jαij=1\sum_j \alpha_{ij} = 1.
  4. Смешивание. yi=∑j=1nαijvjy_i = \sum_{j=1}^{n} \alpha_{ij} v_j.

Одни и те же WQW_Q, WKW_K и WVW_V используются в каждой позиции, поэтому один слой обрабатывает последовательности любой длины. Строки YY — контекстные эмбеддинги: одно и то же слово получает разные векторы в разных предложениях; на них опираются заметка о BERT и семантический поиск.

Зачем разделять запросы и ключи#

Оценка через xi⋅xjx_i \cdot x_j не работает по двум причинам. Она симметрична, поэтому «местоимение ищет свой антецедент» и обратное отношение были бы одним и тем же. И сам токен обычно побеждает в своей строке: по неравенству Коши–Буняковского xi⋅xj≤∥xi∥∥xj∥≤∥xi∥2x_i \cdot x_j \le \lVert x_i \rVert \lVert x_j \rVert \le \lVert x_i \rVert^2, если ∥xj∥≤∥xi∥\lVert x_j \rVert \le \lVert x_i \rVert, поэтому при равных нормах диагональный элемент — наибольшая оценка в каждой строке.

С проекциями оценка равна

sij=xi WQWK⊤ xj⊤dk,s_{ij} = \frac{x_i\, W_Q W_K^\top\, x_j^\top}{\sqrt{d_k}},

то есть обучаемой билинейной форме. Она не обязана быть симметричной, а большая оценка для самого себя становится выученным выбором, а не заложенным заранее предпочтением. Ранг матрицы WQWK⊤W_Q W_K^\top не больше dkd_k: голова сравнивает токены вдоль dkd_k выученных направлений. Отдельная WVW_V разделяет то, что сопоставляется, и то, что передаётся дальше.

Почему скалярное произведение#

Предыдущая заметка сравнила аддитивную, билинейную и скалярную функции оценки (Luong, Pham and Manning, 2015). Здесь скалярное произведение делает всю матрицу оценок одним произведением QK⊤QK^\top, а нормы несут информацию: запрос с большей нормой даёт более острый softmax, ключ с большей нормой забирает больше веса. Контролировать нужно только типичную величину оценок, которая растёт с dkd_k.

Внимание на основе масштабированного скалярного произведения#

Составив из строк матрицы, получаем матричную форму внимания на основе масштабированного скалярного произведения (scaled dot-product attention):

Q=XWQ∈Rn×dk,K=XWK∈Rn×dk,V=XWV∈Rn×dv,\begin{gathered} Q = X W_Q \in \mathbb R^{n \times d_k}, \\ K = X W_K \in \mathbb R^{n \times d_k}, \\ V = X W_V \in \mathbb R^{n \times d_v}, \end{gathered} Attention⁡(Q,K,V)=softmax⁡ ⁣(QK⊤dk)V.\begin{aligned} &\operatorname{Attention}(Q, K, V) \\ &\qquad = \operatorname{softmax}\!\Big(\frac{Q K^\top}{\sqrt{d_k}}\Big) V . \end{aligned}

Элемент (i,j)(i, j) матрицы QK⊤QK^\top равен qi⋅kjq_i \cdot k_j; деление на dk\sqrt{d_k} удерживает оценки в масштабе, с которым softmax может работать (ниже); softmax даёт αij\alpha_{ij}, а строка ii произведения с VV равна yiy_i: это четыре шага выше, выполненные сразу для всех токенов.

МатрицаРазмерСмысл
XXn×dn \times dпредставления токенов
Q,KQ, Kn×dkn \times d_kзапросы, ключи
VVn×dvn \times d_vзначения
QK⊤QK^\topn×nn \times nоценки «токен — токен»
A=softmax⁡(QK⊤/dk)A = \operatorname{softmax}(QK^\top/\sqrt{d_k})n×nn \times nвеса внимания, каждая строка даёт в сумме 1
AVAVn×dvn \times d_vпо одному вектору с учётом контекста на каждый токен
Рисунок

Размеры матриц в self-attention и путь одной строки

Размеры матриц во внимании на основе масштабированного скалярного произведенияСверху: вход X, пять строк на восемь столбцов, умножается на три матрицы проекций, и получаются Q, K и V, каждая из пяти строк и четырёх столбцов. Снизу: Q, умноженная на транспонированную K, даёт матрицу оценок пять на пять, softmax превращает её в матрицу весов пять на пять, а веса, умноженные на V, дают выход Y пять на четыре. Вторая строка выделена в X, Q, оценках, весах и выходе; K и V закрашены целиком, потому что в этой строке участвуют все ключи и значения.Xn × dQn × d_kKn × d_kVn × d_vQn × d_kKᵀd_k × nS = QKᵀ/√d_kn × n, оценкиA = softmax(S)n × n, весаVn × d_vY = AVn × d_v×=×=× W_Q, × W_K, × W_Vкаждая d × d_ksoftmaxпо строкамВыделен токен 2. Его строка выхода Σⱼ α₂ⱼ vⱼ — взвешенное среднее всех строк значений.Размеры матриц во внимании на основе масштабированного скалярного произведенияСверху: вход X, пять строк на восемь столбцов, умножается на три матрицы проекций, и получаются Q, K и V, каждая из пяти строк и четырёх столбцов. Снизу: Q, умноженная на транспонированную K, даёт матрицу оценок пять на пять, softmax превращает её в матрицу весов пять на пять, а веса, умноженные на V, дают выход Y пять на четыре. Вторая строка выделена в X, Q, оценках, весах и выходе; K и V закрашены целиком, потому что в этой строке участвуют все ключи и значения.Xn × dQn × d_kKn × d_kVn × d_vQn × d_kKᵀd_k × nS = QKᵀ/√d_kn × n, оценкиA = softmax(S)n × n, весаVn × d_vY = AVn × d_v×=×=× W_Q, × W_K, × W_Vкаждая d × d_k, общая для всех строкsoftmax,по строкамВыделен токен 2; его выход y₂ = Σⱼ α₂ⱼ vⱼ.
В масштабе для n = 5 токенов, d = 8 и d_k = d_v = 4; каждый маленький квадрат — одно число. Три проекции превращают вход размера n × d в Q, K и V. QKᵀ имеет размер n × n: строка i содержит оценки запроса токена i относительно всех ключей. Softmax работает по строкам, а AV даёт по одному d_v-мерному выходу на токен. Выделенная строка прослеживает токен 2: его входную строку, его запрос, его строку оценок и весов и его выход, в котором смешаны все строки V.

Выход содержит по одной строке на каждый входной токен, поэтому слои можно ставить друг на друга. В батче, дополненном паддингом (padding), тензоры имеют размер [B,n,d][B, n, d], а оценки — [B,n,n][B, n, n].

Разобранный пример#

Три токена, «dogs chase cats» («собаки гонятся за кошками»), d=4d = 4, dk=dv=2d_k = d_v = 2, игрушечные числа:

X=(101001011001),X = \begin{pmatrix} 1&0&1&0 \\ 0&1&0&1 \\ 1&0&0&1 \end{pmatrix}, WQ=(10011000),WK=(01100011),WV=(10021101).\begin{gathered} W_Q = \begin{pmatrix} 1&0 \\ 0&1 \\ 1&0 \\ 0&0 \end{pmatrix}, \qquad W_K = \begin{pmatrix} 0&1 \\ 1&0 \\ 0&0 \\ 1&1 \end{pmatrix}, \\[4pt] W_V = \begin{pmatrix} 1&0 \\ 0&2 \\ 1&1 \\ 0&1 \end{pmatrix}. \end{gathered}

Проекции:

Q=(200110),K=(012112),V=(210311).\begin{gathered} Q = \begin{pmatrix} 2&0 \\ 0&1 \\ 1&0 \end{pmatrix}, \qquad K = \begin{pmatrix} 0&1 \\ 2&1 \\ 1&2 \end{pmatrix}, \\[4pt] V = \begin{pmatrix} 2&1 \\ 0&3 \\ 1&1 \end{pmatrix}. \end{gathered}

Оценки и масштабированные оценки (2≈1.414\sqrt 2 \approx 1.414):

QK⊤=(042112021),QK⊤2=(02.8281.4140.7070.7071.41401.4140.707).\begin{gathered} QK^\top = \begin{pmatrix} 0&4&2 \\ 1&1&2 \\ 0&2&1 \end{pmatrix}, \\[4pt] \frac{QK^\top}{\sqrt 2} = \begin{pmatrix} 0&2.828&1.414 \\ 0.707&0.707&1.414 \\ 0&1.414&0.707 \end{pmatrix}. \end{gathered}

Softmax по строкам и выход:

A=(0.0450.7680.1870.2480.2480.5030.1400.5760.284),Y=AV=(0.2772.5361.0001.4970.5642.152).\begin{gathered} A = \begin{pmatrix} 0.045&0.768&0.187 \\ 0.248&0.248&0.503 \\ 0.140&0.576&0.284 \end{pmatrix}, \\[4pt] Y = AV = \begin{pmatrix} 0.277&2.536 \\ 1.000&1.497 \\ 0.564&2.152 \end{pmatrix}. \end{gathered}

Проверим вторую строку вручную: y2=0.248 (2,1)+0.248 (0,3)+0.503 (1,1)=(1.000,1.497)y_2 = 0.248\,(2, 1) + 0.248\,(0, 3) + 0.503\,(1, 1) = (1.000, 1.497) с точностью до округления. Dogs берёт 77% своего нового вектора от chase и лишь 4.5% от себя; chase берёт половину из cats. Каждая строка даёт в сумме единицу, столбцы — не обязательно.

Рисунок

Self-attention для одного токена: что куда идёт

Запрос, ключи, значения и веса для токена chaseЗапрос q₂ = (0, 1); ключи (0, 1), (2, 1), (1, 2); оценки 0.707, 0.707, 1.414; веса 0.248, 0.248, 0.503; значения (2, 1), (0, 3), (1, 1); выход (1.000, 1.497).1010dogs0101chase1001catsновое01k121v121k203v212k311v3WKWVWQ01q210.7070.7071.4142softmax0.2480.2480.5033v1×0.4970.248v2×0.0000.745v3×0.5030.503+1.0001.497y24входсостояниевеса и гейтывыходЗапрос, ключи, значения и веса для токена chaseЗапрос q₂ = (0, 1); ключи (0, 1), (2, 1), (1, 2); оценки 0.707, 0.707, 1.414; веса 0.248, 0.248, 0.503; значения (2, 1), (0, 3), (1, 1); выход (1.000, 1.497).1010dogs0101chase1001catsновое01k121v121k203v212k311v3WKWVWQ01q210.7070.7071.4142softmax0.2480.2480.5033v1×0.4970.248v2×0.0000.745v3×0.5030.503+1.0001.497y24входсостояниевеса и гейтывыход
Числа разобранного примера, строка токена chase. Каждый токен проецируется в ключ k и значение v, а chase ещё и в запрос q (1). Запрос сравнивается со всеми ключами, включая собственный (2), softmax даёт веса 0.248, 0.248, 0.503 (3), и новый вектор chase — взвешенная сумма значений, (1.000, 1.497) (4). Это тот же механизм, что у внимания в переводчике: запрос вместо состояния декодера, ключи и значения вместо состояний энкодера; новое — все три берутся из одной последовательности линейными проекциями, и так делается для каждого токена одновременно.

Рисунок проводит строку chase через четыре шага раздела От эмбеддингов к Q, K и V: спроецировать (1), оценить (2), нормировать (3), смешать (4). Если положить его рядом с шагом внимания переводчика, это то же вычисление: запрос играет роль состояния декодера, ключи и значения — роль состояний энкодера. Нового две вещи, в янтарной рамке: все три берутся из одной последовательности обучаемыми проекциями, и каждый токен проходит те же четыре шага одновременно с остальными — это и вычисляет матричная форма выше.

Рисунок

Один запрос: от оценок к выходу

Self-attention для одного запроса, шаг за шагомТри панели. На первой перечислены запрос токена dogs, а также ключи и значения токенов dogs, chase и cats. На второй горизонтальные полосы показывают три масштабированные оценки, а рядом — полосы весов softmax; chase получает большую часть веса, около 0.77. Третья — небольшой график: три вектора значений в виде оранжевых стрелок из начала координат и синий путь, который по очереди прибавляет взвешенные значения и заканчивается в точке выхода около (0.28, 2.54) внутри пунктирного треугольника значений.① проекциязапрос токена dogsq1 = x1WQ = (2, 0)токенkjvjdogs(0, 1)(2, 1)chase(2, 1)(0, 3)cats(1, 2)(1, 1)одни и те же W_K, W_V для всех② оценка ③ нормировкаq₁·kⱼ / √2вес α₁ⱼdogs0.000.045chase2.830.768cats1.410.187softmax: веса положительны, сумма 1④ смешивание: y₁ = Σ α₁ⱼ vⱼ121230v₁ dogsv₂ chasev₃ catsy₁ (0.277, 2.536)Self-attention для одного запроса, шаг за шагомТри панели. На первой перечислены запрос токена dogs, а также ключи и значения токенов dogs, chase и cats. На второй горизонтальные полосы показывают три масштабированные оценки, а рядом — полосы весов softmax; chase получает большую часть веса, около 0.77. Третья — небольшой график: три вектора значений в виде оранжевых стрелок из начала координат и синий путь, который по очереди прибавляет взвешенные значения и заканчивается в точке выхода около (0.28, 2.54) внутри пунктирного треугольника значений.① проекциязапрос токена dogsq1 = x1WQ = (2, 0)токенkjvjdogs(0, 1)(2, 1)chase(2, 1)(0, 3)cats(1, 2)(1, 1)одни и те же W_K, W_V для всех② оценка ③ нормировкаq₁·kⱼ / √2вес α₁ⱼdogs0.000.045chase2.830.768cats1.410.187softmax: веса положительны, сумма 1④ смешивание: y₁ = Σ α₁ⱼ vⱼ121230v₁ dogsv₂ chasev₃ catsy₁ (0.277, 2.536)
Игрушечный пример из заметки, токен «dogs» в роли запроса (d = 4, d_k = d_v = 2). ① Его запрос q₁ = (2, 0) сравнивается с ключами всех трёх токенов, включая его самого. ② Скалярные произведения, делённые на √2, равны 0.00, 2.83, 1.41. ③ Softmax превращает их в веса 0.045, 0.768, 0.187, сумма которых равна единице. ④ Выход y₁ = (0.277, 2.536) — взвешенная сумма векторов значений; синяя ломаная откладывает их один за другим. Поскольку веса положительны и дают в сумме единицу, каждый выход лежит внутри треугольника с вершинами в значениях (пунктир).

Те же входы без проекций, A0=softmax⁡(XX⊤/4)A_0 = \operatorname{softmax}(XX^\top/\sqrt 4), делают мысль предыдущего подраздела наглядной:

XX⊤=(201021112),A0=(0.5060.1860.3070.1860.5060.3070.2740.2740.452):\begin{gathered} XX^\top = \begin{pmatrix} 2&0&1 \\ 0&2&1 \\ 1&1&2 \end{pmatrix}, \\[4pt] A_0 = \begin{pmatrix} 0.506&0.186&0.307 \\ 0.186&0.506&0.307 \\ 0.274&0.274&0.452 \end{pmatrix}: \end{gathered}

оценки XX⊤XX^\top симметричны и диагональ побеждает в каждой строке, но сами веса не симметричны: у каждой строки свой знаменатель softmax, поэтому α13=e0.5/5.367=0.307\alpha_{13} = e^{0.5}/5.367 = 0.307, а α31=e0.5/6.016=0.274\alpha_{31} = e^{0.5}/6.016 = 0.274. При симметричных оценках влияние dogs на cats и cats на dogs по-прежнему задаётся одним числом, с точностью до нормировки строк. Выученные WQ≠WKW_Q \ne W_K делают несимметричными сами оценки, а большая оценка токена для самого себя становится выученным выбором.

Рисунок

Веса внимания с обучаемыми проекциями и без них

Внимание на сырых скалярных произведениях и внимание с проекциямиДве матрицы весов три на три для токенов dogs, chase и cats. Левая, без проекций, построена из симметричных оценок и почти симметрична (dogs к cats 0.307, cats к dogs 0.274, потому что каждая строка нормируется отдельно); её диагональные элементы — наибольшие в каждой строке (0.506, 0.506 и 0.452). Правая, с обучаемыми проекциями запросов и ключей, построена из несимметричных оценок; наибольшие элементы — dogs к chase, chase к cats и cats к chase.без проекцийdogschasecatsdogschasecats.506.186.307.186.506.307.274.274.452запросы = ключи = xс W_Q и W_Kdogschasecatsdogschasecats.045.768.187.248.248.503.140.576.284q = xW_Q, k = xW_KВнимание на сырых скалярных произведениях и внимание с проекциямиДве матрицы весов три на три для токенов dogs, chase и cats. Левая, без проекций, построена из симметричных оценок и почти симметрична (dogs к cats 0.307, cats к dogs 0.274, потому что каждая строка нормируется отдельно); её диагональные элементы — наибольшие в каждой строке (0.506, 0.506 и 0.452). Правая, с обучаемыми проекциями запросов и ключей, построена из несимметричных оценок; наибольшие элементы — dogs к chase, chase к cats и cats к chase.без проекцийdogschasecatsdogschasecats.506.186.307.186.506.307.274.274.452запросы = ключи = xс W_Q и W_Kdogschasecatsdogschasecats.045.768.187.248.248.503.140.576.284q = xW_Q, k = xW_K
Строки — запросы, столбцы — ключи; каждая строка даёт в сумме единицу, наибольший вес в каждой строке обведён. Слева: оценки — обычные скалярные произведения входных векторов, softmax(XXᵀ/√4). Оценки XXᵀ симметричны, и диагональный элемент — наибольший в каждой строке, потому что при равных нормах вектор лучше всего совпадает сам с собой. Веса почти симметричны, но не совсем: у каждой строки свой знаменатель, поэтому dogs → cats 0.307, а cats → dogs 0.274. Справа: те же входы с игрушечными проекциями W_Q ≠ W_K, softmax(QKᵀ/√2). Теперь несимметричны сами оценки (dogs → chase 4, chase → dogs 1): «dogs» читает в основном из «chase» (0.768) и сохраняет от себя лишь 0.045.

Зачем делить на dk\sqrt{d_k}#

Предположим, что компоненты qq и kk независимы, имеют среднее 0 и дисперсию 1 — примерно это дают инициализация и нормализация. Тогда q⋅k=∑c=1dkqckcq \cdot k = \sum_{c=1}^{d_k} q_c k_c — сумма dkd_k независимых слагаемых, у каждого из которых среднее E[qc] E[kc]=0\mathbb E[q_c]\,\mathbb E[k_c] = 0 и дисперсия E[qc2] E[kc2]=1\mathbb E[q_c^2]\,\mathbb E[k_c^2] = 1. Дисперсии независимых слагаемых складываются, поэтому

Var⁡(q⋅k)=dk,Var⁡ ⁣(q⋅kdk)=1.\operatorname{Var}(q \cdot k) = d_k, \qquad \operatorname{Var}\!\Big(\frac{q \cdot k}{\sqrt{d_k}}\Big) = 1 .

(Vaswani et al., 2017, §3.2.1, сноска 4.) Делитель — размер одной головы dkd_k (64 в базовом трансформере), а не ширина модели dd (512).

Почему величина важна: якобиан softmax равен ∂αj/∂sl=αj(δjl−αl)\partial \alpha_j / \partial s_l = \alpha_j(\delta_{jl} - \alpha_l). Когда одна оценка доминирует, α\alpha близок к one-hot-вектору, все элементы якобиана близки к нулю, и WQW_Q и WKW_K перестают получать градиент. Сводка одним числом — след ∑jαj(1−αj)=1−∑jαj2\sum_j \alpha_j(1 - \alpha_j) = 1 - \sum_j \alpha_j^2: 1−1/n1 - 1/n для равномерных весов и 00 для весов в виде one-hot-вектора.

Игрушечная симуляция: n=16n = 16 ключей, q,k∼N(0,Idk)q, k \sim \mathcal N(0, I_{d_k}), 2000 испытаний на каждое значение dkd_k, начальное значение генератора (seed) 0.

dkd_kст. откл. q⋅kq \cdot kнаибольший вес, без масштабанаибольший вес, с масштабом1−∑αj21 - \sum \alpha_j^2, без масштаба1−∑αj21 - \sum \alpha_j^2, с масштабом
10.990.210.210.870.87
41.990.430.230.710.87
164.010.690.250.430.87
648.020.840.250.220.87
51222.490.940.250.080.87

Без масштабирования при dk=64d_k = 64 один ключ уже на инициализации забирает большую часть веса, а мера градиента падает до четверти. В разобранном примере без 2\sqrt 2 первая строка была бы (0.016,0.867,0.117)(0.016, 0.867, 0.117) вместо (0.045,0.768,0.187)(0.045, 0.768, 0.187). Масштаб задаёт только начальную точку: обучение по-прежнему может сделать внимание острым, увеличивая нормы запросов и ключей.

Рисунок

Зачем оценки делят на √d_k

Дисперсия оценок и насыщение softmax с ростом размера ключаЛевый график: три колоколообразные кривые с центром в нуле для немасштабированной оценки при d_k, равном 4, 16 и 64, каждая шире и ниже предыдущей, и три высокие узкие синие кривые для масштабированной оценки, из которых кривая для d_k, равного 4, немного острее. Правый график, по оси абсцисс d_k в логарифмической шкале от 1 до 512: без масштабирования наибольший вес растёт примерно от 0.2 до 0.94, а мера градиента падает от 0.87 до 0.08; с масштабированием обе линии остаются горизонтальными, около 0.25 и 0.87.распределение оценки−24−16−8081624q·k/√d_k: дисперсия 1d_k = 4d_k = 16d_k = 64softmax по 16 ключам00.51141664256размер ключа d_k (лог. шкала)без масштаба: q·kq·k/√dₖсплошные: наибольший вес, max αⱼпунктир: мера градиента, 1 − Σ αⱼ²Дисперсия оценок и насыщение softmax с ростом размера ключаЛевый график: три колоколообразные кривые с центром в нуле для немасштабированной оценки при d_k, равном 4, 16 и 64, каждая шире и ниже предыдущей, и три высокие узкие синие кривые для масштабированной оценки, из которых кривая для d_k, равного 4, немного острее. Правый график, по оси абсцисс d_k в логарифмической шкале от 1 до 512: без масштабирования наибольший вес растёт примерно от 0.2 до 0.94, а мера градиента падает от 0.87 до 0.08; с масштабированием обе линии остаются горизонтальными, около 0.25 и 0.87.распределение оценки−24−16−8081624q·k/√d_k: дисперсия 1d_k = 4d_k = 16d_k = 64softmax по 16 ключам00.51141664256размер ключа d_k (лог. шкала)без масштаба: q·kq·k/√dₖсплошные: наибольший вес, max αⱼпунктир: мера градиента, 1 − Σ αⱼ²
Слева: если компоненты q и k независимы, имеют среднее 0 и дисперсию 1, то оценка q·k имеет дисперсию d_k; кривые — её точные плотности при d_k = 4, 16, 64, которые с ростом d_k приближаются к нормальным. Деление на √d_k возвращает каждый случай к дисперсии 1 (синие кривые; пунктирная, d_k = 4, самая острая, две другие почти совпадают). Справа: симуляция с 16 ключами, 2000 случайных испытаний на каждое значение d_k (игрушечная, seed 0). Без масштабирования (красные линии) наибольший вес softmax растёт от 0.21 до 0.94, а мера градиента 1 − Σ αⱼ² падает от 0.87 до 0.08; при d_k = 64 они равны 0.84 и 0.22. С масштабированием (синие линии) обе величины остаются около 0.25 и 0.87 при любом d_k.

Маски#

Исключаемым оценкам до softmax присваивается −∞-\infty: поскольку e−∞=0e^{-\infty} = 0, их веса в точности равны нулю, а остальные веса строки по-прежнему дают в сумме единицу.

  • Маска паддинга (padding mask). Столбцы [PAD] маскируются во всех строках: маска размера [B,1,n][B, 1, n] распространяется (broadcasting) на все запросы. Выходы запросов-паддингов отбрасываются позже.
  • Каузальная маска (causal mask). Языковая модель, предсказывающая токен t+1t + 1, не должна давать позиции tt видеть более поздние позиции. Маскирование j>ij > i (верхний треугольник, размер [n,n][n, n]) делает это сразу для всех позиций, поэтому декодер обучается на всей последовательности за один проход; на это опирается заметка о GPT.

В разобранном примере, если считать cats паддингом, строки становятся (0.056,0.944,0)(0.056, 0.944, 0), (0.5,0.5,0)(0.5, 0.5, 0) и (0.196,0.804,0)(0.196, 0.804, 0); с каузальной маской — (1,0,0)(1, 0, 0), (0.5,0.5,0)(0.5, 0.5, 0) и (0.140,0.576,0.284)(0.140, 0.576, 0.284).

Рисунок

Маска паддинга и каузальная маска на одних и тех же оценках

Веса внимания с маской паддинга и с каузальной маскойТри матрицы весов три на три для dogs, chase и cats. Без маски все девять весов положительны. С маской паддинга столбец cats заштрихован и помечен минус бесконечностью, и веса каждой строки распределены только между dogs и chase. С каузальной маской заштрихованы три клетки над диагональю; первая строка равна 1, 0, 0, вторая 0.5, 0.5, 0, а третья сохраняет все три веса.без маскиdogschasecatsdogschasecats.045.768.187.248.248.503.140.576.284разрешены все ключимаска паддингаdogschasecatsdogschasecats.056.944−∞.500.500−∞.196.804−∞ключ «cats» — паддингкаузальная маскаdogschasecatsdogschasecats1−∞−∞.500.500−∞.140.576.284нет ключей после запросаВеса внимания с маской паддинга и с каузальной маскойТри матрицы весов три на три для dogs, chase и cats. Без маски все девять весов положительны. С маской паддинга столбец cats заштрихован и помечен минус бесконечностью, и веса каждой строки распределены только между dogs и chase. С каузальной маской заштрихованы три клетки над диагональю; первая строка равна 1, 0, 0, вторая 0.5, 0.5, 0, а третья сохраняет все три веса.без маскиdogschasecatsdogschasecats.045.768.187.248.248.503.140.576.284разрешены все ключимаска паддингаdogschasecatsdogschasecats.056.944−∞.500.500−∞.196.804−∞ключ «cats» — паддингкаузальная маскаdogschasecatsdogschasecats1−∞−∞.500.500−∞.140.576.284нет ключей после запроса
Веса внимания игрушечного примера; строки — запросы, столбцы — ключи. Маска присваивает оценке −∞ до softmax (заштрихованные клетки), поэтому её вес в точности равен 0, а остальные веса строки перенормируются так, чтобы их сумма была равна единице. Маска паддинга: ключ «cats» исключён из каждой строки, и первая строка становится 0.056, 0.944, 0. Каузальная маска: верхний треугольник исключён, поэтому первый токен видит только себя, а последняя строка не меняется.
scores = q @ k.transpose(-2, -1) / math.sqrt(d_k)          # [B, n, n]
scores = scores.masked_fill(mask, float("-inf"))           # mask: True там, где ключ запрещён
weights = scores.softmax(dim=-1)                           # каждая строка даёт в сумме 1 по разрешённым ключам
output = weights @ v                                       # [B, n, d_v]

causal = torch.triu(torch.ones(n, n, dtype=torch.bool), diagonal=1)   # True над диагональю

Phuong and Hutter (2022) приводят точный псевдокод для обеих масок. Строка, в которой замаскированы все ключи, даёт 0/00/0, то есть NaN. Каузальная маска такого не допускает (диагональ разрешена), маска паддинга — тоже, пока в каждой последовательности есть настоящий токен; но при обеих масках и паддинге слева, как при генерации батчами, у запроса-паддинга могут быть замаскированы все ключи, поэтому реализации используют вместо −∞-\infty большое конечное отрицательное число.

Self-attention не видит порядка слов#

Без информации о позициях «cats chase dogs» переставляет строки XX и выхода, а у весов — строки и столбцы: dogs получает (0.277,2.536)(0.277, 2.536) в обоих предложениях. В общем случае Attention⁡(ΠX)=Π Attention⁡(X)\operatorname{Attention}(\Pi X) = \Pi\,\operatorname{Attention}(X) для любой матрицы перестановки Π\Pi, если маски нет (каузальная маска привязана к позициям и это нарушает); доказательство — в заметке о позиционных представлениях. Порядок нужно передать извне: прибавить к XX (абсолютные позиции) или встроить в оценки (относительные и ротационные позиции).

Рисунок

Перестановка двух слов лишь переставляет результат

Self-attention не видит порядка словДве матрицы весов, одна для dogs chase cats, другая для cats chase dogs, с одинаковыми элементами на переставленных местах. Под каждой матрицей перечислены три выходных вектора; выход для dogs выделен и равен 0.277, 2.536 в обоих предложениях.«dogs chase cats»dogschasecatsdogschasecats.045.768.187.248.248.503.140.576.284выходы (без позиций)dogs(0.277, 2.536)chase(1.000, 1.497)cats(0.564, 2.152)«cats chase dogs»catschasedogscatschasedogs.284.576.140.503.248.248.187.768.045выходы (без позиций)cats(0.564, 2.152)chase(1.000, 1.497)dogs(0.277, 2.536)обмен1 ↔ 3Self-attention не видит порядка словДве матрицы весов, одна для dogs chase cats, другая для cats chase dogs, с одинаковыми элементами на переставленных местах. Под каждой матрицей перечислены три выходных вектора; выход для dogs выделен и равен 0.277, 2.536 в обоих предложениях.«dogs chase cats»dogschasecatsdogschasecats.045.768.187.248.248.503.140.576.284выходы (без позиций)dogs(0.277, 2.536)chase(1.000, 1.497)cats(0.564, 2.152)«cats chase dogs»catschasedogscatschasedogs.284.576.140.503.248.248.187.768.045выходы (без позиций)cats(0.564, 2.152)chase(1.000, 1.497)dogs(0.277, 2.536)
Self-attention без информации о позициях на «dogs chase cats» и на «cats chase dogs». Вторая матрица весов — это первая, у которой строки и столбцы переставлены одинаково, и строки выхода тоже переставлены: вектор, вычисленный для «dogs», равен (0.277, 2.536) в обоих предложениях. Слой не может определить, кто за кем гонится; порядок нужно передать отдельно.

Многоголовое внимание#

Токену часто нужны сразу несколько отношений: его синтаксическая вершина, антецедент, предыдущий токен. Один softmax может распределить вес по ним, но тогда их значения усредняются в один вектор. Многоголовое внимание (multi-head attention) запускает hh вычислений внимания параллельно, каждое со своими проекциями меньшего размера, и объединяет их выходы:

Hr=Attention⁡(Q(r),K(r),V(r)),MultiHead⁡(X)=[H1;… ;Hh] WO,\begin{gathered} H_r = \operatorname{Attention}\big(Q^{(r)}, K^{(r)}, V^{(r)}\big), \\ \operatorname{MultiHead}(X) = \big[H_1; \dots; H_h\big]\, W_O , \end{gathered}

где Q(r)=XWQ(r)Q^{(r)} = X W_Q^{(r)}, K(r)=XWK(r)K^{(r)} = X W_K^{(r)}, V(r)=XWV(r)V^{(r)} = X W_V^{(r)} для r=1,…,hr = 1, \dots, h, выход HrH_r головы rr лежит в Rn×dv\mathbb R^{n \times d_v}, [ ⋅ ; ⋅ ][\,\cdot\,;\,\cdot\,] — конкатенация по оси признаков, а WO∈Rhdv×dW_O \in \mathbb R^{h d_v \times d}.

При dk=dv=d/hd_k = d_v = d/h все 3h3h матриц проекций содержат 3h⋅d⋅d/h=3d23h \cdot d \cdot d/h = 3d^2 весов, а WOW_O — ещё d2d^2: 4d24d^2 на слой при любом hh, как у одной головы полной ширины. В небольшом энкодере для AG News из заметки о трансформере (d=32d = 32, h=4h = 4, dk=8d_k = 8) это 3⋅4⋅32⋅8+322=4 0963 \cdot 4 \cdot 32 \cdot 8 + 32^2 = 4\,096. Базовый трансформер использует d=512d = 512, h=8h = 8, dk=dv=64d_k = d_v = 64; у BERT-base (d=768d = 768, h=12h = 12) — 4⋅7682≈2.364 \cdot 768^2 \approx 2.36 млн весов внимания на слой.

Конкатенация, за которой следует WOW_O, — это сумма по головам. Разобьём WOW_O на hh блоков по dvd_v строк, WO(r)W_O^{(r)}; тогда, обозначив через A(r)A^{(r)} матрицу весов головы rr, получаем

MultiHead⁡(X)=∑r=1hA(r) X WV(r)WO(r).\operatorname{MultiHead}(X) = \sum_{r=1}^{h} A^{(r)}\, X\, W_V^{(r)} W_O^{(r)} .

Каждая голова добавляет свой вклад, со своим шаблоном внимания и своим отображением WV(r)WO(r)W_V^{(r)} W_O^{(r)} ранга не выше dvd_v из прочитанного в записываемое (Elhage et al., 2021). В коде головы — одна проекция, у которой затем меняется форма; это быстрее, чем цикл по головам:

# W_Q: [d, h*d_k], h матриц W_Q^(r), поставленных рядом; так же для W_K и W_V
q = (x @ W_Q).view(B, n, h, d_k).transpose(1, 2)    # [B, h, n, d_k]; так же для k и v
out = attention(q, k, v, mask)                      # mask: [B, 1, n, n] или [B, 1, 1, n], распространяется на все головы; out: [B, h, n, d_k]
out = out.transpose(1, 2).reshape(B, n, h * d_k)    # конкатенация голов
y = out @ W_O                                       # [B, n, d]
Рисунок

Многоголовое внимание: четыре головы размера 8 в модели ширины 32

Четыре головы внимания работают параллельно, и их выходы конкатенируютсяВход X слева подаётся в четыре головы. Для каждой головы показан шаблон весов шесть на шесть: голова 1 смотрит на предыдущий токен (полоса под диагональю), голова 2 — на следующий токен (полоса над диагональю), голова 3 — на первый токен (столбец слева), голова 4 — на одно значимое слово (столбец посередине). Каждая голова выдаёт узкую полосу выхода n на 8; четыре полосы ставятся рядом в блок n на 32, который умножается на W_O и даёт выход n на 32.Xn × 32голова 1предыдущий токенn × 8голова 2следующий токенn × 8голова 3первый токенn × 8голова 4значимое словоn × 8конкатенацияn × 32× W_O32 × 32выходn × 32Четыре головы внимания работают параллельно, и их выходы конкатенируютсяВход X сверху подаётся в четыре головы, расположенные столбцом. Голова 1 смотрит на предыдущий токен, голова 2 — на следующий токен, голова 3 — на первый токен, голова 4 — на одно значимое слово; каждая показана шаблоном весов шесть на шесть. Их выходы n на 8 ставятся рядом в блок n на 32, который умножается на W_O и даёт выход n на 32 внизу.Xn × 32голова 1предыдущий токенголова 2следующий токенголова 3первый токенголова 4значимое словоконкатенация× W_O (32 × 32)выходn × 32
Каждая голова проецирует один и тот же вход X (n × 32) в собственные запросы, ключи и значения размера d_k = 32/4 = 8, вычисляет собственные веса n × n и возвращает выход n × 8. Четыре выхода конкатенируются обратно в n × 32 и смешиваются через W_O. Шаблоны весов — игрушечные примеры на шести токенах (оценка 4 для одного ключа, 0 для остальных, на целевой ключ приходится около 0.92 веса): головы могут смотреть на предыдущий токен, на следующий токен, на общий первый токен или на одно значимое слово. Параметров на слой: 3 · 4 · 32 · 8 + 32² = 4096 = 4 · 32², столько же, сколько у одной головы полной ширины.

Головы работают бок о бок, и их вклады суммируются; слои ставятся друг на друга, поэтому запросы слоя уже несут контекст из нижних слоёв. Обученные головы смотрят на предыдущий или следующий токен либо следуют синтаксису или кореференции (Clark et al., 2019; Voita et al., 2019), и многие из них можно удалить после обучения с небольшой потерей качества (Michel, Levy and Neubig, 2019).

Вычислительная сложность#

В матрице оценок n2n^2 элементов на голову, каждый вычисляется за dkd_k умножений, и произведение с VV стоит столько же:

ОперацияВремяПамятьПоследовательные шагиСамый длинный путь
Проекции XWQ,XWK,XWVXW_Q, XW_K, XW_VO(nd2)O(n d^2)O(nd)O(nd)O(1)O(1)—
Оценки QK⊤QK^\top и веса ×V\times VO(n2d)O(n^2 d)O(n2)O(n^2) на головуO(1)O(1)O(1)O(1)
Рекуррентный слой, для сравненияO(nd2)O(n d^2)O(nd)O(nd)O(n)O(n)O(n)O(n)

Последние два столбца взяты из таблицы 1 в Vaswani et al. (2017). Внимание начинает доминировать, как только nn становится небольшим кратным dd: n=2dn = 2d по сравнению с проекциями и около 6d6d по сравнению с целым блоком, где ширина полносвязного блока (feed-forward) равна 4d4d. В этом небольшом энкодере (n=128n = 128, d=32d = 32) матрицы оценок содержат 16 38416\,384 элемента на голову на пример, а 2n2d≈1.0×1062n^2 d \approx 1.0 \times 10^6 умножений со сложением (multiply-adds) для QK⊤QK^\top и AVAV уже больше, чем 4nd2≈5.2×1054nd^2 \approx 5.2 \times 10^5 для WQW_Q, WKW_K, WVW_V и WOW_O. Взамен все nn строк получаются одним матричным произведением. Системы с длинным контекстом борются с квадратичным членом: кэширование ключей и значений, точные ядра (kernels), которые никогда не хранят матрицу n×nn \times n (Dao et al., 2022), и разреженные, оконные или линеаризованные варианты.

Рисунок

Рекуррентность и self-attention: длина пути и стоимость

Длина пути и стоимость рекуррентности и self-attentionПанель а: шесть токенов в ряд, соединённых стрелками от каждого к следующему; путь от токена 1 к токену 6 выделен и занимает пять шагов. Панель б: те же шесть токенов с дугами между каждой парой; выделена единственная дуга от токена 1 к токену 6. Панель в: график в логарифмических осях числа операций на слой в зависимости от длины последовательности при ширине 512; прямая внимания n в квадрате на d круче прямой рекуррентного слоя n на d в квадрате, и они пересекаются при n, равном 512.(а) рекуррентность1234561 → 6: 5 шагов; 6 последовательных обновлений(б) self-attention123456любая пара — 1 шаг; все позиции параллельно(в) число операций на слой, d = 51210⁶10⁸10¹⁰10¹²1612810248192длина последовательности nn = d = 512внимание n²dRNN nd²Длина пути и стоимость рекуррентности и self-attentionПанель а: шесть токенов в ряд, соединённых стрелками от каждого к следующему; путь от токена 1 к токену 6 выделен и занимает пять шагов. Панель б: те же шесть токенов с дугами между каждой парой; выделена единственная дуга от токена 1 к токену 6. Панель в: график в логарифмических осях числа операций на слой в зависимости от длины последовательности при ширине 512; прямая внимания n в квадрате на d круче прямой рекуррентного слоя n на d в квадрате, и они пересекаются при n, равном 512.(а) рекуррентность1234561 → 6: 5 шагов; 6 последовательных обновлений(б) self-attention123456любая пара — 1 шаг; все позиции параллельно(в) число операций на слой, d = 51210⁶10⁸10¹⁰10¹²1612810248192длина последовательности nn = d = 512внимание n²dRNN nd²
(а) В рекуррентном слое информация от токена 1 доходит до токена 6 через 5 обновлений состояния, и 6 шагов должны выполняться один за другим. (б) В self-attention каждый токен напрямую читает каждый другой токен: все 15 пар разделены одним шагом, и все позиции вычисляются одновременно. (в) Цена: на один слой оценки внимания и взвешенная сумма требуют около n²d умножений со сложением против nd² для рекуррентного слоя или проекций; при d = 512 оба члена равны при n = 512 с точностью до постоянных множителей, а для более длинных последовательностей доминирует внимание. Обе оси логарифмические.

Как выглядят головы обученной модели#

Обучим 4-слойный энкодер с 4 головами из заметки о трансформере на AG News и посмотрим на все 16 голов на одном обучающем примере из 49 токенов:

  • Слой 1. Строки близки к one-hot-векторам на нескольких содержательных токенах; в голове 4 большинство строк выбирают injured, travel, southern или baghdad.
  • Слой 2. Веса распределены по многим ключам.
  • Слои 3 и 4. Вертикальные полосы: почти каждый запрос читает одни и те же немногие ключи (некоторые головы слоя 3, все головы слоя 4).

Карты показывают, откуда голова читает, а не на что опирается классификатор.

Что изменилось#

Внимание декодера к энкодеруSelf-attention
Откуда запросыСостояние декодераКаждый токен последовательности
Откуда ключи и значенияСостояния энкодера (один вектор для обоих)Каждый токен, через отдельные проекции
Путь между двумя позициямиЧерез рекуррентность, O(n)O(n)Один шаг
Последовательная зависимостьЧерез обе рекуррентностиОтсутствует между позициями внутри слоя (генерация по-прежнему выдаёт по одному токену за раз)
ВычислениеОдин softmax на шаг декодераОдин softmax n×nn \times n на голову и слой
СтоимостьO(nd)O(nd) на шаг декодераO(n2d)O(n^2 d) на слой
Порядок словИз рекуррентностиПотерян; его нужно передать
ГоловыОднаhh параллельно

Ограничения#

  • Нет понятия порядка. Его вводит заметка о позиционных представлениях.
  • Квадратичность по nn. Удвоение контекста учетверяет работу и память: 65 53665\,536 весов на последовательность на слой при n=128n = 128, h=4h = 4; около 10910^9 на голову при n=32 000n = 32\,000. Точные ядра экономят память, но не работу.
  • Веса должны давать в сумме единицу. Голова, которой нечего читать, «паркует» вес на разделителях, пунктуации или первом токене (Clark et al., 2019; Xiao et al., 2024).
  • Только взвешенные средние. Выходы — выпуклые комбинации значений, линейно отображённые через WOW_O, без преобразования каждого токена по отдельности. Заметка о трансформере добавляет полносвязный слой, остаточные связи (residual connections) и нормализацию.
  • Веса — не объяснения. Разные шаблоны весов могут давать одно и то же предсказание (Jain and Wallace, 2019; более аккуратный взгляд — у Wiegreffe and Pinter, 2019), а веса не учитывают величину значений, на которые они умножаются (Kobayashi et al., 2020).

Что нужно запомнить#

  • Attention⁡(Q,K,V)=softmax⁡(QK⊤/dk) V\operatorname{Attention}(Q, K, V) = \operatorname{softmax}(QK^\top/\sqrt{d_k})\,V, где Q=XWQQ = XW_Q, K=XWKK = XW_K, V=XWVV = XW_V: строка ii выхода — взвешенное среднее строк значений с весами по тому, насколько запрос ii совпадает с каждым ключом.
  • Размеры: XX — n×dn \times d, оценки — n×nn \times n (m×nm \times n для cross-attention), выход — n×dvn \times d_v.
  • Отдельные WQW_Q и WKW_K позволяют оценкам sijs_{ij} и sjis_{ji} различаться и не дают совпадению токена с самим собой доминировать. Оценки через обычное скалярное произведение симметричны; веса softmax, построенные из них, уже немного различаются, потому что у каждой строки свой знаменатель.
  • Делим на dk\sqrt{d_k} одной головы, потому что Var⁡(q⋅k)=dk\operatorname{Var}(q \cdot k) = d_k; иначе softmax насыщается и его градиент исчезает.
  • Маски присваивают оценкам −∞-\infty: маска паддинга закрывает столбцы ключей, каузальная маска — верхний треугольник.
  • Self-attention без маски эквивариантен относительно перестановок (permutation-equivariant): порядок нужно передать во входе или в оценках.
  • hh голов размера d/hd/h стоят 4d24d^2 весов на слой при любом hh и складываются как сумма вкладов отдельных голов.
  • Один шаг между любыми двумя позициями, никакой последовательной зависимости, память O(n2)O(n^2) на голову.

Ноутбук#

week05_transformer.ipynb. Ячейки нумеруются с 1, считая все ячейки, включая текстовые. Приведённые числа взяты из прогона, сохранённого в курсе; у ячеек, изменённых с тех пор под текущие версии библиотек, сохранённого выхода нет.

  • Внимание (ячейки 38–40). Одна голова, причём головы слоя вычисляются в цикле Python, а не одной проекцией с изменением формы: проекции nn.Linear(embed_dim, embed_dim // num_heads) без смещения, оценки через torch.bmm, делённые на dk\sqrt{d_k}, masked_fill с −∞-\infty, softmax, дропаут, bmm с VV. MultiHeadAttention конкатенирует головы из nn.ModuleList и применяет nn.Linear(embed_dim, embed_dim) без смещения в роли WOW_O. Маска паддинга tokens == pad_idx повторяется по строкам запросов до размера [B,n,n][B, n, n].
  • Модель (ячейки 42–45). Четыре слоя, описанные в заметке о трансформере. Сохранённый выход ячейки 45 показывает Linear(32 → 8) для каждой головы и Linear(32 → 32) для WOW_O: 4 0964\,096 весов внимания на слой.
  • Карты внимания (ячейки 51–62). 16 матриц весов одного примера из 49 токенов (ячейка 59, у каждой панели своя цветовая шкала) и слой 1, голова 4 с подписями токенов (ячейка 62); они описаны выше и сохранены только как изображения.
  • Подводные камни. Комментарии о размерах неверны для WVW_V в ячейке 39 (она отображает 32 в 8) и для attention_scores в ячейке 42 (размер [B,n,n,h,L][B, n, n, h, L]).
  • Cross-attention. week04_lstm_attention_transformer.ipynb, ячейки 71–79, сохраняет усреднённые по головам веса однослойного трансформера для перевода, размер [32,31,32][32, 31, 32] (батч, целевая, исходная последовательность); они обсуждаются в предыдущей заметке.

Дальше#

Self-attention смешивает позиции и больше ничего не делает: у него нет нелинейности для отдельного токена, нет понятия о порядке, а глубокий стек голых слоёв внимания трудно обучать. Заметка о трансформере оборачивает многоголовое внимание остаточными связями и нормализацией слоя (layer normalization), добавляет полносвязную сеть, применяемую к каждой позиции отдельно (position-wise feed-forward), и собирает из блоков энкодер, декодер с каузальным self-attention или энкодер–декодер с cross-attention.

Литература#