Нашли ошибку?
продвинутыйNumPy 2.5.3

Матричное умножение

Оператор @ и функции dot, inner, outer — чем матричное произведение отличается от поэлементного.

Нужно знать:Арифметика с массивамиОси: T, newaxis, squeeze

Содержание статьи

Коротко

  • a @ b — матричное произведение: (n, k) @ (k, m) → (n, m). Внутренние размеры должны совпадать.
  • a * b — поэлементное умножение, это другое.
  • Вектор на вектор через @ — скалярное произведение (число); матрица на вектор — вектор.
  • Для массивов 3D и больше @ умножает матрицы пачкой по последним двум осям, а первые оси транслирует. np.dot для них ведёт себя иначе — в новом коде используйте @.
  • np.outer(x, y) — все попарные произведения, np.inner / np.dot для векторов — скалярное произведение.

Синтаксис

a @ b                 # = np.matmul(a, b)
np.dot(x, y)          # для векторов и матриц совпадает с @
np.inner(x, y)        # скалярное произведение векторов
np.outer(x, y)        # (n,) и (m,) → (n, m)
Формы a @ b
(n, k) @ (k, m) (n, m)
(n, k) @ (k,) (n,)
(k,) @ (k,) число
(b, n, k) @ (k, m) (b, n, m) — пачка матриц

Примеры

Данные для примеровиспользуются во всех примерах ниже
# цены 3 товаров в 2 магазинах и заказы: 2 покупателя × 3 товара
prices = np.array([[120, 110],
                   [ 80,  85],
                   [ 45,  40]])      # (3 товара, 2 магазина)
orders = np.array([[1, 2, 0],
                   [3, 0, 4]])       # (2 покупателя, 3 товара)
Стоимость заказов в магазинах
orders @ prices                      # (2, 3) @ (3, 2) → (2, 2): стоимость заказа в каждом магазине
Вывод
array([[280, 280],
       [540, 490]])
* и @
a = np.array([[1, 2], [3, 4]])
print(a * a)                         # поэлементно
a @ a                                # матричное произведение
Вывод
[[ 1  4]
 [ 9 16]]
array([[ 7, 10],
       [15, 22]])
Матрица на вектор, вектор на вектор
v = np.array([1, 0, 2])
print(orders @ v)                    # матрица на вектор → вектор (2,)
v @ v                                # вектор на вектор → число (скалярное произведение)
Вывод
[ 1 11]
np.int64(5)
Скалярное и внешнее произведение
x = np.array([1, 2, 3])
y = np.array([4, 5, 6])
print(np.dot(x, y), np.inner(x, y))  # скалярное произведение
np.outer(x, y)                       # все попарные произведения (3, 3)
Вывод
32 32
array([[ 4,  5,  6],
       [ 8, 10, 12],
       [12, 15, 18]])
Пачка матриц
batch = np.ones((10, 2, 3))          # 10 матриц 2 × 3
(batch @ prices).shape               # каждая умножается на prices
Вывод
(10, 2, 2)

Подводные камни

Внутренние размеры не совпадают

Проверьте формы: у (2, 3) @ (2, 3) внутренние размеры 3 и 2. Скорее всего, нужна транспонированная матрица: orders @ orders.T.

Несовместимые формыошибка: ValueError
orders @ orders                      # (2, 3) @ (2, 3): 3 ≠ 2
Ошибка
ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 2 is different from 3)

np.dot и @ для многомерных массивов

Результат для 3D-массивов
a = np.ones((2, 3, 4))
b = np.ones((2, 4, 5))
print((a @ b).shape)                 # попарно по первой оси
np.dot(a, b).shape                   # np.dot для 3D делает совсем другое
Вывод
(2, 3, 5)
(2, 3, 2, 5)

@ с числом

Число @ матрицаошибка: ValueError
2 @ orders                           # на число умножают через *
Ошибка
ValueError: matmul: Input operand 0 does not have enough dimensions (has 0, gufunc core with signature (n?,k),(k,m?)->(n?,m?) requires 1)