Related
I'm trying to vectorize a slice assignment of the form
for i in range(a.shape[1]):
for j in range(a.shape[2]):
a[:,i,j,:,i:i+b.shape[2],j:j+b.shape[3]] = b
where b itself is an array. This is because the nested Python loop is too inefficient and is taking up most of the runtime. Is there a way to do this?
For a simpler case, consider the following:
for i in range(a.shape[1]):
a[:,i,:,i:i+b.shape[2]] = b
This is what b and a might look like:
You can see the diagonal, "sliding" structure of the resulting matrix.
We can leverage np.lib.stride_tricks.as_strided based scikit-image's view_as_windows to get sliding windowed views into a 0s padded version of the input and being a view would be efficient on memory and performance. More info on use of as_strided based view_as_windows.
Hence, for the simpler case, it would be -
from skimage.util.shape import view_as_windows
def sliding_2D_windows(b, outshp_axis1):
# outshp_axis1 is desired output's shape along axis=1
n = outshp_axis1-1
b1 = np.pad(b,((0,0),(0,0),(n,n)),'constant')
w_shp = (1,b1.shape[1],b.shape[2]+n)
return view_as_windows(b1,w_shp)[...,0,::-1,0,:,:]
Sample run -
In [192]: b
Out[192]:
array([[[54, 57, 74, 77],
[77, 19, 93, 31],
[46, 97, 80, 98]],
[[98, 22, 68, 75],
[49, 97, 56, 98],
[91, 47, 35, 87]]])
In [193]: sliding_2D_windows(b, outshp_axis1=3)
Out[193]:
array([[[[54, 57, 74, 77, 0, 0],
[77, 19, 93, 31, 0, 0],
[46, 97, 80, 98, 0, 0]],
[[ 0, 54, 57, 74, 77, 0],
[ 0, 77, 19, 93, 31, 0],
[ 0, 46, 97, 80, 98, 0]],
[[ 0, 0, 54, 57, 74, 77],
[ 0, 0, 77, 19, 93, 31],
[ 0, 0, 46, 97, 80, 98]]],
[[[98, 22, 68, 75, 0, 0],
[49, 97, 56, 98, 0, 0],
[91, 47, 35, 87, 0, 0]],
....
[[ 0, 0, 98, 22, 68, 75],
[ 0, 0, 49, 97, 56, 98],
[ 0, 0, 91, 47, 35, 87]]]])
Assuming b has a shape (2,3,x1), and a has a shape (2,x2-x1+1,3,x2). In your screenshot, we can infer that x1=4, x2=6.
import numpy as np
b_shape = (2,3,4)
a_shape = (2,3,3,6)
b = np.arange(1,25).reshape(b_shape)
#array([[[ 1, 2, 3, 4],
# [ 5, 6, 7, 8],
# [ 9, 10, 11, 12]],
#
# [[13, 14, 15, 16],
# [17, 18, 19, 20],
# [21, 22, 23, 24]]])
c = np.pad(b, (*[(0,0) for _ in range(len(b_shape[:-1]))], (0,a_shape[-1]-b_shape[-1])), 'constant')
#array([[[ 1, 2, 3, 4, 0, 0],
# [ 5, 6, 7, 8, 0, 0],
# [ 9, 10, 11, 12, 0, 0]],
#
# [[13, 14, 15, 16, 0, 0],
# [17, 18, 19, 20, 0, 0],
# [21, 22, 23, 24, 0, 0]]])
a = np.stack([np.roll(c, shift=i) for i in range(a_shape[-1]-b_shape[-1]+1)], axis=1)
# array([[[[ 1, 2, 3, 4, 0, 0],
# [ 5, 6, 7, 8, 0, 0],
# [ 9, 10, 11, 12, 0, 0]],
# [[ 0, 1, 2, 3, 4, 0],
# [ 0, 5, 6, 7, 8, 0],
# [ 0, 9, 10, 11, 12, 0]],
# [[ 0, 0, 1, 2, 3, 4],
# [ 0, 0, 5, 6, 7, 8],
# [ 0, 0, 9, 10, 11, 12]]],
# [[[13, 14, 15, 16, 0, 0],
# [17, 18, 19, 20, 0, 0],
# [21, 22, 23, 24, 0, 0]],
# [[ 0, 13, 14, 15, 16, 0],
# [ 0, 17, 18, 19, 20, 0],
# [ 0, 21, 22, 23, 24, 0]],
# [[ 0, 0, 13, 14, 15, 16],
# [ 0, 0, 17, 18, 19, 20],
# [ 0, 0, 21, 22, 23, 24]]]])
I wonder if there is a built-in operation which would free my code from Python-loops.
The problem is this: I have two matrices A and B. A has N rows and B has N columns. I would like to multiply every i row from A with corresponding i column from B (using NumPy broadcasting). The resulting matrix would form i layer in the output. So my result would be 3-dimensional array.
Is such operation available in NumPy?
One way to express your requirement directly is by using np.einsum():
>>> A = np.arange(12).reshape(3, 4)
>>> B = np.arange(15).reshape(5, 3)
>>> np.einsum('...i,j...->...ij', A, B)
array([[[ 0, 0, 0, 0, 0],
[ 0, 3, 6, 9, 12],
[ 0, 6, 12, 18, 24],
[ 0, 9, 18, 27, 36]],
[[ 4, 16, 28, 40, 52],
[ 5, 20, 35, 50, 65],
[ 6, 24, 42, 60, 78],
[ 7, 28, 49, 70, 91]],
[[ 16, 40, 64, 88, 112],
[ 18, 45, 72, 99, 126],
[ 20, 50, 80, 110, 140],
[ 22, 55, 88, 121, 154]]])
This uses the Einstein summation convention.
For further discussion, see chapter 3 of Vectors, Pure and Applied: A General Introduction to Linear Algebra by T. W. Körner. In it, the author cites an amusing passage from Einstein's letter to a friend:
"I have made a great discovery in mathematics; I have suppressed the summation sign every time that the summation must be made over an index which occurs twice..."
Yes, in it's simplest form you just add "zero" dimensions so the NumPy broadcasts along the rows of A and columns of B:
>>> import numpy as np
>>> A = np.arange(12).reshape(3, 4) # 3 row, 4 colums
>>> B = np.arange(15).reshape(5, 3) # 5 rows, 3 columns
>>> res = A[None, ...] * B[..., None]
>>> res
array([[[ 0, 0, 0, 0],
[ 4, 5, 6, 7],
[ 16, 18, 20, 22]],
[[ 0, 3, 6, 9],
[ 16, 20, 24, 28],
[ 40, 45, 50, 55]],
[[ 0, 6, 12, 18],
[ 28, 35, 42, 49],
[ 64, 72, 80, 88]],
[[ 0, 9, 18, 27],
[ 40, 50, 60, 70],
[ 88, 99, 110, 121]],
[[ 0, 12, 24, 36],
[ 52, 65, 78, 91],
[112, 126, 140, 154]]])
The result has a shape of (5, 3, 4) and you can easily move the axis around if you want a different shape. For example using np.moveaxis:
>>> np.moveaxis(res, (0, 1, 2), (2, 0, 1)) # 0 -> 2 ; 1 -> 0, 2 -> 1
array([[[ 0, 0, 0, 0, 0],
[ 0, 3, 6, 9, 12],
[ 0, 6, 12, 18, 24],
[ 0, 9, 18, 27, 36]],
[[ 4, 16, 28, 40, 52],
[ 5, 20, 35, 50, 65],
[ 6, 24, 42, 60, 78],
[ 7, 28, 49, 70, 91]],
[[ 16, 40, 64, 88, 112],
[ 18, 45, 72, 99, 126],
[ 20, 50, 80, 110, 140],
[ 22, 55, 88, 121, 154]]])
With a shape of (3, 4, 5).
I have a matrix A = [x1, x2, ..., xm] where each xi is a column vector of size [n, 1]. So A has shape [n, m]. I am trying to find the covariance matrix of each column vector so that if the result is another matrix C, C has shape [n, n, m] and C[:,:,i] = np.outer(xi, xi).
Can someone tell my how to do the above in numpy or point me to a tensor operation that I should check out?
So your outer loop produces:
In [1147]: A = np.arange(12).reshape(3,4)
In [1148]: [np.outer(A[:,i],A[:,i]) for i in range(4)]
Out[1148]:
[array([[ 0, 0, 0],
[ 0, 16, 32],
[ 0, 32, 64]]), array([[ 1, 5, 9],
[ 5, 25, 45],
[ 9, 45, 81]]), array([[ 4, 12, 20],
[ 12, 36, 60],
[ 20, 60, 100]]), array([[ 9, 21, 33],
[ 21, 49, 77],
[ 33, 77, 121]])]
stacking that on the a new 1st dimension produces:
In [1149]: np.stack(_)
Out[1149]:
array([[[ 0, 0, 0],
[ 0, 16, 32],
[ 0, 32, 64]],
....
[ 21, 49, 77],
[ 33, 77, 121]]])
In [1150]: _.shape
Out[1150]: (4, 3, 3) # wrong order - can be transposed.
stack lets us specify a different axis:
In [1153]: np.stack([np.outer(A[:,i],A[:,i]) for i in range(4)],2)
Out[1153]:
array([[[ 0, 1, 4, 9],
[ 0, 5, 12, 21],
[ 0, 9, 20, 33]],
[[ 0, 5, 12, 21],
[ 16, 25, 36, 49],
[ 32, 45, 60, 77]],
[[ 0, 9, 20, 33],
[ 32, 45, 60, 77],
[ 64, 81, 100, 121]]])
np.einsum does this nicely as well:
In [1151]: np.einsum('mi,ni->mni',A,A)
Out[1151]:
array([[[ 0, 1, 4, 9],
[ 0, 5, 12, 21],
[ 0, 9, 20, 33]],
[[ 0, 5, 12, 21],
[ 16, 25, 36, 49],
[ 32, 45, 60, 77]],
[[ 0, 9, 20, 33],
[ 32, 45, 60, 77],
[ 64, 81, 100, 121]]])
In [1152]: _.shape
Out[1152]: (3, 3, 4)
broadcasted multiply is also nice
In [1156]: A[:,None,:]*A[None,:,:]
Out[1156]:
array([[[ 0, 1, 4, 9],
[ 0, 5, 12, 21],
...
[ 32, 45, 60, 77],
[ 64, 81, 100, 121]]])
Given a 2D numpy image array with shape (height, width, 3), and BGR tuples as elements, I want to multiply each element by a kernel to extract the B/G/R channels individually. The blue kernel, for example, would be (1, 0, 0). Something like this:
# extact color channel
def extract_color_channel(image, kernel):
channel = np.copy(image)
height, width = image.shape[:2]
for y in range(0, height):
for x in range(0, width):
channel[y,x] = image[y, x] * kernel
return channel
# extract blue channel
def extract_blue(image):
return extract_color_channel(image, (1, 0, 0))
What is the most efficient "numpy way" to do this?
With a sample array:
In [220]: arr = np.arange(5*5*3).reshape(5,5,3)
Basic indexing is the most efficient way (this will be a view)
In [221]: arr[:,:,0]
Out[221]:
array([[ 0, 3, 6, 9, 12],
[15, 18, 21, 24, 27],
[30, 33, 36, 39, 42],
[45, 48, 51, 54, 57],
[60, 63, 66, 69, 72]])
The [1,0,0] list is not what you want. But you could cast it as a bool array.
In [222]: kernel = np.array([1,0,0],dtype=bool)
In [223]: kernel
Out[223]: array([ True, False, False], dtype=bool)
In [224]: arr[:,:,kernel].shape
Out[224]: (5, 5, 1)
In [225]: arr[:,:,kernel].squeeze()
Out[225]:
array([[ 0, 3, 6, 9, 12],
[15, 18, 21, 24, 27],
[30, 33, 36, 39, 42],
[45, 48, 51, 54, 57],
[60, 63, 66, 69, 72]])
Notice that the shape with the boolean is still 3d. If you don't want that, then you'll need to reshape or squeeze that last dimension out. This indexing is slower since it makes a copy.
This boolean indexing is the equivalent of
In [226]: arr[:,:,[0]].shape
Out[226]: (5, 5, 1)
where [0] is the location of the 'true' value(s) in kernel.
You could also use a dot (matrix product):
In [228]: np.dot(arr,[1,0,0])
Out[228]:
array([[ 0, 3, 6, 9, 12],
[15, 18, 21, 24, 27],
[30, 33, 36, 39, 42],
[45, 48, 51, 54, 57],
[60, 63, 66, 69, 72]])
It will be slower than indexing.
Element multiplication:
In [232]: arr*np.array([1,0,0])
Out[232]:
array([[[ 0, 0, 0],
[ 3, 0, 0],
[ 6, 0, 0],
[ 9, 0, 0],
[12, 0, 0]],
[[15, 0, 0],
[18, 0, 0],
....
[66, 0, 0],
[69, 0, 0],
[72, 0, 0]]])
In this multiplication the [1,0,0] behaves as though it were a (1,1,3) array, and broadcasts with the (n,n,3) just fine.
I have two tensors, each 2D, with a common long axis (eg 20.000) and diffenrent short axes eg one 9 the other 10). I want to end up with a 9X10X20000 tensor, such that for each location on the long axis, the other two axes are the tensor product.
Explicitly, with the "long" axis here 4, I want to do:
A = np.arange(8).reshape(2,4)
B = np.arange(12).reshape(3,4)
C = np.zeros(2,3,4)
for i in range(2):
for j in range(3):
for k in range(4):
C[i,j,k] = A[i,k]*B[j,k]
This code works, but I was wondering: is there a numpy way of doing this, without running for loops?
The context is for training a neural net, with the long axis being the training examples. I get a formula of this form when calculating gradients of the cost function.
Cheers,
Leo
I tend to use np.einsum for these problems, which makes it easy to specify what should happen in terms of the indices:
>>> A = np.arange(8).reshape(2,4)
>>> B = np.arange(12).reshape(3,4)
>>> np.einsum('ik,jk->ijk', A, B)
array([[[ 0, 1, 4, 9],
[ 0, 5, 12, 21],
[ 0, 9, 20, 33]],
[[ 0, 5, 12, 21],
[16, 25, 36, 49],
[32, 45, 60, 77]]])
This particular one you can also get to work with plain old broadcasting:
>>> A[:, np.newaxis, :] * B[np.newaxis, :, :]
array([[[ 0, 1, 4, 9],
[ 0, 5, 12, 21],
[ 0, 9, 20, 33]],
[[ 0, 5, 12, 21],
[16, 25, 36, 49],
[32, 45, 60, 77]]])
The above is equivalent to the simpler:
>>> A[:, None] * B
array([[[ 0, 1, 4, 9],
[ 0, 5, 12, 21],
[ 0, 9, 20, 33]],
[[ 0, 5, 12, 21],
[16, 25, 36, 49],
[32, 45, 60, 77]]])