Как выполнить умножение матриц под numpy в более высоких измерениях?

Под numpy я хочу выполнить «обычное» умножение матриц следующим образом:

С=А*Б

где

A — матрица «2D-типа», но каждый элемент матрицы имеет форму (1,5).

и

B — вектор «1D-типа», но каждый элемент векторного элемента имеет форму (20,5).

Результат

C должен быть вектором «1D-типа», но каждый элемент векторного элемента снова имеет форму (20,5).

Я попытался создать элементы C1 и C2 из C вручную:

>>> A.shape
(2, 2, 1, 5)
>>> B.shape
(2, 20, 5)
>>> C0 = A[0,0]*B[0]+A[0,1]*B[1]
>>> C0.shape
(20, 5)
>>> C1 = A[1,0]*B[0]+A[1,1]*B[1]
>>> C1.shape
(20, 5)
>>>

Трансляция (1,5) из A с (20,5) из B работает как положено.

Однако мне не удалось выяснить, как это можно записать как умножение матриц:

C = np.matmul(A, B)

Конечно, это не работает, потому что numpy не может знать, какие индексы я хочу суммировать. Но я предполагаю, что должно существовать какое-то простое «нумерпитоническое» решение...

A*B НЕ является «обычным» матричным умножением (которое будет A @ B или A.dot(B)), это поэлементное умножение. Чего вы на самом деле хотите?
Julien 15.05.2024 03:19

Также создайте минимальный воспроизводимый пример с фактическими ожидаемыми значениями.

Julien 15.05.2024 03:21

В любом случае решение, скорее всего, будет включать np.einsum

Julien 15.05.2024 03:23
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
3
57
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы можете использовать einsum:

C = np.einsum('ij...,j...->i...', A, B)

Это сжатие второго измерения А с первым измерением Б. Остальные измерения переносятся вместе с нами.

Пример в сеансе ipython:

In [114]: rng = np.random.default_rng(8779780912870439733)

In [115]: A = rng.integers(0, 5, size=(2, 2, 1, 5))

In [116]: B = rng.integers(0, 5, size=(2, 20, 5))

In [117]: C = np.einsum('ij...,j...->i...', A, B)

In [118]: C.shape
Out[118]: (2, 20, 5)

In [119]: C0 = A[0,0]*B[0]+A[0,1]*B[1]

In [120]: C1 = A[1,0]*B[0]+A[1,1]*B[1]

In [121]: np.all(C[0] == C0)
Out[121]: True

In [122]: np.all(C[1] == C1)
Out[122]: True

Это сработало немедленно. Спасибо.

MichaelW 15.05.2024 23:12

Вы можете напрямую умножить A и B и суммировать второе измерение:

C = (A*B).sum(1)

# or
C = np.multiply(A, B).sum(1)

C.shape
# (2, 20, 5)

Это дает тот же результат, что и ваши ручные вычисления:

np.allclose(C[0], C0)
# True

np.allclose(C[1], C1)
# True

Это работает, поскольку numpy выравнивает массивы справа:

A (2, 2,  1, 5)
   |  |   |  |   # multiplication
B    (2, 20, 5)
B (1, 2, 20, 5)  # broadcasting
   |      |  |   # sum(1)
C (2,    20, 5)

Обратите внимание, что это эквивалентно скалярному произведению, используя:

C = np.matmul(B[:, None].T, A.T).T[:, 0]

# or
C = (B[:, None][email protected]).T[:, 0]

Ваш matmul можно объяснить так: B меняется с (2,20,5) на (2,1,20,5) на (5,20,1,2). A.T это (5,1,2,2). Затем dot на последних 2 измерениях сделать (1,2), и транслировать на ведущие 2, чтобы сделать (5,20,1,2). Транспонируйте и уберите 1, чтобы получить (2,20,5).

hpaulj 15.05.2024 08:04

Точно, спасибо за подробное описание @hpaulj, я показал эту альтернативу, чтобы продемонстрировать, что matmul можно использовать, хотя здесь это не совсем практично.

mozway 15.05.2024 08:15

Другие вопросы по теме