Умножить массив матриц на массив векторов в Numpy

У меня есть массив матриц A формы A.shape = (N, 3, 3) и массив векторов V формы V.shape = (N, 3). Я хочу получить массив (N, 3), где каждый вектор является результатом умножения i-й матрицы на i-й вектор. Такой как:

result = [A[i] @ V[i] for i in range(N)]

Есть ли способ сделать это без зацикливания с помощью NumPy? В идеале мне бы также хотелось, чтобы это работало с массивами более высоких размерностей, например A.shape = (N, M, 3, 3) и V.shape = (N, M, 3, 3).

Обратите внимание, что Numpy неэффективен для работы с матрицами 3x3. Нативный код или код Numba может работать намного быстрее, чем Numpy. Я думаю, что использование лучшего макета памяти, например 3x3xNxM, может сделать это еще быстрее (как с Numba, так и с Numpy).

Jérôme Richard 12.08.2024 14:02

Я только что реорганизовал весь свой код, чтобы иметь такое расположение памяти из-за правил трансляции numpy. Насколько я понимаю, numpy всегда использует последние измерения для работы с трансляцией ((N, M, 3) + (3,) нормально, но (3, N, M) + (3,) выдает ошибку). Почему другой макет быстрее?

Alex V. 12.08.2024 16:23

Потому что он более дружелюбен к SIMD. Модули SIMD на современных процессорах x86-64 обычно могут вычислять 16 32-битных чисел с плавающей запятой за цикл или 8 64-битных чисел с плавающей запятой. При нынешней компоновке можно использовать только часть модулей SIMD (всего 3 шт.). Что касается Numpy, он не оптимизирован для работы на оси с небольшим количеством элементов (и, конечно, никогда не будет).

Jérôme Richard 12.08.2024 19:27
Почему в 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
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Я бы рекомендовал использовать np.einsum(). Это позволяет эффективно умножать матрицы по нескольким измерениям без явных циклов. (ссылка)

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

import numpy as np

def matrix_vector_multiply(A, V):
    return np.einsum('...ij,...j->...i', A, V)

Это решение работает как для случая, когда A.shape = (N, 3, 3) и V.shape = (N, 3), так и для более высоких измерений, таких как A.shape = (N, M, 3, 3) и V.shape = (N, M, 3).

Я протестировал вышеуказанную функцию и сравнил ее с вашим зацикленным решением:

N = 5
A1 = np.random.rand(N, 3, 3)
V1 = np.random.rand(N, 3)

result1_loop = np.array([A1[i] @ V1[i] for i in range(N)])
result1_einsum = matrix_vector_multiply(A1, V1)

print("Loop result shape:", result1_loop.shape)
print("Einsum result shape:", result1_einsum.shape)
print("Results match:", np.allclose(result1_loop, result1_einsum))

Вот результат:

Форма результата цикла: (5, 3) Форма результата Einsum: (5, 3) Результаты совпадают: Истинный

Этот пост также поможет лучше понять einsum

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