I want to multiply a 3D tensor with a 2D tensor and get a 2D tensor as result. The way I envision this to be done is rather strange, so I ask for your help.
The 3D tensor, for when we fix the value of the first dimension to some integer within range(3D_tensor.shape[0])
, is a 2D matrix. We picture this 2D matrix as:
``[[a0, b0, c0], [d0, e0, f0], [g0, h0, p0]]``
I want this to be multiplied by the 2D tensor (see above) which can be pictured as:
``[[10, 20, 30], [40, 50, 60], [70, 80, 90]]``
The multiplication shall not be a matrix-matrix multiplication, but each row from the (left ) 2D matrix to be multiplied, element wise, with the corresponding row from the 2D tensor. The result shall be a 3 element array:
``[a0 * 10 b0 * 20 c0 * 30, d0 * 40, e0 * 50, f0 * 60, g0 * 70 h0 * 80 p0 * 90]``.
This multiplication procedure repeats for all the other "slices" of the 3D tensor. That is, if the 2D matrix picture above was for 3D_tensor[0, :, :]
, then we would have
``[[a1, b1, c1], [d1, e1, f1] ,[g1, h1, p1]]``
for 3D_tensor[1, :, :]
, and the result shall be
``[a1 * 10 b1 * 20 c1 * 30, d1 * 40 e1 * 50 f1 * 60, g1 * 70 h1 * 80 p1 * 90]``.
These 3-element 1D arrays shall be glued together into a bigger 2D array (the result I want to obtain), where each 3-element 1D array to be one of the 2D array's rows (so place them horizontally). That would be, my final result in the pictures above, is a 2D array:
``[ [a0 * 10 b0 * 20 c0 * 30, d0 * 40, e0 * 50, f0 * 60, g0 * 70 h0 * 80 p0 * 90], [a1 * 10 b1 * 20 c1 * 30, d1 * 40 e1 * 50 f1 * 60, g1 * 70 h1 * 80 p1 * 90] ] ``
Ideally, I want this to be done with the BLAS
routines, but using numpy
. Is what I want to obtain possible?
I know about np.dot, multiply, @, ... , and tried to use them, but I cannot figure how I can do the above, EFFICIENTLY, myself.
Thank you!
CodePudding user response:
Use:
import numpy as np
tensor3d = np.arange(27).reshape((3, 3, 3))
tensor2d = np.array([[10, 20, 30], [40, 50, 60], [70, 80, 90]])
res = (tensor3d * tensor2d).sum(2)
print(res)
Output
[[ 80 620 1700]
[ 620 1970 3860]
[1160 3320 6020]]
The expression:
tensor3d * tensor2d
returns:
[[[ 0 20 60]
[ 120 200 300]
[ 420 560 720]]
[[ 90 200 330]
[ 480 650 840]
[1050 1280 1530]]
[[ 180 380 600]
[ 840 1100 1380]
[1680 2000 2340]]]
basically element-wise multiplication, as requested. Then:
.sum(2)
sums the results across the third axis (2 for 0 based indexing). For example, for the first row of the final result:
80 = 0 20 60
620 = 120 200 300
1700 = 420 560 720
UPDATE
To do the above operation for multiple arrays do:
import numpy as np
tensor3d = np.array([np.arange(27).reshape((3, 3, 3)), np.arange(27).reshape((3, 3, 3))])
tensor2d = np.array([np.array([[10, 20, 30], [40, 50, 60], [70, 80, 90]]),
np.array([[10, 20, 30], [40, 50, 60], [70, 80, 90]])])
tensor2d = tensor2d[:, np.newaxis, ...]
res = (tensor3d * tensor2d).sum(3)
print(res)
Output
[[[ 80 620 1700]
[ 620 1970 3860]
[1160 3320 6020]]
[[ 80 620 1700]
[ 620 1970 3860]
[1160 3320 6020]]]
In this example:
tensor3d.shape = (2, 3, 3, 3) # two 3d tensors
tensor2d.shape = (2, 1, 3, 3) # two 2d matrices
res.shape = (2, 3, 3) # two 2d matrices
note that the original shape of tensor2d
is (2, 3, 3)
but in order for broadcasting to work you need to add an extra dimension, hence the expression:
tensor2d = tensor2d[:, np.newaxis, ...]
CodePudding user response:
I suspect this is largely a repeat of Dani's answer, but I just want to confirm my understanding of your question.
Making a more concrete version of your 3d array:
In [340]: A = np.arange(2*3*3).reshape(2,3,3); B = np.arange(10,100,10).reshape(3,3)
In [341]: A
Out[341]:
array([[[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8]],
[[ 9, 10, 11],
[12, 13, 14],
[15, 16, 17]]])
In [342]: B
Out[342]:
array([[10, 20, 30],
[40, 50, 60],
[70, 80, 90]])
In [343]: (A*B).shape
Out[343]: (2, 3, 3)
It looks like you just want to multiply the 2 arrays, taking advantage of broadcasting
, and sum along the last dimension (across columns):
In [344]: (A*B).sum(axis=2) # axis=-1 for last
Out[344]:
array([[ 80, 620, 1700],
[ 620, 1970, 3860]])
So for one plane of A
and one row:
In [345]: (A[0,0,:]*B[0]).sum()
Out[345]: 80
In general if A
is (N,3,3) and B
is (3,3), then A*B
is also (N,3,3), and the sum
reduces it on the last dimension, producing a (N,3) result.
This can also be expressed as a matrix product with einsum
:
In [352]: np.einsum('ijk,jk->ij',A,B)
Out[352]:
array([[ 80, 620, 1700],
[ 620, 1970, 3860]])
matmul/@
can also do it, but getting mix of dimensions right is trickier.
If A
is (N,M,3,3) and B
is (M,3,3), A*B.sum(axis=-1)
produces (N,M,3).
In broadcasting
the B
is expanded to (1,M,3,3) and then to (N,M,3,3) to match A
.