Pytorch Set value for Tensor with index tensor - python

Suppose i have a 2d index Array of shape [B,1,N,2] i.e N points holding indexes on a target tensor of size [B,1,H,W].
what is the best way to assign value to all the indices in the tensor?
for example:
for b in batchsize:
for i in N:
target[b,0,ind[i,0],ind[i,1]] = 1
but not in loop form
thanks

If we look at this setup, you have a tensor target shaped (b, 1, h, w) and a tensor containing indices ind, shaped (b, 1, N, 2). You want to assign 1 to the N points per batch given by the two coordinates in ind.
The way I see it you could use torch.scatter_. We will stick with a 3D tensor since axis=1 is unused. Given a 3D tensor and argument value=1 and dim=1, .scatter_ operates on the input tensor as so:
input[i][index[i][j]] = 1
This does not exactly fit your setting since what would wish for is rather
input[i][index1[i][j]][index2[i][j]] = 1
In order to use scatter you could flatten target and unfold the values in ind accordingly.
Let's take an example:
>>> target = torch.zeros(2, 1, 10, 10)
>>> ind = torch.tensor([[[[0, 0], [1, 1], [2, 2]]],
[[[1, 2], [3, 4], [7, 8]]]])
tensor([[[[0, 0],
[1, 1],
[2, 2]]],
[[[1, 2],
[3, 0],
[4, 2]]]])
We will start by splitting ind into xs and ys coordinates:
>>> x, y = ind[..., 0], ind[..., 1]
Unfold and reshape them:
>>> z = x*target.size(-1) + y
tensor([[[ 0, 4, 8]],
[[ 5, 9, 14]]])
Flatten target:
>>> t = target.flatten(2)
tensor([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])
Then scatter the 1s:
>>> t.scatter_(dim=2, index=z, value=1)
tensor([[[1., 0., 0., 0., 1., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.]],
[[0., 0., 0., 0., 0., 1., 0., 0., 0., 1., 0., 0., 0., 0., 1.]]])
And finally reshape back to the original shape:
>>> t.reshape_as(target)
tensor([[[[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.],
[0., 0., 0.],
[0., 0., 0.]]],
[[[0., 0., 0.],
[0., 0., 1.],
[0., 0., 0.],
[1., 0., 0.],
[0., 0., 1.]]]])
In summary:
>>> x, y = ind[..., 0], ind[..., 1]
>>> z = x*target.size(-1) + y
>>> target.flatten(2).scatter_(dim=2, index=z, value=1).reshape_as(target)
This last line will mutate target.

It's a bit hard to tell what you mean by "a 2D array of shape [B,1,N,2]" (that looks like a 4D array in commonly used notation). But it looks like across all elements in the batch, you want to assign the same values on dimensions 2 and 3 to each element indexed by N. You can use list indexing notation:
# assuming ind is a list of tuples of (dim2 index, dim3 index)
# unzip ind such that each resulting list contains indices along a single dimension
dim2_indices = [item[0] for item in ind]
dim3_indices = [item[1] for item in ind]
target[:,0,dim2,dim3] = 1

Related

In a pytorch tensor, return an array of indices of the rows of specific value

Given the below tensor that has vectors of all zeros and vectors with ones and zeros:
tensor([[0., 0., 0., 0.],
[0., 1., 1., 0.],
[0., 0., 0., 0.],
[0., 0., 1., 0.],
[0., 0., 0., 0.],
[0., 0., 1., 0.],
[1., 0., 0., 1.],
[0., 0., 0., 0.],...])
How can I have an array of indices of the vectors with ones and zeros so the output is like this:
indices = tensor([ 1, 3, 5, 6,...])
Update
A way to do it is:
indices = torch.unique(torch.nonzero(y>0,as_tuple=True)[0])
But I'm not sure if there's a better way to do it.
An alternative way is to use torch.Tensor.any coupled with torch.Tensor.nonzero:
>>> x.any(1).nonzero()[:,0]
tensor([1, 3, 5, 6])
Otherwise, since the tensor contains only positive value, you can sum the columns and mask:
>>> x.sum(1).nonzero()[:,0]
tensor([1, 3, 5, 6])

How to interpret numpy advanced indexing solution

I have a piece of numpy code that I know works. I know this because I have tested it in my generic case successfully. However, I arrived at the solution after two hours of back and forth referencing the docs and trial and error. I can't grasp how I would know to do this intuitively.
The setup:
a = np.zeros((5,5,3))
The goal: Set to 1 indices 0,1 of axis 1, 0,1 of axis 2, all of axis 3 and indices 3,4 of axis 1, 3,4 of axis 2, all of axis 3
Clearer goal: Set block 1 and 2's first two rows to 1 and block 3 and 4's last two rows to 1
The result:
ax1 =np.array([np.array([0,1]),np.array([3,4])])
ax1 =np.array([x[:,np.newaxis] for x in ax1])
ax2 = np.array([[[0,1]],[[3,4]]])
a[ax1,ax2,:] = 1
a
Output:
array([[[1., 1., 1.],
[1., 1., 1.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]],
[[1., 1., 1.],
[1., 1., 1.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]],
[[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]],
[[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[1., 1., 1.],
[1., 1., 1.]],
[[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[1., 1., 1.],
[1., 1., 1.]]])
I'm inclined to believe I should be able to look at the shape of the matrix in question, the shape of the indices, and the index operation to intuitively know the output. However, I can't put the story together in my head. Like, what's the final shape of the subspace it is altering? How would you explain how this works?
The shapes:
input: (5, 5, 3)
ind1: (2, 2, 1)
ind2: (2, 1, 2)
final_op: input[ind1, ind2, :]
With shapes
ind1: (2, 2, 1)
ind2: (2, 1, 2)
they broadcast together to select a (2,2,2) space
In [4]: ax1
Out[4]:
array([[[0],
[1]],
[[3],
[4]]])
In [5]: ax2
Out[5]:
array([[[0, 1]],
[[3, 4]]])
So for the 1st dimension (blocks) it is selecting blocks 0,1,3,and 4. In the second dimension it is also selecting these rows.
Together that's the first 2 rows of the first 2 blocks, and the last 2 rows of the last 2 blocks. That's where the 1s appear in your result.
A simpler way of creating the index arrays:
In [7]: np.array([[0,1],[3,4]])[:,:,None] # (2,2) expanded to (2,2,1)
In [8]: np.array([[0,1],[3,4]])[:,None,:] # expand to (2,1,2)
This is how broadcasting expands them:
In [10]: np.broadcast_arrays(ax1,ax2)
Out[10]:
[array([[[0, 0], # block indices
[1, 1]],
[[3, 3],
[4, 4]]]),
array([[[0, 1], # row indices
[0, 1]],
[[3, 4],
[3, 4]]])]
This may make the pattern clearer:
In [15]: a[ax1,ax2,:] = np.arange(1,5).reshape(2,2,1)
In [16]: a[:,:,0]
Out[16]:
array([[1., 2., 0., 0., 0.],
[3., 4., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 1., 2.],
[0., 0., 0., 3., 4.]])

Numpy change diagonal values in a 3D array

I have a vector with the following values: dv = array([0., 0., 1.]).
How do I diagonalize this vector into a 3D array with each element in the vector have its own diagonal:
array([[[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]],
[[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]],
[[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]]])
So far i have tried:
import numpy as np
a = np.zeros((3,3,3))
di = np.diag_indices(3,3)
a[di] = dv
This is almost correct, but it does not change all of the elements on the diagonal.
Try this -
a = np.zeros((3,3,3))
dv = np.array([0, 0, 1])
i,j = np.diag_indices(3) #diagonal indices for a 2D matrix (3,3)
a[:,i,j] = dv[:,None]
a
array([[[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]],
[[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]],
[[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]]])
You basically get 2D matrix diagonals and use those to get a view of the 3 diagonals from the original matrix. You then repeat the diagonal values you want to update to the same shape by broadcasting and map it to the diagonals.
The above approach is based on fetching a view from the original numpy array and then using assignment to update it. If you are not trying to do an assignment task, you can simply use arr.diagonal with axis1 and axis2 parameters to get a copy of the diagonal values for those 2 axes. Note, the shape of those axes must be equal (square matrix)
a = np.arange(0,2*3*3).reshape(2,3,3)
# array([[[ 0, 1, 2],
# [ 3, 4, 5],
# [ 6, 7, 8]],
# [[ 9, 10, 11],
# [12, 13, 14],
# [15, 16, 17]]])
a.diagonal(axis1=1, axis2=2)
# array([[ 0, 4, 8],
# [ 9, 13, 17]])

How does this one-hot vector conversion work?

When I was working on my machine learning project, I was looking for a line of code to turn my labels into one-hot vectors. I came across this nifty line of code from u/benanne on Reddit.
np.eye(n_labels)[target_vector]
For example, for a target_vector = np.array([1, 4, 2, 1, 0, 1, 3, 2]), it returns the one-hot coded values:
np.eye(5)[target_vector]
Out:
array([[ 0., 1., 0., 0., 0.],
[ 0., 0., 0., 0., 1.],
[ 0., 0., 1., 0., 0.],
...,
[ 0., 1., 0., 0., 0.],
[ 0., 0., 0., 1., 0.],
[ 0., 0., 1., 0., 0.]])
While it definitely does work, I'm not sure how or why it works.
It's rather simple. np.eye(n_labels) creates an identity matrix of size n_labels then you use your target_vector to select rows, corresponding to the value of the current target, from that matrix. Since each row in an identity matrix contains exactly one 1 element and the rest 0, each row will be a valid 'one hot coding'.
ndarray[[0]] is to select the first line in the ndarray
t = np.arange(9).reshape(3,3)
print t
print t[[1]]
Output is
[[0 1 2]
[3 4 5]
[6 7 8]]
[[3 4 5]]

How do I use a different index for each row in a numpy array?

I have a NxM numpy array filled with zeros and a 1D numpy array of size N with random integers between 0 to M-1. As you can see the dimension of the array matches the number of rows in the matrix. Each element in the integer array means that at that given position in its corresponding row must be set to 1. For example:
# The matrix to be modified
a = np.zeros((2,10))
# Indices array of size N
indices = np.array([1,4])
# Indexing, the result must be
a = a[at indices per row]
print a
[[0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0]]
I tried using the indexing a[:,indices] but this sets the same indices for each row, and this finally sets all the rows with ones. How can I set the given index to 1 per row?
Use np.arange(N) in order to address the rows and indices for columns:
>>> a[np.arange(2),indices] = 1
>>> a
array([[ 0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 1., 0., 0., 0., 0., 0.]])
Or:
>>> a[np.where(indices)+(indices,)] = 1
>>> a
array([[ 0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 1., 0., 0., 0., 0., 0.]])
You should also check the np.eye() function which does exactly what you want. It basically creates 2D arrays filled with zero and diagonal ones.
>>> np.eye(a.shape[1])[indices]
array([[0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 1., 0., 0., 0., 0., 0.]])

Categories

Resources