VKВлад КуликовОткрытые лекции
Современные LLM5 / 20

Модуль 5. Современные механизмы внимания

Сначала попробуйте решить упражнение. Ответы, подробные решения и проектные ориентиры раскрываются отдельно и сохраняют своё состояние при переключении RU/EN.

Скачать файлы курса (.zip) 2588,1 КБ

Архив содержит файлы на обоих языках и вспомогательные материалы с сохранением структуры каталогов.

У1. Содержимое KV-кэша

Для 36-слойной модели с Hq=64H_q=64, Hkv=8H_{kv}=8, dh=64d_h=64, bf16 и контекстом 131 072 вычислите кэш при полном хранении во всех слоях. Затем замените половину слоёв окном 128. Считайте одну последовательность и только содержимое K/V; локальные кэши ограничены окном.

Показать ответ и решение

Решение. На каждом слое для одного сохранённого токена нужны и ключ, и значение:

2Hkvdh⋅2 байт=2⋅8⋅64⋅2=2048 B/token.2H_{kv}d_h\cdot2\ \text{байт}=2\cdot8\cdot64\cdot2=2048\ \text{B/token}.

Поэтому кэш полных слоёв равен 36⋅131072⋅2048=936\cdot131072\cdot2048=9 GiB. В гибриде 18 полных слоёв занимают 18⋅131072⋅2048=4.518\cdot131072\cdot2048=4.5 GiB, а 18 локальных хранят только окно: 18⋅128⋅2048=4.518\cdot128\cdot2048=4.5 MiB. Сумма: 4.5043945 GiB. Число голов запросов в этой формуле не участвует: K/V хранятся по HkvH_{kv}.

У2. Online softmax

Для логитов [2,1,3,0.5] и значений [1,2,3,4] обработайте первые два и последние два элемента отдельными блоками. Воспроизведите ℓ1=1.367879\ell_1=1.367879, n1=1.735759n_1=1.735759, коэффициент перенормировки e−1e^{-1} и итог O=2.502297O=2.502297.

Показать ответ и решение

Решение. Для первого блока берём максимум m1=2m_1=2:

ℓ1=e2−2+e1−2=1+e−1=1.367879,\ell_1=e^{2-2}+e^{1-2}=1+e^{-1}=1.367879, n1=1e0+2e−1=1.735759.n_1=1e^{0}+2e^{-1}=1.735759.

Для второго блока m2=3m_2=3, поэтому ℓ2=1+e−2.5=1.082085\ell_2=1+e^{-2.5}=1.082085 и n2=3+4e−2.5=3.328340n_2=3+4e^{-2.5}=3.328340. Общий максимум M=3M=3; статистики первого блока нужно перенести в эту шкалу множителем em1−M=e−1e^{m_1-M}=e^{-1}:

ℓ=e−1ℓ1+ℓ2=1.585300,n=e−1n1+n2=3.966890.\ell=e^{-1}\ell_1+\ell_2=1.585300,\qquad n=e^{-1}n_1+n_2=3.966890.

Итоговый выход внимания O=n/ℓ=2.502297O=n/\ell=\mathbf{2.502297}. Перенормировка и позволяет объединять блоки без хранения всей матрицы логитов.

У3. Обучаемый sink

Покажите, что обучаемый sink эквивалентен дополнительному логиту ss с нулевым вектором значения. Объясните, почему обычный фиксированный ключ не создаёт произвольный постоянный логит для всех запросов.

Показать ответ и решение

Решение. Добавим к обычным логитам zi=q⊤ki/dhz_i=q^\top k_i/\sqrt{d_h} ещё один логит ss и соответствующее значение vs=0v_s=0. Тогда

O=es0+∑iezivies+∑iezi=∑iezivies+∑iezi.O=\frac{e^s0+\sum_i e^{z_i}v_i}{e^s+\sum_i e^{z_i}} =\frac{\sum_i e^{z_i}v_i}{e^s+\sum_i e^{z_i}}.

Этот дополнительный столбец логитов может получить часть вероятностной массы, не добавляя собственного вклада в выход. У фиксированного ключа логит равен q⊤ks/dhq^\top k_s/\sqrt{d_h} и меняется вместе с запросом. Вектор ks=0k_s=0 даёт только константу 0; произвольную обучаемую константу ss для всех qq нужно задавать отдельным скаляром или смещением.

У4. Порядок голов GQA

Постройте малый пример с Hq=8H_q=8, Hkv=2H_{kv}=2 и докажите, что repeat по каждой KV-голове совпадает с групповой формулой, а tile меняет соответствие голов.

Показать ответ и решение

Решение. Размер группы равен g=Hq/Hkv=4g=H_q/H_{kv}=4, поэтому голова запроса hh использует KV-голову ⌊h/4⌋\lfloor h/4\rfloor: головы 0–3 получают KV0, а 4–7 — KV1. Если обозначить их значениями [10, 20], правильное расширение равно

text
repeat_interleave(4): [10, 10, 10, 10, 20, 20, 20, 20]

Это и есть групповая формула. tile(4) даёт [10, 20, 10, 20, 10, 20, 10, 20]: размер тензора верен, но головы запросов связываются с другими KV-головами. Следовательно, одной проверки формы массива недостаточно.

У5 [проект]

На GPU сравните прямой PyTorch attention и доступную реализацию SDPA/Flash для N=1K,2K,4K,8KN=1K,2K,4K,8K. Измерьте время, пиковую память и максимальную ошибку относительно эталона float64.

Демо и критерии проекта

Демо к этому проектному упражнению: русскоязычный demo-notebook модуля 5, разделы «Демо 1» и «Демо 2» как отправная точка для проектной работы модуля. Он показывает компактный воспроизводимый пример на малых данных, а не готовое решение полномасштабного проекта.

У6. Маска после префикса

Для запроса в позиции 5 и ключей 0–5 выпишите маску полного внимания и окна W=3W=3. Сравните с tril(ones(1, 6)).

Показать ответ и решение

Решение. Причинное ограничение сравнивает абсолютные позиции: ключ kk разрешён при k≤5k\le5. Поэтому полная строка маски равна [1,1,1,1,1,1]. Окно ширины 3 добавляет условие 5−3+1≤k5-3+1\le k, то есть оставляет позиции 3, 4 и 5: [0,0,0,1,1,1].

У tril(ones(1,6)) единственная строка имеет локальный индекс 0, поэтому результат [1,0,0,0,0,0]. Эта операция ничего не знает о пяти уже обработанных позициях. Чтобы получить нужную маску, нужно сравнивать абсолютные индексы или передать соответствующее смещение. В ноутбуке повторите рассуждение для нескольких новых запросов: у каждой строки будет своя абсолютная позиция.

У7. Поглощение MLA

Задайте A∈R6×4A\in\mathbb R^{6\times4}, B∈R5×4B\in\mathbb R^{5\times4}, семь латентов размерности 4 и три запроса размерности 6. Сверьте логиты и выход до и после переноса проекций; затем добавьте позиционную ветвь. Объясните, почему перенос вращения на запрос зависит от позиции ключа.

Показать ответ и решение

Решение. Соберём запросы строками Q∈R3×6Q\in\mathbb R^{3\times6}, а латенты — строками C∈R7×4C\in\mathbb R^{7\times4}. В прямом варианте K=CA⊤K=CA^\top имеет форму 7×67\times6, а V=CB⊤V=CB^\top — 7×57\times5. Содержательные логиты равны

QK⊤=QAC⊤.QK^\top=QAC^\top.

Справа сначала вычисляется QAQA формы 3×43\times4, затем матрица логитов формы 3×73\times7. В обеих ветвях добавим одну и ту же позиционную часть и применим одинаковые масштабирование, маску и softmax. Получим одинаковые веса P∈R3×7P\in\mathbb R^{3\times7}. Для выхода:

PV=P(CB⊤)=(PC)B⊤.PV=P(CB^\top)=(PC)B^\top.

Прямой путь сначала восстанавливает семь значений ширины 5. Переставленный путь смешивает латенты в PCPC формы 3×43\times4 и лишь затем расширяет результат до 3×53\times5. При выходной матрице O∈R5×doutO\in\mathbb R^{5\times d_{out}} можно заранее объединить B⊤OB^\top O; результат сохраняется.

Если RoPE вращает восстановленный ключ, эффективный запрос становится A⊤Rn−m⊤qA^\top R_{n-m}^\top q: он зависит от позиции ключа nn. Это не одно преобразование, которое можно сохранить для всех ключей. Отдельная позиционная ветвь обходит именно эту зависимость. Опыт §8 ноутбука проверяет логиты, выход и выходную проекцию с допуском 10−1210^{-12}; контрпример с другой позицией ключа проверяет предел перестановки.

Для GPU-проекта сначала сравните точность на малой последовательности с одной головой и эталоном FP64. Затем измеряйте выбранные большие размеры: одинаковые входы, маски и точности, прогрев, no_grad, синхронизация CUDA перед замером и после него, сброс пикового счётчика. Указывайте, входят ли проекции, создание маски и кэш в измерение; фиксируйте выбранное ядро и различайте выделенную и зарезервированную память. Уменьшите размер при нехватке памяти; не стройте большой FP64-эталон лишь ради проверки скорости.