Умножение массива Numpy вдоль определенной оси

Я хочу умножить два массива numpy разной формы по определенным осям, не меняя их местами вручную и не добавляя «фиктивные» измерения, если это возможно.

Для двух массивов, скажем, A формы (3,2,2) и B формы (3), я хочу получить продукт формы np.swapaxes(np.swapaxes(A,0,2)*B),0,2) или альтернативно A*B[:,None,None] Однако для больших массивов оба метода, похоже, увеличивают заметное время вычислений по сравнению с начальной инициализацией A формой (2,2,3). Однако я не могу этого сделать, потому что мне нужно, чтобы A имело форму (3,2,2) для последующей операции умножения.

Могу ли я избежать двойной замены осей или добавления размеров «Нет» для сокращения времени расчета?

Обновлено: Привет и спасибо за все ответы, вот более подробный пример того, что я пытаюсь точно вычислить:

import numpy as np
from time import time

A = np.random.rand(100,100,2,20,100)
B = np.random.rand(100)
C = np.random.rand(2,20,100)
D = np.random.rand(100,100,2,20,100)

start = time()
for k in range(98,-1,-1):
    A[k,:,:,:,:] = np.pad(C[:,:,k:],((0,0),(0,0),(0,k)), 
    constant_values=1)*np.pad(B[k:],((0,k)), 
    constant_values=1)*A[k+1,:,:,:,:] + D[k,:,:,:,:]
end = time()
print('straight forward product:    ', end-start)

start = time()
for k in range(98,-1,-1):
    A[k,:,:,:,:] = np.pad(B[k:],((0,k)), constant_values=1) 
    [:,None,None,None]*A[k,:,:,:,:]*np.pad(C[:,:,k:],((0,0),(0,0), 
    (0,k)), constant_values=1)*A[k+1,:,:,:,:] + D[k,:,:,:,:]
end = time()
print('None Dummy-Dimensions:   ', end-start)

второй метод занимает у меня почти вдвое больше времени, но я думаю, что теперь это связано с порядком операторов умножения, а не с тем, какая ось на какую ось умножается.

edit2: Я знаю, что результаты этих двух вычислений не совпадают. В первом случае просто B и C нужно было умножить на A по 5-й оси, поэтому порядок не имел значения, и я случайно выбрал этот. В следующем случае B нужно было умножить на A вдоль 2-й оси, поэтому я изменил порядок умножения, не думая, что это повлияет на производительность, я подозревал, что потеря производительности происходит из-за нотации B[:,None,None,None] .

Что не так с A*B[:,None,None]? Я не уверен, что вы можете сделать что-то проще этого. Вы выполняете поэлементное умножение, а не обычное матричное умножение.

jared 15.05.2024 16:22

Можете ли вы предоставить фрагмент кода с правильно определенными A и B?

Boubacar Traoré 15.05.2024 16:23

Ничего плохого в A*B[:,None,None] как таковом нет, это просто стоит мне времени, которое я хотел бы сэкономить на расчетах.

Sebastian 15.05.2024 16:41

Думаю, можно просто взять np.ones((3,2,2)) для A и np.ones((3)) для B.

Sebastian 15.05.2024 16:42

Для каких размеров вы видите увеличение времени? B[:,None,None] делает view, что, как показывает обычный опыт, занимает незначительное время. Но размещение очень большого измерения первым, а не последним может привести к разнице во времени из-за проблем с кэшированием/подкачкой памяти.

hpaulj 15.05.2024 16:55

На практике моя форма A примерно равна (2,20,100,100,100), а B из (100), операцию умножения также пришлось выполнять много раз, например, от 100 до 1000 раз, когда B в моем случае нужно умножить на третью ось.

Sebastian 15.05.2024 16:58
Почему в 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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
6
83
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

В моих быстрых тестах умножение по третьей оси на самом деле происходит быстрее, чем по последней.

In [135]: A=np.ones((2,2,100,100,100)); B=np.ones((100))

In [136]: timeit A*B
17.9 ms ± 333 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [137]: timeit A*B[:,None,None]
16 ms ± 1.75 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)

эйнсум

Другой ответ предложил einsum. Я не вижу никаких улучшений

In [138]: timeit np.einsum('ijklm,k->ijklm',A,B)
22.7 ms ± 576 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [139]: timeit np.einsum('ijklm,m->ijklm',A,B)
22.8 ms ± 2.31 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

Спасибо за тестирование. Я как-то недоумеваю, почему нет разницы во времени расчета. Я проверю это еще раз, когда завтра вернусь на работу, чтобы посмотреть, не пропустил ли я что-то еще.

Sebastian 15.05.2024 21:26

Что касается einsum, я выше отметил, что подозреваю, что это другая операция — сокращение тензора. Имеет ли это смысл, если в вашем тесте потребуется больше времени, например, из-за неявной операции суммирования.

Sebastian 15.05.2024 21:36

Хотя einsum часто используется для обозначения сокращения, при таком индексировании он ничего sum-of-products не делает. Посмотрите print(np.einsum_path('ijklm,m->ijklm',A,B)[1]). То же самое ijklm 5d как слева, так и справа.

hpaulj 15.05.2024 21:43

@hpaulj, возможно, вы сможете немного повысить скорость, если при выполнении A*B измените массив на формат Fortran/колонок.

Mercury 16.05.2024 08:43

@Меркурий. Это именно то, что проверяет первый тест.

Mad Physicist 16.05.2024 16:08

вы можете попробовать использовать функцию библиотеки einsum numpy, которая работает намного быстрее,

import numpy as np
A, B = np.random.rand(3, 2, 2), np.random.rand(3)

%%timeit
np.einsum('ijk,i->ijk', A, B)
>>> 1.19 µs ± 22.3 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)

На первый взгляд, глядя на функцию einsum, я не уверен, что это то же самое, что и умножение, о котором я упоминал. Разве формы впоследствии не станут другими из-за суммирования? Я ожидаю, что результат вашего примера будет иметь форму (2,2) вместо (3,2,2), не так ли?

Sebastian 15.05.2024 21:31

@Себастьян, ты путаешь это с "ijk,i->jk". Но это «ijk,i->ijk». Оно останется (3, 2, 2).

Mercury 16.05.2024 08:30

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