Numpy Slicing - Calculate Matrix PseudoInverses without Iteration from 3x3 array - python

I have N, 2x4 arrays stored in a (2x4xN) array J. I am trying to calculate the pseudoinverse for each of the N, 2x4 arrays, and save the pseudoinverses to a (N x 4 x 2) array J_pinv.
What I'm currently doing:
J_pinvs = np.zeros((N, 4, 2))
for i in range(N):
J_pinvs[i, :, :] = np.transpose(J[:, :, i]) # np.linalg.inv(J[:, :, i] # J[:, :, i].transpose())
This works but I would like to speed up the compute time as this will be running in a layer of a neural network so I would like to make it as fast as possible.
What I've tried:
J_pinvs = np.zeros((N, 4, 2))
J_pinvs2[:, :, :] = np.transpose(J[:, :, :]) # np.linalg.inv(J[:, :, :] # J[:, :, :].transpose())
Generates the error:
<ipython-input-87-d8ee1ba2ae5e> in <module>
1 J_pinvs2 = np.zeros((4, 2, 3))
----> 2 J_pinvs2[:, :, :] = np.transpose(J[:, :, :]) # np.linalg.inv(J[:, :, :] # J[:, :, :].transpose())
ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 4 is different from 3)
Is there a way to do this with slicing so that I don't need to use an iterator? I'm having trouble finding anything online. Any help/suggestions would be appretiated!
Thanks,
JM

I think you need to specify how to transpose a 3-D array:
np.linalg.inv(a # a.transpose(0,2,1))
will work. As oppose to
# sample data
a = np.arange(24).reshape(-1,2,4)
a.shape
# (3, 2, 4)
a.transpose().shape
# (4, 2, 3)
and
a # a.transpose()
will not work.
Finally, the whole script should be:
a.transpose(0,2,1) # np.linalg.inv(a # a.transpose(0,2,1))

Related

Fancy indexing for numpy arrary

I want to sample len(valid_frame_id_ls) frame from data by fancy indexing for numpy arrary. But I received an error message when i run code1. I don't know why the shape of data[n, :, valid_frame_id_ls, :, :] is not equal to the shape of new_data[n, :, :len(valid_frame_id_ls), :, :].Can anyone help me solve this bug. help...
I modify my code and write in code2 block. I did't receive any error message when i run code2. I don't know why code2 is correct.
code1:
data = np.random.random((2, 3, 50, 25, 1))
N, C, T, V, M = data.shape
new_data = np.zeros((N, C, T, V, M))
valid_frame_id_ls = [2, 3, 4, 5, 6]
for n in range(N):
new_data[n, :, :len(valid_frame_id_ls), :, :] = data[n, :, valid_frame_id_ls, :, :]
# code1 error message:
new_data[n, :, :len(valid_frame_id_ls), :, :] = data[n, :, valid_frame_id_ls, :, :]
ValueError: could not broadcast input array from shape (5,3,25,1) into shape (3,5,25,1)
code2:
data = np.random.random((2, 3, 50, 25, 1))
N, C, T, V, M = data.shape
new_data = np.zeros((N, C, T, V, M))
valid_frame_id_ls = [2, 3, 4, 5, 11]
for n in range(N):
new_data[n][:, :len(valid_frame_id_ls), :, :] = data[n][ :, valid_frame_id_ls, :, :]
https://numpy.org/doc/stable/reference/arrays.indexing.html#combining-advanced-and-basic-indexing
As described in this section of the docs, putting a slice in the middle of 'advanced' indexing results in an unexpected rearrangement of dimensions. Your size 5 dimension has been placed first, and the other dimensions after.
This has come up occasionally on SO as well. With a scalar n this really shouldn't be happening, but apparently the issue occurs deep in the indexing, and isn't easily corrected.
data[n][ :, valid_frame_id_ls, :, :]
breaks up the indexing, so the first ':' is no longer in the middle.
Another fix is to replace the slice with an equivalent array. Now both sides will have the same dimensions.
new_data[n, :, np.arange(len(valid_frame_id_ls)), :, :] = data[n, :, valid_frame_id_ls, :, :]
Though in this case I don't think you need to iterate on N at all:
new_data[:,:,:len(valid_frame_id_ls),:,:] = data[:,:, valid_frame_id_ls, :,:]

ValueError: could not broadcast input array from shape (64,1) into shape (10,1)

I get said error in line 5 when running this code:
elif backward_pass[j].type == "L":
backward_pass[j].change_w = np.multiply(backward_pass[j + 1].out[i].T, error[i, j, :, :]) # vector is tipped so multiplication returns a matrix; has to be tipped anti-clock-wise
backward_pass[j].change_b = 1 * error[i, j, :, :]
print(backward_pass[j].grad.T.shape)
error[i, j + 1, :, :] = np.dot(backward_pass[j].grad.T, error[i, j, :, :])
error[...] has shape (10, 1). For the first argument's shape, the print command returns
(64, 10)
as it is supposed to be. However, np.dot sets the last axis to 1. Even when using a custom function to replace np.dot, the same problem occurs.
Help.

Array Operations to augment image Python cv2 using numpy

Given X is a Numpy Array X.shape =(1, 96, 96, 3), Basically an Image read from CV2 . I am looking for simpler articulation of a augment operation.
Could you please explain what the following lines of code does
b=X[:, ::-1, :, :]
c=X[:, ::-1, ::-1, :]
d=X[:, :, ::-1, :]
X[::-1] indexing applies: indices of X from first to last in steps of -1.
b=X[:, ::-1, :, :] - Reverse image up/down.
c=X[:, ::-1, ::-1, :] - Reverse image up/down and left/right.
d=X[:, :, ::-1, :] - Reverse image left/right.
Remark:
:: is not an operator, it's actually two : operators one after the other.
X[::-1] is the same as X[ : : -1].
Refer to Indexing documentation.
The basic slice syntax is i:j:k where i is the starting index, j is the stopping index, and k is the step.
If i is not given it defaults to 0
If j is not given it defaults to n
Writing [: : -1], omits i and j, and sets k to -1.
The syntax means: "from index 0, take all elements, with step -1", that gives all elements in reverse order (all elements along this axis).
Example:
import cv2
import numpy as np
# Build input:
im = cv2.imread('chelsea.png')
im = cv2.resize(im, (96, 96))
X = np.empty((1, im.shape[0], im.shape[1], im.shape[2])).astype(np.uint8)
X[0, :, :, :] = im
b = X[:, ::-1, :, :]
c = X[:, ::-1, ::-1, :]
d = X[:, :, ::-1, :]
Result:
im:
b:
c:
d:
Note:
I kind of ignored the fist index because the dimension is 1.
In case of multiple frames, it's common for the fist index to apply the number of frames.

Inconsistency when comparing scipy, torch and fourier periodic convolution

I'm implementing a 2d periodic convolution on a synthetic image in three different ways: using scipy, using torch and using the Fourier transform (also under torch framework).
However, I've got different results. Performing the operation by hand I can see that scipy's convolution yields the correct results. torch's spatial version, on the other hand, yields the expected result inverted. Finally, the Fourier version returns something unexpected.
The code is the following:
import torch
import numpy as np
import scipy.signal as sig
import torch.nn.functional as F
import matplotlib.pyplot as plt
def numpy_periodic_conv(f, k):
H, W = f.shape
periodic_f = np.hstack([f, f])
periodic_f = np.vstack([periodic_f, periodic_f])
conv = sig.convolve2d(periodic_f, k, mode='same')
conv = conv[H // 2:-H // 2, W // 2:-W // 2]
return periodic_f, conv
def torch_periodic_conv(f, k):
H, W = f.shape[-2:]
periodic_f = f.repeat(1, 1, 2, 2)
conv = F.conv2d(periodic_f, k, padding=1)
conv = conv[:, :, H // 2:-H // 2, W // 2:-W // 2]
return periodic_f.squeeze().numpy(), conv.squeeze().numpy()
def torch_fourier_conv(f, k):
pad_x = f.shape[-2] - k.shape[-2]
pad_y = f.shape[-1] - k.shape[-1]
expanded_kernel = F.pad(k, [0, pad_x, 0, pad_y])
fft_x = torch.rfft(f, 2, onesided=False)
fft_kernel = torch.rfft(expanded_kernel, 2, onesided=False)
real = fft_x[:, :, :, :, 0] * fft_kernel[:, :, :, :, 0] - \
fft_x[:, :, :, :, 1] * fft_kernel[:, :, :, :, 1]
im = fft_x[:, :, :, :, 0] * fft_kernel[:, :, :, :, 1] + \
fft_x[:, :, :, :, 1] * fft_kernel[:, :, :, :, 0]
fft_conv = torch.stack([real, im], -1) # (a+bj)*(c+dj) = (ac-bd)+(ad+bc)j
ifft_conv = torch.irfft(fft_conv, 2, onesided=False)
return expanded_kernel.squeeze().numpy(), ifft_conv.squeeze().numpy()
if __name__ == '__main__':
f = np.concatenate([np.ones((10, 5)), np.zeros((10, 5))], 1)
k = np.array([[1, 0, -1], [2, 0, -2], [1, 0, -1]])
f_tensor = torch.from_numpy(f).unsqueeze(0).unsqueeze(0).float()
k_tensor = torch.from_numpy(k).unsqueeze(0).unsqueeze(0).float()
np_periodic_f, np_periodic_conv = numpy_periodic_conv(f, k)
tc_periodic_f, tc_periodic_conv = torch_periodic_conv(f_tensor, k_tensor)
tc_fourier_k, tc_fourier_conv = torch_fourier_conv(f_tensor, k_tensor)
print('Spatial numpy conv shape= ', np_periodic_conv.shape)
print('Spatial torch conv shape= ', tc_periodic_conv.shape)
print('Fourier torch conv shape= ', tc_fourier_conv.shape)
r_np = dict(name='numpy', im=np_periodic_f, k=k, conv=np_periodic_conv)
r_torch = dict(name='torch', im=tc_periodic_f, k=k, conv=tc_periodic_conv)
r_fourier = dict(name='fourier', im=f, k=tc_fourier_k, conv=tc_fourier_conv)
titles = ['{} im', '{} kernel', '{} conv']
results = [r_np, r_torch, r_fourier]
fig, axs = plt.subplots(3, 3)
for i, r_dict in enumerate(results):
axs[i, 0].imshow(r_dict['im'], cmap='gray')
axs[i, 0].set_title(titles[0].format(r_dict['name']))
axs[i, 1].imshow(r_dict['k'], cmap='gray')
axs[i, 1].set_title(titles[1].format(r_dict['name']))
axs[i, 2].imshow(r_dict['conv'], cmap='gray')
axs[i, 2].set_title(titles[2].format(r_dict['name']))
plt.show()
The results I'm obtaining:
Note: The image for both numpyand torch versions show the periodic image, which is required to perform the periodic convolution. The kernel for the Fourier version shows the original kernel zero-padded to the image size, which is required to compute the element-wise multiplication in the frequency domain.
-Edit1: There was an error when in the multiplication in the Fourier version, I was doing (ac-bd)+(ad-bc)j instead of (ac-bd)+(ad+bc)j. But now, I get the convolution shifted by one column.
-Edit2: torch's spatial convolution results is inverted because the operation is actually a cross-correlation. This was confirmed in the pytorch's official forum here. Furthermore, after fixing the kernel padding as Cris Luengo's answer, the frequency method yielded the same results as the correlations. This is rather strange for me because, as far as I know, the frequency property hold for convolution, not correlation.
New-results after fixing the kernel:
The FFT result is wrong because the padding is wrong. When padding, you need to put the origin (center of the kernel) at the top-left corner of the image. See this other answer for details.
The difference between the other two is the difference between a convolution and a correlation. It looks like the “numpy“ result is a convolution, the “torch” one a correlation.

How to replace certain values in Tensorflow tensor with the values of the other tensor?

I have a Tensorflow tensor A of size (64, 2, 82, 1), and I want to replace its (:, :, 80:82, :) part with the corresponding part of the tensor B (also (64, 2, 82, 1) size).
How would I do that?
P.S.: To be precise, I mean the operation that would look like this in the numpy:
A[:, :, 80:82, :] = B[:, :, 80:82, :]
the following code might help you to get some idea,
a = tf.constant([[11,0,13,14],
[21,22,23,0]])
condition = tf.equal(a, 0)
case_true = tf.reshape(tf.multiply(tf.ones([8], tf.int32), -9999), [2, 4])
case_false = a
a_m = tf.where(condition, case_true, case_false)
sess = tf.Session()
sess.run(a_m)
here i am accessing individual element of a tensor!
tf.assign should work: (not tested)
tf.assign(A[:, :, 80:82, :], B[:, :, 80:82, :])

Categories