Я понимаю, что transpose
в ndarray
предназначен для эквивалентности функции matlab permute
, но у меня есть конкретная утилита, которая не работает просто. В Matlab у меня есть следующее:
C = @bsxfun(@times, permute(A,[4,2,5,1,3]), permute(B, [1,6,2,7,3,4,5])
где A - трехмерный тензор формы NxNxM, а B - тензор 5D формы NxNxMxPxP. Вышеупомянутая функция предназначена для векторизации петлевых кронекеров. Я предполагаю, что Matlab добавляет 2 синглтонных измерения для A и B, поэтому он может их переупорядочить. Я ищу, чтобы переносить этот код на Python , но я не думаю, что он имеет возможность добавлять эти дополнительные измерения.. Я нашел this, который успешно добавляет дополнительные размеры, однако широковещательная передача не работает с тем же самым matlab bsxfun
. Я попытался сделать очевидный перевод (да, я использую numpy для этих ndarray
и функций):
A = A[...,None,None]
B = B[...,None,None]
C = transpose(A,[3,1,4,0,2])*transpose(B,[0,5,1,6,2,3,4])
, и я получаю следующую ошибку:
return transpose(axes)
ValueError: axes don't match array
Мое первое предположение заключается в том, чтобы сделать reshape
на A и B, чтобы добавить в эти размеры синглтона?
Теперь я получаю следующую ошибку:
mults = transpose(rho_in,[3,1,4,0,2])*transpose(proj,[0,5,1,6,2,3,4])
ValueError: operands could not be broadcast together with shapes (1,9,1,9,8) (9,1,9,1,8,40,40)
EDIT: изменил мой вопрос как минимум о добавлении размеров синглтона, но больше о правильном распространении этого умножения на matlab в python.
Огромная разница между MATLAB и numpy заключается в том, что первый использует формат столбца для своих массивов, в то время как последний ряд строк. Следствием этого является то, что неявные размеры одноэлементности обрабатываются по-разному.
В частности, MATLAB явно игнорирует конечные размеры синглтона: rand(3,3,1,1,1,1,1)
- фактически матрица 3x3
. Вдоль этих строк вы можете использовать bsxfun
для работы на двух массивах, если их ведущие измерения совпадают: NxNxM
неявно NxNxMx1x1
, который совместим с NxNxMxPxP
.
Numpy, с другой стороны, позволяет неявные синглтоны впереди. Вам нужно permute
ваши массивы таким образом, чтобы их конечные размеры совпадали, например shape (40,40,9,1,9,1,8)
с формой (1,9,1,9,8)
, и результат должен иметь форму (40,40,9,9,9,9,8)
.
Пример манекена:
>>> import numpy as np
>>> (np.random.rand(40,40,9,1,9,1,8)+np.random.rand(1,9,1,9,8)).shape
(40, 40, 9, 9, 9, 9, 8)
Обратите внимание, что то, что вы пытаетесь сделать, возможно, можно сделать с помощью numpy.einsum
. Я предлагаю поближе рассмотреть это. Пример того, что я имею в виду: из вашего вопроса, который я собрал, вы хотите выполнить это: взять элементы A[1:N,1:N,1:M]
и B[1:N,1:N,1:M,1:P,1:P]
и построить новый массив C[1:N,1:N,1:N,1:N,1:M,1:P,1:P]
, чтобы
C[i1,i2,i3,i4,i5,i6,i7] = A[i2,i4,i5]*B[i1,i3,i5,i6,i7]
(ваш индексный порядок может отличаться). Если это правильно, вы действительно можете использовать numpy.einsum()
:
>>> a = np.random.rand(3,3,2)
>>> b = np.random.rand(3,3,2,4,4)
>>> np.einsum('ijk,lmkno->limjkno',a,b).shape
(3, 3, 3, 3, 2, 4, 4)
Следует отметить две вещи. Во-первых, вышеупомянутая операция будет очень интенсивной в памяти, что следует ожидать для случаев векторизации (где вы обычно выигрываете процессорное время за счет потребности в памяти). Во-вторых, вам следует серьезно подумать о перестановке модели данных при переносе кода. Причина, по которой трансляция работает по-разному на двух языках, неразрывно связана с разницей в столбце/главном. Это также означает, что в MATLAB сначала нужно работать с ведущими индексами, поскольку A(:,i2,i3)
соответствует непрерывному блоку памяти, а A(i1,i2,:)
- нет. И наоборот, в numpy A[i1,i2,:]
является смежным, а A[:,i2,i3]
- нет.
Эти соображения предполагают, что вы должны настроить логистику своих данных таким образом, чтобы векторизованные операции предпочтительно работали с ведущими индексами в MATLAB и возвращали индексы в numpy. Вы могли бы использовать numpy.einsum
для выполнения самой операции, однако ваши измерения должны быть в другом (возможно, обратном) порядке по сравнению с MATLAB, по крайней мере, если предположить, что обе версии кода используют оптимальную настройку.
Глядя на ваш код MATLAB, у вас есть -
C = bsxfun(@times, permute(A,[4,2,5,1,3]), permute(B, [1,6,2,7,3,4,5])
Итак, по существу -
B : 1 , 6 , 2 , 7 , 3 , 4 , 5
A : 4 , 2 , 5 , 1 , 3
Теперь, в MATLAB нам пришлось брать размеры синглтона из более высоких, поэтому вся эта проблема приносит тусклые 6
, 7
для B
и dims 4
5
для A
.
В NumPy мы вносим их явно с помощью np.newaxis
/None. Таким образом, для NumPy мы могли бы сделать это так:
B : 1 , N , 2 , N , 3 , 4 , 5
A : N , 2 , N , 1 , 3 , N , N
где N
представляет новую ось. Обратите внимание, что нам нужно было поставить новые топоры в конце для A
, чтобы продвинуть другое измерение для выравнивания. В режиме constrast это происходит в MATLAB по умолчанию.
Создание B
выглядит достаточно просто, поскольку размеры кажутся в порядке, и нам просто нужно добавить новые оси в соответствующих местах - B[:,None,:,None,:,:,:]
.
Создание такого A
не выглядит прямым. Игнорируя N's
в A
, мы имели бы - A : 2 , 1 , 3
. Таким образом, отправной точкой будут перестановки размеров, а затем добавить в эти две новые оси, которые были проигнорированы - A.transpose(1,0,2)[None,:,None,:,:,None,None]
.
До сих пор мы имеем -
B (new): B[:,None,:,None,:,:,:]
A (new): A.transpose(1,0,2)[None,:,None,:,:,None,None]
В NumPy мы можем пропустить ведущие новые оси и конечные не-синглетонные dims. Таким образом, мы могли бы упростить так:
B (new): B[:,None,:,None]
A (new): A.transpose(1,0,2)[:,None,...,None,None]
Конечным результатом будет умножение между этими двумя расширенными версиями -
C = A.transpose(1,0,2)[:,None,...,None,None]*B[:,None,:,None]
Тест времени выполнения
Я полагаю, что сообщение @Andras означало, что эквивалентная реализация np.einsum
должна выглядеть примерно так: np.einsum('ijk,lmkno->ljmikno',A,B)
.
In [24]: A = np.random.randint(0,9,(10,10,10))
...: B = np.random.randint(0,9,(10,10,10,10,10))
...:
In [25]: C1 = np.einsum('ijk,lmkno->ljmikno',A,B)
In [26]: C2 = A.transpose(1,0,2)[:,None,...,None,None]*B[:,None,:,None]
In [27]: np.allclose(C1,C2)
Out[27]: True
In [28]: %timeit np.einsum('ijk,lmkno->ljmikno',A,B)
10 loops, best of 3: 102 ms per loop
In [29]: %timeit A.transpose(1,0,2)[:,None,...,None,None]*B[:,None,:,None]
10 loops, best of 3: 78.4 ms per loop
In [30]: A = np.random.randint(0,9,(15,15,15))
...: B = np.random.randint(0,9,(15,15,15,15,15))
...:
In [31]: %timeit np.einsum('ijk,lmkno->ljmikno',A,B)
1 loop, best of 3: 1.76 s per loop
In [32]: %timeit A.transpose(1,0,2)[:,None,...,None,None]*B[:,None,:,None]
1 loop, best of 3: 1.36 s per loop
einsum
:)
einsum
)? Я чувствую, что мои дальнейшие вопросы были бы слишком конкретными, чтобы быть актуальными здесь.