🔴 Сложный ⏱️ 45 минут

Операции над матрицами

📋 Содержание урока

Операции над матрицами 🧮

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

Давай разберёмся сразу с главным: когда ты пишешь на PyTorch output = model(x), под капотом происходит цепочка из десятков и сотен операций именно над матрицами — умножений, сложений, снова умножений. Каждый слой нейросети — это буквально $y = Wx + b$, где $W$ — матрица весов, $x$ — вектор входа, $b$ — вектор смещения. Обучение модели — это миллионы таких операций подряд. Если ты не понимаешь, как именно складываются и перемножаются матрицы, весь этот процесс так и останется для тебя чёрным ящиком с магией внутри.

Хорошая новость: операции над матрицами устроены логично, и большая часть правил интуитивно понятна — если один раз аккуратно разобраться "руками", на конкретных числах. Именно этим мы и займёмся: без магии, без "просто поверь формуле", а с полным разбором каждого шага.

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


История: откуда это взялось?

Идея складывать и перемножать таблицы чисел витала в воздухе задолго до того, как для этого появилось строгое обоснование. Ещё в древнем Китае, в трактате "Математика в девяти книгах" (примерно II век до н. э.), решали системы линейных уравнений методом, по сути эквивалентным работе с матрицами — хотя самого термина, конечно, не существовало.

Настоящее рождение матричной алгебры как самостоятельной науки связано с именем британского математика Артура Кэли (Arthur Cayley). В 1858 году он опубликовал работу "A Memoir on the Theory of Matrices", где впервые ввёл операции сложения, умножения и обращения матриц как объектов самих по себе — не просто как удобной записи для систем уравнений, а как полноценных математических сущностей со своей алгеброй. Именно Кэли заметил и явно сформулировал то, что сегодня удивляет почти каждого студента: умножение матриц, в отличие от умножения чисел, не коммутативно — $AB$ не обязано равняться $BA$. Для человека XIX века, воспитанного на арифметике, где $2 \times 3 = 3 \times 2$ всегда, это было почти революционным наблюдением.

Термин "матрица" (от латинского matrix — "матка", "источник") предложил ещё чуть раньше, в 1850 году, коллега и друг Кэли — Джеймс Джозеф Сильвестр. Он использовал это слово в смысле "то, из чего порождаются определители" (сами определители к тому моменту уже активно изучались). Вместе Кэли и Сильвестр заложили фундамент того, что позже назовут линейной алгеброй — а спустя полтора столетия эта алгебра стала математическим языком, на котором говорят нейросети, компьютерная графика, квантовая механика и вообще практически весь современный цифровой мир. Кэли, разумеется, не мог предвидеть GPT или трансформеры — но правила, которые он вывел, работают в них без единого изменения.


Сложение и вычитание матриц

Интуиция

Представь, что ты ведёшь два интернет-магазина и в конце месяца хочешь посчитать суммарные продажи по каждому товару в каждом регионе. У тебя есть таблица продаж магазина №1 и точно такая же по структуре таблица продаж магазина №2 — те же товары в строках, те же регионы в столбцах. Чтобы получить общие продажи, ты просто складываешь соответствующие ячейки: продажи товара А в регионе 1 из первой таблицы плюс продажи товара А в регионе 1 из второй таблицы.

Это и есть сложение матриц — предельно честная, "покомпонентная" операция, без каких-либо хитростей. Никакого правила "строка на столбец" тут нет и не будет — оно появится только в умножении.

Но есть одно жёсткое требование: таблицы должны быть одинакового размера. Складывать продажи по 5 регионам с продажами по 7 регионам бессмысленно — просто нечего складывать с чем.

Определение

Определение: Суммой матриц $A$ и $B$ одинакового размера $m \times n$ называется матрица $C = A + B$ того же размера $m \times n$, каждый элемент которой равен сумме соответствующих элементов: $c_{ij} = a_{ij} + b_{ij}$. Вычитание определяется аналогично: $(A - B)_{ij} = a_{ij} - b_{ij}$. Складывать (вычитать) можно только матрицы одинакового размера.

Пример 1 (лёгкий)

Даны две матрицы $2 \times 2$:

$$A = \begin{pmatrix} 1 & 2 \\ 3 & 4 \end{pmatrix}, \quad B = \begin{pmatrix} 5 & 6 \\ 7 & 8 \end{pmatrix}$$

Складываем поэлементно:

$$A + B = \begin{pmatrix} 1+5 & 2+6 \\ 3+7 & 4+8 \end{pmatrix} = \begin{pmatrix} 6 & 8 \\ 10 & 12 \end{pmatrix}$$

Вычитаем:

$$A - B = \begin{pmatrix} 1-5 & 2-6 \\ 3-7 & 4-8 \end{pmatrix} = \begin{pmatrix} -4 & -4 \\ -4 & -4 \end{pmatrix}$$

Ничего сложного — просто аккуратно складываем ячейку с ячейкой на той же позиции.

Пример 2 (средний): накопление градиентов

В обучении нейросетей есть техника gradient accumulation (накопление градиентов): когда батч данных слишком большой, чтобы влезть в память видеокарты, его дробят на несколько мини-батчей, считают градиент по каждому отдельно, а затем складывают.

Допустим, для слоя с матрицей весов $2 \times 3$ градиент по первому мини-батчу получился таким:

$$G_1 = \begin{pmatrix} 0.2 & -0.1 & 0.05 \\ 0.3 & 0.0 & -0.2 \end{pmatrix}$$

А по второму — таким:

$$G_2 = \begin{pmatrix} 0.1 & 0.05 & -0.02 \\ -0.1 & 0.2 & 0.1 \end{pmatrix}$$

Суммарный градиент, который и пойдёт в шаг оптимизатора:

$$G_1 + G_2 = \begin{pmatrix} 0.3 & -0.05 & 0.03 \\ 0.2 & 0.2 & -0.1 \end{pmatrix}$$

Обрати внимание: без этой операции gradient accumulation вообще не существовал бы как техника — весь трюк в том, чтобы честно складывать градиенты по всем мини-батчам, как будто они были одним большим батчем.

Пример 3 (сложный): матрица ошибки в классификаторе

При обучении классификатора на 3 класса и батче из 3 примеров у нас есть матрица истинных меток (one-hot) и матрица предсказаний модели:

$$Y_{\text{true}} = \begin{pmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{pmatrix}, \quad Y_{\text{pred}} = \begin{pmatrix} 0.7 & 0.2 & 0.1 \\ 0.1 & 0.8 & 0.1 \\ 0.2 & 0.3 & 0.5 \end{pmatrix}$$

Матрица ошибки (residual), которая используется как один из сигналов при вычислении градиента на выходном слое:

$$E = Y_{\text{true}} - Y_{\text{pred}} = \begin{pmatrix} 1-0.7 & 0-0.2 & 0-0.1 \\ 0-0.1 & 1-0.8 & 0-0.1 \\ 0-0.2 & 0-0.3 & 1-0.5 \end{pmatrix} = \begin{pmatrix} 0.3 & -0.2 & -0.1 \\ -0.1 & 0.2 & -0.1 \\ -0.2 & -0.3 & 0.5 \end{pmatrix}$$

Первая строка показывает: для первого примера (класс 0) модель почти угадала — предсказала $0.7$ вместо $1$. Третья строка (класс 2) — модель ошиблась сильнее: только $0.5$ уверенности вместо $1$.

Почему это важно

Сложение матриц кажется скучным на фоне умножения, но именно оно лежит в основе: residual-соединений в ResNet и трансформерах (там буквально $x + F(x)$), накопления градиентов при обучении на ограниченной памяти, вычисления ошибки между предсказанием и истиной, батч-нормализации (вычитание среднего). Каждый раз, когда ты видишь знак "+" между двумя тензорами в архитектуре нейросети — это ровно та операция, которую мы только что разобрали.


Умножение матрицы на число

Интуиция

Представь, что у тебя есть рецепт на 4 порции, и его нужно увеличить втрое — на 12 порций. Ты берёшь каждый ингредиент и умножаешь его количество на 3. Мука была 200 граммов — становится 600. Яйца были 2 штуки — становятся 6. Каждое число в рецепте масштабируется одинаково.

Точно так же работает умножение матрицы на скаляр (обычное число): каждый элемент матрицы умножается на это число, независимо от остальных.

Определение

Определение: Произведением матрицы $A$ размера $m \times n$ на число (скаляр) $\lambda$ называется матрица $\lambda A$ того же размера, каждый элемент которой равен $(\lambda A)_{ij} = \lambda \cdot a_{ij}$.

Пример 1 (лёгкий)

$$A = \begin{pmatrix} 1 & -2 \\ 0 & 4 \end{pmatrix}, \quad \lambda = 3$$$$3A = \begin{pmatrix} 3 \cdot 1 & 3 \cdot(-2) \\ 3 \cdot 0 & 3 \cdot 4 \end{pmatrix} = \begin{pmatrix} 3 & -6 \\ 0 & 12 \end{pmatrix}$$

Пример 2 (средний): шаг градиентного спуска

Это, пожалуй, самое частое применение умножения на скаляр во всём машинном обучении. Формула обновления весов в градиентном спуске:

$$W_{\text{new}} = W - \eta \cdot \nabla W$$

где $\eta$ — скорость обучения (learning rate), а $\nabla W$ — матрица градиента. Пусть градиент для слоя размера $2 \times 3$ такой:

$$\nabla W = \begin{pmatrix} 10 & -4 & 6 \\ 2 & -8 & 0 \end{pmatrix}, \quad \eta = 0.01$$

Считаем шаг обновления:

$$\eta \cdot \nabla W = \begin{pmatrix} 0.01 \cdot 10 & 0.01 \cdot(-4) & 0.01 \cdot 6 \\ 0.01 \cdot 2 & 0.01 \cdot(-8) & 0.01 \cdot 0 \end{pmatrix} = \begin{pmatrix} 0.1 & -0.04 & 0.06 \\ 0.02 & -0.08 & 0 \end{pmatrix}$$

Именно эту матрицу (с обратным знаком) прибавят к весам на следующем шаге обучения. Если бы learning rate был не $0.01$, а $1.0$, шаг оказался бы огромным — веса "прыгнули" бы слишком далеко, и модель могла бы разойтись вместо того, чтобы сходиться. Скаляр здесь буквально управляет тем, насколько агрессивно учится модель.

Пример 3 (сложный): отрицательный скаляр и коррекция яркости

$$A = \begin{pmatrix} 2 & 4 & -6 \\ 8 & -2 & 0 \\ 1 & 3 & 5 \end{pmatrix}, \quad \lambda = -0.5$$$$-0.5A = \begin{pmatrix} -0.5\cdot2 & -0.5\cdot4 & -0.5\cdot(-6) \\ -0.5\cdot8 & -0.5\cdot(-2) & -0.5\cdot0 \\ -0.5\cdot1 & -0.5\cdot3 & -0.5\cdot5 \end{pmatrix} = \begin{pmatrix} -1 & -2 & 3 \\ -4 & 1 & 0 \\ -0.5 & -1.5 & -2.5 \end{pmatrix}$$

Обрати внимание: при умножении на отрицательное число знаки всех элементов меняются на противоположные — плюсы становятся минусами и наоборот. Это, например, ровно то, что происходит при инвертировании изображения (негатив) или при развороте направления градиента на этапе градиентного подъёма (gradient ascent) вместо спуска.

Почему это важно

Умножение на скаляр — это "регулятор громкости" для матрицы: не меняет её структуру, но меняет масштаб. В ML на этом стоит весь градиентный спуск (масштабирование шага через learning rate), регуляризация (штраф $\lambda \|W\|$ буквально требует умножения матрицы весов на число), нормализация данных (деление признаков на стандартное отклонение — тоже умножение на скаляр $\frac{1}{\sigma}$), и даже банальная предобработка изображений (яркость, контраст).


Умножение матриц: правило «строка на столбец»

Интуиция

А теперь — самая важная и самая непривычная операция этого урока. Представь, что ты считаешь итоговую стоимость покупки: у тебя есть список количеств товаров (2 кг яблок, 3 кг груш, 1 кг слив) и список цен за килограмм (яблоки — 100 ₽, груши — 150 ₽, сливы — 200 ₽). Чтобы получить итоговую сумму, ты не складываешь количества с ценами напрямую — ты перемножаешь попарно и складываешь результаты: $2 \cdot 100 + 3 \cdot 150 + 1 \cdot 200 = 200 + 450 + 200 = 850$ рублей.

Это и есть суть умножения матриц: каждый элемент результата — это сумма произведений, "скалярное произведение" строки одной матрицы на столбец другой. Именно поэтому умножение матриц называют операцией "строка на столбец" — берём строку из левой матрицы, столбец из правой, перемножаем поэлементно и складываем.

Здесь же кроется и главное ограничение: чтобы взять "скалярное произведение" строки и столбца, они должны быть одной длины. А значит, число столбцов у левой матрицы обязано совпадать с числом строк у правой.

Определение

Определение: Произведением матрицы $A$ размера $m \times k$ на матрицу $B$ размера $k \times n$ называется матрица $C = AB$ размера $m \times n$, элемент которой на пересечении строки $i$ и столбца $j$ вычисляется как

$$c_{ij} = \sum_{s=1}^{k} a_{is} \cdot b_{sj} = a_{i1}b_{1j} + a_{i2}b_{2j} + \cdots + a_{ik}b_{kj}$$

Умножение $AB$ определено только тогда, когда число столбцов $A$ равно числу строк $B$. Это называется условием согласованности размеров.

Схематично: если $A$ имеет размер $(m, k)$, а $B$ — размер $(k, n)$, то "внутренние" размеры $k$ и $k$ должны совпасть, а "внешние" $m$ и $n$ определяют размер результата.

Пример 1 (лёгкий): квадратные матрицы

$$A = \begin{pmatrix} 1 & 2 \\ 3 & 4 \end{pmatrix}, \quad B = \begin{pmatrix} 5 & 6 \\ 7 & 8 \end{pmatrix}$$

Считаем каждый элемент отдельно:

  • $c_{11} = 1\cdot5 + 2\cdot7 = 5 + 14 = 19$

  • $c_{12} = 1\cdot6 + 2\cdot8 = 6 + 16 = 22$

  • $c_{21} = 3\cdot5 + 4\cdot7 = 15 + 28 = 43$

  • $c_{22} = 3\cdot6 + 4\cdot8 = 18 + 32 = 50$

$$AB = \begin{pmatrix} 19 & 22 \\ 43 & 50 \end{pmatrix}$$

Пример 2 (средний): один нейрон, реальный forward pass

Пусть в Dense-слое 3 входа и 2 выхода, матрица весов $W$ размера $2 \times 3$, а на вход подан один пример в виде вектора-столбца $x$ размера $3 \times 1$:

$$W = \begin{pmatrix} 0.5 & -0.2 & 0.1 \\ 0.3 & 0.4 & -0.1 \end{pmatrix}, \quad x = \begin{pmatrix} 1 \\ 2 \\ -1 \end{pmatrix}$$

Проверяем согласованность: у $W$ 3 столбца, у $x$ 3 строки — совпадает, можно умножать, результат будет $2 \times 1$.

$$Wx = \begin{pmatrix} 0.5\cdot1 + (-0.2)\cdot2 + 0.1\cdot(-1) \\ 0.3\cdot1 + 0.4\cdot2 + (-0.1)\cdot(-1) \end{pmatrix} = \begin{pmatrix} 0.5 - 0.4 - 0.1 \\ 0.3 + 0.8 + 0.1 \end{pmatrix} = \begin{pmatrix} 0.0 \\ 1.2 \end{pmatrix}$$

Вот и всё — это буквально то, что вычисляет каждый нейрон в каждом слое каждой нейросети на планете, только обычно одновременно для тысяч нейронов и тысяч примеров.

Пример 3 (сложный): батч из нескольких примеров сразу

На практике данные почти никогда не подают по одному примеру — их собирают в батч и обрабатывают всех разом одним матричным умножением. Пусть у нас 3 примера с 2 признаками каждый (матрица $X$ размера $3 \times 2$, строки — примеры) и матрица весов $W$ размера $2 \times 2$:

$$X = \begin{pmatrix} 1 & 2 \\ 0 & -1 \\ 3 & 1 \end{pmatrix}, \quad W = \begin{pmatrix} 2 & 0 \\ 1 & -1 \end{pmatrix}$$

Результат $XW$ будет размера $3 \times 2$ — по одной строке на каждый пример:

  • строка 1: $(1\cdot2 + 2\cdot1,\ 1\cdot0 + 2\cdot(-1)) = (4,\ -2)$

  • строка 2: $(0\cdot2 + (-1)\cdot1,\ 0\cdot0 + (-1)\cdot(-1)) = (-1,\ 1)$

  • строка 3: $(3\cdot2 + 1\cdot1,\ 3\cdot0 + 1\cdot(-1)) = (7,\ -1)$

$$XW = \begin{pmatrix} 4 & -2 \\ -1 & 1 \\ 7 & -1 \end{pmatrix}$$

Это и есть батч-обработка: одна операция матричного умножения мгновенно преобразует сразу все 3 примера — вместо того, чтобы гонять их по одному в цикле. Именно поэтому GPU так хороши для нейросетей: они заточены под быстрое умножение больших матриц.

А теперь — важный контрпример на согласованность размеров. Пусть $A$ имеет размер $2 \times 3$, а $B$ — размер $2 \times 2$. У $A$ 3 столбца, у $B$ только 2 строки — $3 \neq 2$, значит, умножить $AB$ нельзя. Именно эта ошибка чаще всего вызывает RuntimeError: mat1 and mat2 shapes cannot be multiplied в PyTorch — если ты когда-нибудь её увидишь, знай: где-то не совпали "внутренние" размеры.

Почему это важно

Умножение матриц — это математический язык, на котором записан весь forward pass нейросети: линейный слой, свёртка (после разворачивания в матрицу), self-attention (произведения запросов и ключей), полносвязный классификатор. Когда ChatGPT генерирует следующее слово, внутри происходят триллионы именно таких операций "строка на столбец", просто на матрицах гигантского размера, посчитанных параллельно на тысячах ядер GPU.


Свойства операций над матрицами

Теперь, когда у тебя в руках все три операции, разберём их поведение — и тут тебя ждёт главный сюрприз этого урока.

Некоммутативность умножения: $AB \neq BA$

В арифметике чисел порядок умножителей не важен: $3 \times 5 = 5 \times 3$. С матрицами это правило ломается. Возьмём:

$$A = \begin{pmatrix} 1 & 1 \\ 0 & 1 \end{pmatrix}, \quad B = \begin{pmatrix} 1 & 0 \\ 1 & 1 \end{pmatrix}$$

Считаем $AB$:

$$AB = \begin{pmatrix} 1\cdot1+1\cdot1 & 1\cdot0+1\cdot1 \\ 0\cdot1+1\cdot1 & 0\cdot0+1\cdot1 \end{pmatrix} = \begin{pmatrix} 2 & 1 \\ 1 & 1 \end{pmatrix}$$

Теперь $BA$:

$$BA = \begin{pmatrix} 1\cdot1+0\cdot0 & 1\cdot1+0\cdot1 \\ 1\cdot1+1\cdot0 & 1\cdot1+1\cdot1 \end{pmatrix} = \begin{pmatrix} 1 & 1 \\ 2 & 2 \end{pmatrix}$$

$AB \neq BA$ — результаты совсем разные, хотя матрицы те же самые, просто в другом порядке! Более того, для прямоугольных матриц порядок может быть ещё драматичнее: если $A$ размера $2\times3$, а $B$ — $3\times2$, то $AB$ будет размера $2\times2$, а $BA$ — размера $3\times3$. Это вообще разные по размеру матрицы, их даже сравнивать бессмысленно.

Почему это так важно для ML: когда ты пишешь код прямого прохода, порядок умножения матрицы весов и входа строго фиксирован. $Wx$ и $xW$ — это, как правило, вообще разные (или несовместимые) операции, и перепутать их — классическая ошибка новичка.

Ассоциативность: $(AB)C = A(BC)$

А вот что сохраняется — это ассоциативность: результат умножения трёх (и более) матриц не зависит от того, как расставить скобки, лишь бы порядок множителей не менялся. Проверим на примере:

$$A = \begin{pmatrix} 1 & 0 \\ 2 & 1 \end{pmatrix}, \quad B = \begin{pmatrix} 1 & 1 \\ 0 & 1 \end{pmatrix}, \quad C = \begin{pmatrix} 2 & 0 \\ 1 & 1 \end{pmatrix}$$

Считаем $(AB)C$: сначала $AB = \begin{pmatrix} 1 & 1 \\ 2 & 3 \end{pmatrix}$, затем $(AB)C = \begin{pmatrix} 1\cdot2+1\cdot1 & 1\cdot0+1\cdot1 \\ 2\cdot2+3\cdot1 & 2\cdot0+3\cdot1 \end{pmatrix} = \begin{pmatrix} 3 & 1 \\ 7 & 3 \end{pmatrix}$.

Теперь $A(BC)$: сначала $BC = \begin{pmatrix} 1\cdot2+1\cdot1 & 1\cdot0+1\cdot1 \\ 0\cdot2+1\cdot1 & 0\cdot0+1\cdot1 \end{pmatrix} = \begin{pmatrix} 3 & 1 \\ 1 & 1 \end{pmatrix}$, затем $A(BC) = \begin{pmatrix} 1\cdot3+0\cdot1 & 1\cdot1+0\cdot1 \\ 2\cdot3+1\cdot1 & 2\cdot1+1\cdot1 \end{pmatrix} = \begin{pmatrix} 3 & 1 \\ 7 & 3 \end{pmatrix}$.

Результаты совпали: $(AB)C = A(BC) = \begin{pmatrix} 3 & 1 \\ 7 & 3 \end{pmatrix}$. Ассоциативность подтверждена — а это значит, что при вычислении цепочки слоёв нейросети $W_3(W_2(W_1 x))$ ты можешь заранее перемножить веса между собой $(W_3 W_2 W_1)x$, и результат не изменится. Это не просто теоретическая красота — на этом факте строятся серьёзные оптимизации: иногда выгоднее сначала перемножить маленькие матрицы весов между собой, а потом уже применить их к данным, чем наоборот (мы вернёмся к этому в задании 28).

Дистрибутивность: $A(B+C) = AB + AC$

Умножение матриц дистрибутивно относительно сложения — точно как в обычной арифметике. Используем те же $A$, $B$, $C$: $B + C = \begin{pmatrix} 3 & 1 \\ 1 & 2 \end{pmatrix}$.

$$A(B+C) = \begin{pmatrix} 1\cdot3+0\cdot1 & 1\cdot1+0\cdot2 \\ 2\cdot3+1\cdot1 & 2\cdot1+1\cdot2 \end{pmatrix} = \begin{pmatrix} 3 & 1 \\ 7 & 4 \end{pmatrix}$$

А теперь отдельно $AB = \begin{pmatrix} 1 & 1 \\ 2 & 3 \end{pmatrix}$ (уже считали выше) и $AC = \begin{pmatrix} 1\cdot2+0\cdot1 & 1\cdot0+0\cdot1 \\ 2\cdot2+1\cdot1 & 2\cdot0+1\cdot1 \end{pmatrix} = \begin{pmatrix} 2 & 0 \\ 5 & 1 \end{pmatrix}$.

$$AB + AC = \begin{pmatrix} 1+2 & 1+0 \\ 2+5 & 3+1 \end{pmatrix} = \begin{pmatrix} 3 & 1 \\ 7 & 4 \end{pmatrix}$$

Совпало с $A(B+C)$. Это свойство постоянно используется при аналитическом выводе формул обратного распространения ошибки (backpropagation) — раскрывая скобки в градиентах, математики опираются именно на дистрибутивность матричного умножения.

А теперь попробуй сам: возьми любые три матрицы подходящих размеров и проверь оба свойства на своих числах — руки быстрее убедят тебя в этом, чем любое доказательство.


Практика: 30 заданий

Базовые (1–10)

Задание 1. Даны $A = \begin{pmatrix} 3 & 1 \\ 2 & -4 \end{pmatrix}$, $B = \begin{pmatrix} 1 & 5 \\ -2 & 0 \end{pmatrix}$. Найдите $A + B$.


Задание 2. Даны $A = \begin{pmatrix} 2 & -1 \\ 0 & 3 \end{pmatrix}$, $B = \begin{pmatrix} 5 & 2 \\ 1 & -3 \end{pmatrix}$. Найдите $A - B$.


Задание 3. Дана $A = \begin{pmatrix} 1 & -2 & 3 \\ 0 & 4 & -1 \end{pmatrix}$. Найдите $2A$.


Задание 4. Дана $A = \begin{pmatrix} 4 & 0 \\ -2 & 6 \end{pmatrix}$. Найдите $-0.5A$.


Задание 5. Даны $A = \begin{pmatrix} 1 & 2 \\ 3 & 4 \end{pmatrix}$, $B = \begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix}$. Найдите $AB$.


Задание 6. Для тех же $A$ и $B$ из задания 5 найдите $BA$ и сравните с $AB$.


Задание 7. Можно ли перемножить матрицу $A$ размера $3 \times 2$ на матрицу $B$ размера $4 \times 2$? Обоснуйте.


Задание 8. Один нейрон с весами $w = \begin{pmatrix} 2 & -1 & 0.5 \end{pmatrix}$ (строка $1\times3$) получает вход $x = \begin{pmatrix} 1 \\ 3 \\ -2 \end{pmatrix}$ (столбец $3\times1$). Найдите взвешенную сумму $wx$.


Задание 9. Дана $A = \begin{pmatrix} 5 & 2 \\ 3 & -1 \end{pmatrix}$. Найдите $A + A$ и убедитесь, что результат равен $2A$.


Задание 10. Дан градиент $G = \begin{pmatrix} 0.4 & -0.2 \\ 0.1 & 0.3 \end{pmatrix}$ и learning rate $\eta = 0.1$. Найдите шаг обновления весов $\Delta W = -\eta G$ (направление шага SGD).


Средние (11–20)

Задание 11. Даны $A = \begin{pmatrix} 1 & 0 & 2 \\ -1 & 3 & 1 \end{pmatrix}$ ($2\times3$), $B = \begin{pmatrix} 3 & 1 \\ 2 & 1 \\ 0 & 4 \end{pmatrix}$ ($3\times2$). Найдите $AB$.


Задание 12. Dense-слой: $W = \begin{pmatrix} 1 & -1 \\ 2 & 0 \end{pmatrix}$, вход $x = \begin{pmatrix} 3 \\ 1 \end{pmatrix}$, смещение $b = \begin{pmatrix} 1 \\ -2 \end{pmatrix}$. Найдите $Wx + b$.


Задание 13. Батч из 3 примеров с 2 признаками: $X = \begin{pmatrix} 1 & 0 \\ 2 & -1 \\ 0 & 3 \end{pmatrix}$, матрица весов $W = \begin{pmatrix} 1 & 2 \\ -1 & 1 \end{pmatrix}$. Найдите $XW$ (по строке результата на каждый пример).


Задание 14. Даны $A = \begin{pmatrix} 2 & -1 \\ 0 & 3 \end{pmatrix}$, $B = \begin{pmatrix} 1 & 4 \\ 2 & -2 \end{pmatrix}$, $\lambda = 3$. Проверьте: $\lambda(A+B) = \lambda A + \lambda B$?


Задание 15. Даны $A = \begin{pmatrix} 1 & 2 & 0 \\ 0 & 1 & 1 \end{pmatrix}$ ($2\times3$), $B = \begin{pmatrix} 1 & 0 \\ 2 & 1 \\ 0 & 1 \end{pmatrix}$ ($3\times2$). Найдите $AB$ и $BA$, сравните их размеры.


Задание 16. Даны $A = \begin{pmatrix} 2 & 1 \\ 0 & -1 \end{pmatrix}$, $B = \begin{pmatrix} 1 & 0 \\ 3 & 2 \end{pmatrix}$, $c = \begin{pmatrix} 1 \\ 2 \end{pmatrix}$. Проверьте ассоциативность: $(AB)c = A(Bc)$?


Задание 17. Батч из 4 примеров, 3 класса. $Y_{\text{true}} = \begin{pmatrix} 1&0&0 \\ 0&1&0 \\ 0&0&1 \\ 1&0&0 \end{pmatrix}$, $Y_{\text{pred}} = \begin{pmatrix} 0.6&0.3&0.1 \\ 0.2&0.5&0.3 \\ 0.1&0.2&0.7 \\ 0.8&0.1&0.1 \end{pmatrix}$. Найдите матрицу ошибки $E = Y_{\text{true}} - Y_{\text{pred}}$.


Задание 18. Двухслойная сеть без функции активации: $x = \begin{pmatrix} 2 \\ -1 \end{pmatrix}$, $W_1 = \begin{pmatrix} 1 & 0 \\ 0 & 1 \\ 1 & 1 \end{pmatrix}$ ($3\times2$), $W_2 = \begin{pmatrix} 1 & 1 & 0 \\ 0 & 1 & -1 \end{pmatrix}$ ($2\times3$). Найдите $h = W_1x$, затем $y = W_2h$.


Задание 19. Батч из 50 примеров по 20 признаков проходит через сеть со слоями $W_1$ ($20\times64$), $W_2$ ($64\times32$), $W_3$ ($32\times10$). Какой размер результата на каждом шаге?


Задание 20. Даны $A = \begin{pmatrix} 4 & 2 \\ -2 & 6 \end{pmatrix}$, $B = \begin{pmatrix} 1 & 3 \\ 0 & 2 \end{pmatrix}$, $\lambda = 2$. Проверьте: $\lambda(A - B) = \lambda A - \lambda B$?


Продвинутые (21–30)

Задание 21. Даны $A = \begin{pmatrix} 1&2&0 \\ 0&1&3 \\ 2&0&1 \end{pmatrix}$, $B = \begin{pmatrix} 1&0&1 \\ 2&1&0 \\ 0&1&2 \end{pmatrix}$. Найдите $AB$.


Задание 22. Для тех же $A$ и $B$ из задания 21 найдите $BA$ и сравните с $AB$.


Задание 23. Используя $A$ из задания 21, покажите, что $A \cdot I_3 = A$, где $I_3$ — единичная матрица.


Задание 24. Батч из 2 примеров, 3 признака: $X = \begin{pmatrix} 1&2&-1 \\ 0&1&3 \end{pmatrix}$, $W_1 = \begin{pmatrix} 1&0 \\ 2&-1 \\ 0&1 \end{pmatrix}$, $W_2 = \begin{pmatrix} 1&1 \\ 0&2 \end{pmatrix}$. Найдите $Y = (XW_1)W_2$.


Задание 25. Для матриц из задания 24 проверьте ассоциативность: вычислите $X(W_1W_2)$ и сравните с результатом $Y$ из задания 24.


Задание 26. Упрощённый self-attention: "запросы" $Q = \begin{pmatrix} 1 & 2 \\ 0 & 1 \end{pmatrix}$, "ключи" $K = \begin{pmatrix} 2 & 0 \\ 1 & 1 \end{pmatrix}$. Найдите матрицу "оценок" $S = QK$.


Задание 27. Упрощённая батч-нормализация: активации батча из 2 примеров $A = \begin{pmatrix} 4&6&2 \\ 8&2&6 \end{pmatrix}$, матрица средних (уже продублированная по строкам) $M = \begin{pmatrix} 3&3&3 \\ 3&3&3 \end{pmatrix}$, коэффициент масштабирования $\lambda = 0.5$. Найдите $\lambda(A - M)$.


Задание 28. Батч $X$ размера $(4,3)$ проходит через $W_1$ размера $(3,5)$ и $W_2$ размера $(5,2)$. Сравните число скалярных умножений при вычислении $(XW_1)W_2$ и $X(W_1W_2)$.


Задание 29. Даны $A = \begin{pmatrix} 0.5 & 1 \\ 2 & -0.5 \end{pmatrix}$, $B = \begin{pmatrix} 1 & 2 \\ 0 & 1 \end{pmatrix}$, $C = \begin{pmatrix} 1 & 0 \\ 1 & 1 \end{pmatrix}$. Проверьте дистрибутивность: $A(B+C) = AB + AC$?


Задание 30. Полный прямой проход крошечной сети: 3 входа → 2 скрытых нейрона → 1 выход. $x = \begin{pmatrix} 1 \\ 2 \\ -1 \end{pmatrix}$, $W_1 = \begin{pmatrix} 1&0&2 \\ 0&1&-1 \end{pmatrix}$, $b_1 = \begin{pmatrix} 1 \\ -2 \end{pmatrix}$, $W_2 = \begin{pmatrix} 2 & -1 \end{pmatrix}$, $b_2 = \begin{pmatrix} 0.5 \end{pmatrix}$. Найдите $h = W_1x + b_1$ и $y = W_2h + b_2$.


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

Давай разберёмся, где студенты обычно спотыкаются при работе с операциями над матрицами — эти пять ловушек встречаются снова и снова, и в коде на PyTorch/NumPy они же превращаются в самые частые runtime-ошибки.

1. Уверенность, что $AB = BA$. Это главная и самая коварная ошибка — привычка из школьной арифметики чисел просто не работает с матрицами. Порядок множителей меняет результат, а иногда делает операцию вообще невозможной (если $A$ имеет размер $m\times k$, а $B$ — $k\times m$ с $m \neq k$, то $AB$ будет $m\times m$, а $BA$ — $k\times k$: даже размеры разные). Всегда сохраняй порядок множителей таким, каким его требует задача — особенно в формулах вида $Wx$, где переставить местами $W$ и $x$ означает получить либо ошибку, либо совсем другой (неверный) результат.

2. Попытка перемножить матрицы "поэлементно", как при сложении. Умножение матриц — это не $c_{ij} = a_{ij} \cdot b_{ij}$ (это отдельная операция, произведение Адамара, обозначается $A \odot B$ и в этом уроке не рассматривается). Правильное умножение матриц — это "строка на столбец": $c_{ij} = \sum_s a_{is}b_{sj}$. Спутать эти две операции — частая ошибка новичков, которые привыкли, что все операции над матрицами "покомпонентны", как сложение.

3. Забыть проверить условие согласованности размеров перед умножением. Прежде чем считать $AB$, всегда убедись, что число столбцов $A$ равно числу строк $B$. Игнорирование этого правила — причина большинства ValueError: shapes not aligned в NumPy и RuntimeError: size mismatch в PyTorch. Хорошая привычка — прямо перед строкой матричного умножения мысленно (или в комментарии к коду) выписать размеры: "$(m,k) \times (k,n)$".

4. Пытаться сложить матрицы разных размеров. В отличие от умножения, у сложения нет "полугибкого" условия — только полное совпадение размеров, строка в строку, столбец в столбец. Если размеры не совпадают, сложение просто не определено (за исключением специального механизма broadcasting в NumPy/PyTorch, но это отдельная тема, выходящая за рамки чистой линейной алгебры).

5. Путать порядок вычитания $A - B$ и $B - A$. В отличие от сложения, вычитание матриц (как и вычитание чисел) не коммутативно: $A - B = -(B - A)$, знаки всех элементов меняются на противоположные. Это особенно критично при вычислении ошибки/остатка (residual) — перепутать $Y_{\text{true}} - Y_{\text{pred}}$ с $Y_{\text{pred}} - Y_{\text{true}}$ означает получить градиент с обратным знаком, и модель начнёт обучаться в неправильную сторону.


Главное запомнить

  • Сложение и вычитание матриц выполняются поэлементно и требуют полного совпадения размеров обеих матриц.

  • Умножение на скаляр — тоже поэлементная операция: каждый элемент матрицы умножается на одно и то же число.

  • Умножение матриц ($AB$) — это операция "строка на столбец": $c_{ij} = \sum_s a_{is}b_{sj}$, а не поэлементное произведение.

  • Условие согласованности: чтобы перемножить $A_{(m\times k)}$ и $B_{(k\times n)}$, число столбцов $A$ должно равняться числу строк $B$; результат имеет размер $m \times n$.

  • Умножение матриц некоммутативно: в общем случае $AB \neq BA$, а иногда одно из произведений вообще не определено.

  • Умножение матриц ассоциативно: $(AB)C = A(BC)$ — порядок расстановки скобок не влияет на результат (хотя влияет на скорость вычисления).

  • Умножение дистрибутивно относительно сложения: $A(B+C) = AB + AC$ и $(A+B)C = AC + BC$.

  • Умножение на скаляр дистрибутивно относительно сложения матриц: $\lambda(A+B) = \lambda A + \lambda B$.

  • Каждый слой нейросети — это, по сути, $y = Wx + b$: умножение матрицы весов на вектор входа плюс сложение с вектором смещения.

  • Батч-обработка — это применение одной и той же операции умножения матриц сразу ко всем примерам батча, а не цикл по отдельным примерам.


Связь с другими темами курса

Операции над матрицами — это своего рода "грамматика" линейной алгебры: без них невозможно двигаться дальше. Урок 156 дал тебе словарь (что такое матрица, какие они бывают) — этот урок дал глаголы (что с ними можно делать). Дальше эта грамматика будет использоваться постоянно.

Уже в следующем уроке про определители матриц второго и третьего порядка тебе понадобится умение уверенно перемножать элементы матрицы и складывать/вычитать результаты — сам определитель вычисляется через комбинацию произведений и сумм элементов. Чуть позже, когда дойдёт до обратных матриц, ты будешь проверять правильность найденной обратной матрицы именно через умножение: $A \cdot A^{-1} = I$ — и без сегодняшнего урока эта проверка была бы непонятной магией.

В теме систем линейных уравнений умножение матриц позволяет записать целую систему одной строкой: $Ax = b$. В линейных преобразованиях и собственных векторах композиция двух преобразований — это буквально умножение их матриц, и то, что $AB \neq BA$, объясняет, почему "сначала повернуть, потом растянуть" — не то же самое, что "сначала растянуть, потом повернуть". А в прикладной части курса, посвящённой нейросетям, вся архитектура forward pass — это цепочка операций сложения и умножения матриц, которую ты уже научился читать и считать вручную.


Интересные факты

  • Умножение двух матриц $n \times n$ "в лоб" требует порядка $n^3$ операций умножения. В 1969 году немецкий математик Фолькер Штрассен нашёл алгоритм, который выполняет это быстрее — за $O(n^{2.807})$ операций, разбивая матрицы на блоки и хитро переиспользуя промежуточные произведения. С тех пор теоретический рекорд снижался ещё несколько раз — на момент написания этого курса лучшие известные алгоритмы работают около $O(n^{2.37})$, хотя на практике они почти не используются из-за огромных скрытых констант.

  • Именно из-за того, что умножение матриц занимает основную часть времени при обучении нейросетей, современные видеокарты (GPU) и специализированные чипы (TPU от Google) физически спроектированы так, чтобы максимально быстро выполнять именно эту операцию — их архитектура во многом является железным воплощением правила "строка на столбец", распараллеленного на тысячи ядер одновременно.

  • Артур Кэли, придумавший матричную алгебру в 1858 году, не считал себя "чистым" математиком в современном смысле — он тридцать шесть лет проработал юристом-барристером, занимаясь математикой в свободное время, и за этот период опубликовал около 300 научных работ. Только в 42 года он получил профессорскую должность в Кембридже.

  • В компьютерной графике и 3D-играх умножение матриц происходит десятки миллионов раз в секунду: поворот, масштабирование и перемещение (сдвиг) каждой вершины 3D-модели — это последовательное умножение вектора координат на цепочку матриц преобразований, и порядок этого умножения (некоммутативность!) определяет, увидишь ли ты объект повёрнутым вокруг своей оси или вокруг центра сцены.


Лайфхаки и полезные трюки

  • Перед умножением всегда выписывай размеры в скобках. $(m,k)\times(k,n) = (m,n)$ — если "внутренние" числа не совпали, даже не начинай считать, сразу ищи ошибку в постановке задачи. Эта простая привычка экономит массу времени и при решении на бумаге, и при отладке кода.

  • Считай по столбцам, а не по элементам вразброс. Чтобы не запутаться при ручном вычислении $AB$, зафиксируй одну строку $A$ и последовательно "пройди" ею по всем столбцам $B$ — так меньше шанс перепутать индексы, чем прыгать между случайными элементами.

  • Используй ассоциативность для ускорения вычислений. Если ты умножаешь несколько матриц подряд и одна из промежуточных матриц значительно меньше остальных, посчитай сначала произведение с ней — как показало задание 28, порядок группировки скобок может сократить число операций в разы.

  • Проверяй свойства на маленьких числах, прежде чем доверять формуле. Не уверен, что $(A+B)C = AC+BC$ применимо в твоей задаче? Возьми матрицы $2\times2$ с простыми числами и проверь за минуту — это быстрее, чем искать ошибку в производных вычислениях позже.

  • В коде всегда печатай .shape до и после матричного умножения. Это не "для новичков" — так делают и опытные ML-инженеры, потому что 90% ошибок в архитектурах нейросетей — это именно несовпадение размеров, а не концептуальная ошибка в идее модели.

  • Помни: единичная матрица $I$ — это "матричная единица". Если тебе нужно проверить результат сложного вычисления, попробуй частный случай с $I$ вместо одной из матриц — умножение на неё не должно ничего менять, и если результат "поехал", ошибка где-то в вычислениях, а не в логике.


Операции над матрицами — это не абстрактное упражнение "для сдачи зачёта". Это ровно тот набор действий, который твой компьютер выполняет прямо сейчас, пока ты читаешь этот текст — если открыт браузер с рекомендациями, работает голосовой ассистент или запущена любая модель машинного обучения. Освоив сложение, умножение на число и, самое главное, умножение "строка на столбец" — вместе с его капризным свойством некоммутативности — ты закрыл важнейший блок фундамента линейной алгебры. Дальше будет только интереснее: определители, обратные матрицы и собственные значения раскроют, что матрицы умеют не просто хранить и преобразовывать числа, а рассказывать о самой структуре линейных пространств. До встречи в следующем уроке!

Понял тему? Закрепи в боте! 🚀

Попрактикуйся на задачах и получи персональные рекомендации от AI

💪 Начать тренировку
💬 Есть вопрос? Спроси бота!