NumPy: вычисления и устройство массивов

Матрично-векторные произведения через оператор @

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

Правило согласования внутренних размеров

Прежде чем запускать @, полезно уметь предсказать результат только по форме операндов — без вычислений.

В этом уроке мы работаем с одно- и двумерными массивами, и для них правило звучит так: последняя ось левого операнда должна совпадать с предпоследней (или единственной, если правый операнд — вектор) осью правого. Именно эти два числа называют «внутренними размерами» — они как бы смотрят друг на друга в записи (m, k) @ (k, n). Если они равны, операция пройдёт. Если нет — ValueError.

Shape результата определяется «внешними» размерами — теми, что остались после сокращения внутренних:

  • (m, k) @ (k, n)(m, n) — внутренний размер k «схлопывается», остаётся (m, n)
  • (m, k) @ (k,)(m,) — вектор shape (k,) не имеет второго измерения, поэтому результат одномерный
  • (n,) @ (n,) → скаляр shape () — оба измерения «схлопываются», ничего не остаётся

Чтобы проверить совместимость до запуска, достаточно выписать оба shape и мысленно поставить их рядом:

(2, 3) @ (3, 4)  →  внутренние: 3 == 3 ✓  →  результат (2, 4)
(2, 3) @ (3,)   →  внутренние: 3 == 3 ✓  →  результат (2,)
(2, 3) @ (2, 3) →  внутренние: 3 ≠ 2   ✗  →  ValueError

Обратите внимание: @ — не поэлементная операция и не broadcasting в том смысле, который мы разбирали в прошлом уроке. Правило внутренних размеров работает независимо от того, насколько похожи формы массивов в целом. Матрицы (2, 3) и (2, 3) имеют одинаковый shape — но перемножить их через @ нельзя, потому что внутренние размеры 3 и 2 не совпадают.

Примечание. Для трёхмерных и выше массивов np.matmul применяет то же правило к двум последним осям каждого операнда, а ведущие («пакетные») измерения согласуются по правилам broadcasting. Например, (5, 2, 3) @ (5, 3, 4) корректно даёт (5, 2, 4). В этом уроке мы не углубляемся в пакетное умножение — достаточно знать, что оно существует и работает согласованно с изученным правилом.