Линейная алгебра

Умножение матриц: движок нейросетей

Цели урока

  • Знать формулу $C_{ij} = \sum_k A_{ik} B_{kj}$ и правило размерностей $(m \times n) \cdot (n \times p) = (m \times p)$
  • Понимать, почему $AB \neq BA$, но $(AB)C = A(BC)$ - и как ассоциативность даёт 50x ускорение в LLM
  • Видеть в Linear-слое, attention и forward pass нейросети одну и ту же matmul-операцию
  • Применять правила $(AB)^T = B^T A^T$ и $(AB)^{-1} = B^{-1} A^{-1}$ в backprop и нормальном уравнении
  • Различать наивный $O(n^3)$, Штрассен $O(n^{2.807})$ и GPU GEMM по практической применимости

Предварительные знания

  • Векторы и операции с ними (сложение, скалярное умножение)
  • Скалярное произведение (dot product) и его геометрический смысл
  • Базовое представление о матрицах и Linear-слое нейросети
  • Векторы: язык ML
  • Скалярное произведение

GPT-4 делает 1.8 триллиона операций умножения матриц на один токен. Не "математических операций вообще" - именно matmul. Каждый Linear слой, каждый attention head, каждый батч в любой нейросети мира - это одна и та же примитивная операция, разогнанная до 989 TFLOPS на H100.

  • **Linear layer** (PyTorch nn.Linear): $y = Wx + b$ - базовый строительный блок BERT, GPT, ResNet, ViT, всех нейросетей
  • **Self-attention** (Transformer): три matmul на один head ($X W_q$, $QK^T$, $\text{attn} \cdot V$); 96 слоёв × 96 голов в GPT-4
  • **cuBLAS GEMM** (NVIDIA, 1990-е): A100 = 312 TFLOPS, H100 = 989 TFLOPS FP16; весь deep learning сводится к этой библиотеке
  • **FlashAttention** (Tri Dao, 2022): IO-aware tiling в SRAM - 5-10x ускорение и 20x меньше памяти; основа Llama-3, Mistral
  • **Batched matmul на TPU MXU**: 128×128 systolic array Google - один такт даёт 16 384 умножения; обучение PaLM, Gemini
  • **MoE sparse matmul** (Mixtral, GPT-4): только 2 из 8 экспертов активны на токен - 4x меньше FLOPs при том же качестве

Сильвестр и Кэли - рождение матричной алгебры

В 1850 году Джеймс Джозеф Сильвестр впервые употребил слово "матрица" (matrix) в современном математическом смысле - в статье о определителях. Через восемь лет, в 1858 году, его друг Артур Кэли опубликовал "A Memoir on the Theory of Matrices" - первую систематическую работу, формализовавшую умножение матриц именно в той форме "строка на столбец", которую мы используем сегодня. Кэли заметил, что матрицы можно складывать, умножать и обращать - и эти операции образуют алгебру, в которой коммутативность нарушена. Через 90 лет, в 1943 году, Маккаллок и Питтс предложили модель искусственного нейрона как взвешенной суммы - то есть как одной строки матричного умножения. От Кэли до GPT-4 - прямая линия в 166 лет.

Современный deep learning - это компьютерная реализация ассоциативной некоммутативной алгебры Кэли на параллельном железе

Что такое умножение матриц

**GPT-4 делает 1.8 триллиона операций умножения матриц на один токен.** Не "математических операций" вообще - именно matmul. Каждый Linear слой, каждый шаг self-attention, каждый батч через сеть - это перемножение матриц. Правило смотрится странно (строки на столбцы, порядок важен), но именно эта странность делает matmul идеальным языком для композиции линейных функций - того самого, на чём построены нейросети и LLM.

**Формула:** $C_{ij} = \sum_k A_{ik} B_{kj}$. Элемент $C_{ij}$ - это скалярное произведение i-й строки матрицы A на j-й столбец матрицы B. Иначе говоря, dot product всех пар строка/столбец, упакованный в одну операцию.

Правило размерностей

ПРАВИЛО: (m x n) * (n x p) = (m x p) ^-------^ Должны совпадать! ЧТО СОВПАДАЕТ: число столбцов первой = числу строк второй ПРИМЕРЫ из реального ML: (32 x 768) * (768 x 3072) = (32 x 3072) OK [batch * W_ff в трансформере] (32 x 64) * (64 x 64) = (32 x 64) OK [batched attention head] (32 x 768) * (512 x 768) = ОШИБКА! FAIL [несовпадение] В ATTENTION-БЛОКЕ: Q: (seq x d_k), K: (seq x d_k) Q * K^T: (seq x d_k) * (d_k x seq) = (seq x seq) Получается квадратная матрица внимания seq x seq

Linear layer как первое применение

W = [[w11 w12 w13] x = [x1] b = [b1] [w21 w22 w23]] [x2] [b2] [x3] y = W*x + b: y1 = w11*x1 + w12*x2 + w13*x3 + b1 y2 = w21*x1 + w22*x2 + w23*x3 + b2 КАЖДЫЙ ВЫХОД - взвешенная сумма ВСЕХ входов. ДЛЯ GPT-2 FFN (n=768, m=3072): - W содержит 768*3072 = 2 359 296 весов - Один forward: 2.3M умножений-сложений - Батч из 512 токенов: ~1.2 миллиарда операций НА ОДИН СЛОЙ - 96 слоёв * 1.2B = 115 миллиардов операций на батч

**Почему правило "строка на столбец" именно такое**: это не случайность. Это единственный способ определить умножение так, чтобы оно совпало с **последовательным применением линейных функций**. Сеть из 96 слоёв GPT-4 - это 96-кратная композиция, записанная как произведение 96 матриц.

AB = BA - умножение матриц должно быть коммутативным как умножение чисел

В общем случае AB ≠ BA. Порядок матриц определяет порядок применения линейных трансформаций

Матрица - это функция из одного пространства в другое. AB означает "сначала B, потом A". "Повернуть, потом отразить" даёт не тот же результат, что "отразить, потом повернуть". Числа коммутируют, потому что это скаляры; матрицы - это операторы, и в общем случае операторы не коммутируют. Простой контрпример: для матриц поворота на 90° R и зеркального отражения F композиции R·F и F·R дают разные финальные ориентации.

Произведение матриц A (4 × 7) и B (7 × 3) имеет какой размер?

Свойства: что сохраняется, что нет

Не каждое привычное свойство арифметики сохраняется. Коммутативность - погибла. Ассоциативность - уцелела и стала важнейшим инструментом оптимизации в LLM-инференсе. Дистрибутивность работает. Транспонирование переворачивает порядок. Каждое свойство имеет прямую цену в TFLOPS на A100.

СвойствоФормулаML-применение
Ассоциативность$(AB)C = A(BC)$Matmul fusion в PyTorch/XLA: компилятор переставляет порядок ради экономии FLOPs
Дистрибутивность$A(B+C) = AB + AC$Разделение веса: $W \cdot x = W_1 \cdot x + W_2 \cdot x$ при разбиении весов на чипы
Identity$AI = IA = A$Residual connections (ResNet, Transformer): $\text{out} = x + F(x)$
Транспонирование$(AB)^T = B^T A^T$Backpropagation: градиент через matmul - умножение на транспонированную матрицу
Обратная$(AB)^{-1} = B^{-1} A^{-1}$Нормальное уравнение, ridge regression, undistortion камеры
Определитель$\det(AB) = \det A \cdot \det B$Normalizing flows: log-det Jacobian для вероятностей
Не коммутативно$AB \neq BA$Порядок слоёв нейросети не переставляется - другая архитектура

Ассоциативность как 55-кратное ускорение

ЗАДАЧА: посчитать A * B * C, где A: (100 x 10) B: (10 x 100) C: (100 x 1) СПОСОБ 1: (A*B)*C A*B = (100x10)*(10x100) = (100x100) -> 100*10*100 = 100 000 операций (A*B)*C = (100x100)*(100x1) = (100x1) -> 100*100*1 = 10 000 операций ИТОГО: 110 000 операций СПОСОБ 2: A*(B*C) B*C = (10x100)*(100x1) = (10x1) -> 10*100*1 = 1 000 операций A*(B*C) = (100x10)*(10x1) = (100x1) -> 100*10*1 = 1 000 операций ИТОГО: 2 000 операций ВЫИГРЫШ: 55x при идентичном математическом результате Это matrix chain multiplication - классическая DP-задача (O(n^3) на оптимизацию). PyTorch JIT и XLA-компилятор находят оптимальный порядок автоматически.

**Главная ловушка транспонирования**: $(AB)^T = B^T A^T$, **НЕ** $A^T B^T$. Порядок переворачивается. Это критично в backpropagation: градиент по входу слоя $W \cdot x$ - это $W^T \cdot \nabla y$, и при цепочке слоёв порядок умножений в прямом и обратном проходе зеркально противоположный.

$(AB)^T = A^T B^T$ - транспонирование распределяется по умножению как обычно

$(AB)^T = B^T A^T$ - порядок переворачивается

Это прямое следствие правила композиции функций: транспонирование меняет роли строк и столбцов, а значит меняет роль "входа" и "результата" каждой матрицы. Аналогично с обратной: $(AB)^{-1} = B^{-1} A^{-1}$ - чтобы откатить "сначала B, потом A", нужно сначала откатить A, потом B (как при снимании двух свитеров). Эта переворачиваемость прямо проявляется в backprop: forward $y = W_3 W_2 W_1 x$, backward $\nabla_x = W_1^T W_2^T W_3^T \nabla_y$ - порядок зеркальный.

Геометрия: композиция линейных карт

Матрица $W$ размера $(m \times n)$ - это **линейная функция** из n-мерного пространства в m-мерное. Применить функцию к вектору - это умножить на него. Произведение матриц - это **композиция** функций. Двигатель сети из 96 слоёв буквально читается как $W_{96} \cdot W_{95} \cdot \ldots \cdot W_1 \cdot x$, где каждая матрица переводит представление из одного латентного пространства в следующее.

v -> A*v -> B*(A*v) = (B*A)*v ПОРЯДОК ПРИМЕНЕНИЯ: Сначала A (стоит правее), потом B (стоит левее) Читаем СПРАВА НАЛЕВО В НЕЙРОСЕТИ: out = W3 * (W2 * (W1 * x)) = (W3 * W2 * W1) * x Сначала W1 (ближайший к x), потом W2, потом W3 В записи слева направо это смотрится "задом наперёд" ГЕОМЕТРИЯ: R(theta) = поворот на theta S(k) = масштабирование в k раз R(45) * S(2): сначала растягиваем, потом вращаем S(2) * R(45): сначала вращаем, потом растягиваем Итоговая ориентация фигуры - разная!

Поворот на 90° + масштабирование в 2 раза

R = [[0, -1], [1, 0]] - поворот на 90° против часовой S = [[2, 0], [0, 2]] - масштабирование в 2 раза Точка p = (1, 0). R*S*p: S*p = (2, 0) R*(S*p) = (0, 2) - сначала растянули в (2,0), потом повернули в (0,2) S*R*p: R*p = (0, 1) S*(R*p) = (0, 2) - повернули в (0,1), потом растянули в (0,2) ЗДЕСЬ результат СОВПАЛ - но это особый случай: масштабирование изотропно (одинаково по осям) и коммутирует с поворотом. Стоит сделать масштабирование анизотропным diag(2, 1) - и результат разойдётся.

**Собственные векторы переживают композицию.** Если $v$ - собственный вектор A с собственным значением $\lambda$, то $A^n v = \lambda^n v$ - направление сохраняется при любом числе применений A. Это основа собственных векторов, PageRank (доминантный собственный вектор графа ссылок) и спектральной кластеризации.

В записи $W_3 W_2 W_1 x$ какая матрица применяется к вектору $x$ первой?

Matmul в ML-стеке: от Linear до FlashAttention

**Весь современный deep learning сводится к одной операции - matmul.** Linear слой, attention head, embedding lookup, batched inference - всё одна и та же primitive, разогнанная до 312 TFLOPS на A100, 989 TFLOPS на H100. NVIDIA построила империю на cuBLAS GEMM. Google - на TPU MXU (matrix multiply unit). FlashAttention переписала весь attention ради одного: больше matmul на меньшем кэше.

Self-attention: три matmul на один токен

**Бухгалтерия трансформера**: один attention head = 3 проекции + 2 attention matmul = 5 matmul. GPT-4 имеет ~96 слоёв × ~96 голов × 5 ≈ 46 080 matmul на forward pass одного токена. Плюс FFN (2 matmul на слой) - итого ~50К matmul на токен. Умножение матриц - не одна из операций, а **главная и почти единственная**.

Стек ускорения matmul

УровеньТехнологияЧто делает
АлгоритмNaive O(n³) → Strassen O(n^2.807)Менее операций; редко используется - плохо параллелится
Библиотека CPUOpenBLAS, MKL, BLISSIMD-оптимизация, cache blocking, ~50-100x от наивного Python
GPU kernelcuBLAS GEMM (CUDA)Tensor Cores: A100 = 312 TFLOPS, H100 = 989 TFLOPS на FP16
TPU hardwareMXU (128×128 systolic array)Один такт = 16 384 умножения; vMAC за такт
SparsityBlock-sparse, MoEMixtral, GPT-4: только 2 из 8 экспертов активны - 4x меньше matmul
IO-awareFlashAttention (Tri Dao, 2022)Tile attention в SRAM - 5-10x ускорение, 20x меньше памяти
QuantizationINT8 / FP8 GEMMВ 2-4x быстрее FP16 на тех же транзисторах

Batched matmul: одна операция на весь батч

**FlashAttention переворачивает игру.** Стандартный attention хранит матрицу $QK^T$ размера seq×seq в HBM (медленная VRAM). Для seq=8192 это 256 МБ на голову - не помещается в SRAM (быстрый кэш ~20 МБ на A100). FlashAttention плиточно считает softmax-блоки прямо в SRAM, никогда не материализуя полную матрицу. Результат - 5-10x ускорение и 20x меньше памяти при том же ответе. На этом построены Llama-3 и Mistral.

ОДИН FORWARD GPT-4 НА 1 ТОКЕН (~1.8 триллиона matmul-операций): Naive Python (3 вложенных цикла): ~10^12 операций / 10^7 ops/sec = 100 000 секунд = 28 часов на токен NumPy через MKL (Intel, 1 CPU): ~10^12 / 10^11 ops/sec = 10 секунд на токен GPU A100 cuBLAS (312 TFLOPS): ~10^12 / 3*10^14 ops/sec = ~3.3 миллисекунды на токен (на самом деле medium-batch ~30-50 мс - bottleneck в HBM, а не в счёте) H100 SXM с FlashAttention-2 (FP8): ~1 миллисекунда на токен РАЗНИЦА Python vs H100: 10^8 раз. Восемь порядков.

Вопросы для размышления

  • Если все архитектуры нейросетей сводятся к одной операции matmul, что определяет различия между ResNet, Transformer и Diffusion модели - геометрия весов, порядок умножений или нелинейности между слоями?

Связанные уроки

  • la-01-vectors-intro — Матрица - это набор векторов; умножение строится на скалярном произведении строки и столбца
  • la-02-dot-product — Каждый элемент произведения C[i,j] - это dot product i-й строки A и j-го столбца B
  • la-13-eigenvectors — Собственные векторы сохраняют направление при матричном умножении - база PCA и PageRank
  • la-15-svd — SVD раскладывает любую матрицу в произведение трёх специальных матриц - low-rank approximation, LoRA
  • aie-03-llm-fundamentals — Каждый Linear слой и каждый attention head в LLM - это matmul; основа inference и обучения
  • ml-25-neural-networks — Forward pass нейросети - это цепочка W·x + b с активацией между слоями
  • alg-01-big-o — Наивный matmul - O(n^3), Штрассен - O(n^2.807); анализ сложности объясняет, почему GPU важен
  • stats-21
Умножение матриц: движок нейросетей

0

1

Войти

Что верно для произвольных совместимых матриц A и B?

Matmul - это поэлементное умножение двух матриц одинакового размера

Matmul - это сумма внешних произведений: $C = \sum_k A_{:,k} \cdot B_{k,:}^T$, или эквивалентно набор скалярных произведений строка-на-столбец

Поэлементное умножение (Hadamard product, $A \odot B$) и matmul - **разные операции**. Поэлементное требует одинаковых размеров и даёт матрицу того же размера; matmul требует совпадения внутренних размерностей и даёт матрицу другого размера. Hadamard в DL встречается реже - в gating механизмах (LSTM, GLU) и масках. Когда говорят "матрица умножается на матрицу" в ML-контексте, почти всегда имеется в виду matmul через cuBLAS GEMM, а не Hadamard.

Зачем в self-attention делят на $\sqrt{d_k}$?