Что такое оператор умножения, который действительно работает с массивами numpy?
Я изучаю NumPy, и я не уверен, что делает оператор *
. Это похоже на некоторую форму умножения, но я не уверен, как это определяется. Из ipython:
In [1]: import numpy as np
In [2]: a=np.array([[1,2,3]])
In [3]: b=np.array([[4],[5],[6]])
In [4]: a*b
Out[4]:
array([[ 4, 8, 12],
[ 5, 10, 15],
[ 6, 12, 18]])
In [5]: b*a
Out[5]:
array([[ 4, 8, 12],
[ 5, 10, 15],
[ 6, 12, 18]])
In [6]: b.dot(a)
Out[6]:
array([[ 4, 8, 12],
[ 5, 10, 15],
[ 6, 12, 18]])
In [7]: a.dot(b)
Out[7]: array([[32]])
Кажется, что он выполняет умножение матрицы, но только b
, умноженное на a
, а не наоборот. Что происходит?
Ответы
Ответ 1
Это немного сложнее и связано с концепцией broadcasting и тем фактом, что все операции numpy являются элементальными.
-
a
- это 2D-массив с 1 строкой и 3 столбцами, а b
- это 2D-массив с 1 столбцом и 3 строками.
- Если вы пытаетесь умножить их на элемент (это то, что делает numpy, если вы делаете
a * b
, потому что каждая базовая операция, кроме операции dot
, является мудрой), она должна транслировать массивы так, чтобы они соответствовали во всех их измерениях.
- Поскольку первый массив равен 1x3, а второй - 3x1, они могут быть переданы в матрицу 3x3 согласно правилам вещания. Они будут выглядеть так:
a = [[1, 2, 3],
[1, 2, 3],
[1, 2, 3]]
b = [[4, 4, 4],
[5, 5, 5],
[6, 6, 6]]
И теперь Numpy может размножать их по элементам, давая вам результат:
[[ 4, 8, 12],
[ 5, 10, 15],
[ 6, 12, 18]]
Когда вы выполняете операцию .dot
, она выполняет стандартное умножение матрицы. Подробнее в docs
Ответ 2
*
делает элементное умножение.
Поскольку массивы имеют разные формы, будут применяться правила broadcasting.
In [5]: a.shape
Out[5]: (1, 3)
In [6]: b.shape
Out[6]: (3, 1)
In [7]: (a * b).shape
Out[7]: (3, 3)
- Все входные массивы с ndim меньше, чем входной массив наибольшего ndim, имеют 1s, добавленные к их формам (здесь не применяется).
- Размер в каждом измерении выходной формы является максимальным для всех размеров ввода в этом измерении.
- В вычислениях может использоваться вход, если его размер в определенном измерении либо соответствует размеру вывода в этом измерении, либо имеет значение точно 1.
- Если вход имеет размер размером 1 в его форме, первая запись данных в этом измерении будет использоваться для всех вычислений вдоль этого измерения. Другими словами, степпинг ufunc просто не будет продвигаться по этому измерению (шаг будет равен 0 для этой размерности).
Таким образом, результирующая фигура должна быть (3, 3)
(максимальные размеры размеров a
и b
), а при выполнении умножения numpy не будет проходить первый размер и b второй размер (их размеры равны 1).
Элемент результата [i][j]
равен произведению широковещательного элемента a
и b
[i][j]
.
(a * b)[0][0] == a[0][0] * b[0][0]
(a * b)[0][1] == a[0][1] * b[0][0] # (not stepping through b second dimension)
(a * b)[0][2] == a[0][2] * b[0][0]
(a * b)[1][0] == a[0][0] * b[1][0] # (not stepping through a first dimension)
etc.