I have the following Tensor:
# (class, index)
obj_class_indexes = tf.constant([(0, 0), (0, 1), (0, 2), (1, 3)])
And to each value I'm looking for the objects with the same class.
For now I'm trying the following:
same_classes = tf.logical_and(tf.equal(obj_classes_indexes[:, 0], obj_classes_indexes[0][0]), \
obj_classes_indexes[:, 1] > obj_classes_indexes[0][1])
found_indexes = tf.where(same_classes)
with tf.Session() as sess:
print(sess.run(same_classes))
print(sess.run(indexes))
The expected output would be:
[False True True False]
[1, 2]
But it's giving me:
[False True True False]
[[1], [2]]
I don't think the logical_and output is actually the correct input to the tf.where function. Or Am I missing something?
Thanks!
There is nothin wrong with the output. tf.where() is expected to output a 2D tensor as quoted here:
"The coordinates are returned in a 2-D tensor where the first dimension (rows) represents the number of true elements, and the second dimension (columns) represents the coordinates of the true elements"
If you want the output to be a 1D tensor as you have mentioned, you could just add a reshape op in your case as below:
found_indexes = tf.where(same_classes)
found_indexes = tf.reshape(found_indexes, [-1])
hope this helps!
Related
I am trying to slice a tensor with a indices tensor. For this purpose I am trying to use tf.gather.
However, I am having a hard time understanding the documentation and don't get it to work as I would expect it to:
I have two tensors. An activations tensor with a shape of [1,240,4] and an ids tensor with the shape [1,1,120]. I want to slice the second dimension of the activations tensor with the indices provided in the third dimension of the ids tensor:
downsampled_activations = tf.gather(activations, ids, axis=1)
I have given it the axis=1 option since that is the axis in the activations tensor I want to slice.
However, this does not render the expected result and only gives me the following error:
tensorflow.python.framework.errors_impl.InvalidArgumentError: indices[0,0,1] = 1 is not in [0, 1)
I have tried various combinations of the axis and batch_dims options, but to no avail so far and the documentation doesn't really help me on my path. Anybody care to explain the parameters in more detail or on the example above would be very helpful!
Edit:
The IDs are precomputed before runtime and come in through an input pipeline as such:
features = tf.io.parse_single_example(
serialized_example,
features={ 'featureIDs': tf.io.FixedLenFeature([], tf.string)}
They are then reshaped into the previous format:
feature_ids_raw = tf.decode_raw(features['featureIDs'], tf.int32)
feature_ids_shape = tf.stack([batch_size, (num_neighbours * 4)])
feature_ids = tf.reshape(feature_ids_raw, feature_ids_shape)
feature_ids = tf.expand_dims(feature_ids, 0)
Afterwards they have the previously mentioned shape (batch_size = 1 and num_neighbours = 30 -> [1,1,120]) and I want to use them to slice the activations tensor.
Edit2: I would like the output to be [1,120,4]. (So I would like to gather the entries along the second dimension of the activations tensor in accordance with the IDs stored in my ids tensor.)
You can use :
downsampled_activations =tf.gather(activations , tf.squeeze(ids) ,axis = 1)
downsampled_activations.shape # [1,120,4]
In most cases, the tf.gather method needs 1d indices, and that is right in your case, instead of indices with 3d (1,1,120), a 1d is sufficient (120,). The method tf.gather will look at the axis( = 1) and return the element at each index provided by the indices tensor.
tf.gather Gather slices from params axis axis according to indices.
Granted that the documentation is not the most expressive, and the emphasis should be placed on the slices (since you index slices from the axis and not elements, which is what I suppose you mistakenly took it for).
Let's take a much smaller example:
activations_small = tf.convert_to_tensor([[[1, 2, 3, 4], [11, 22, 33, 44]]])
print(activations_small.shape) # [1, 2, 4]
Let's picture this tensor:
XX 4 XX 44 XX XX
XX 3 XX 33 X XX
XXX 2 XX 22XX XX
X-----X-----+X XX
| 1 | 11 | XX
+-----+-----+X
tf.gather(activations1, [0, 0], axis=1) will return
<tf.Tensor: shape=(1, 2, 4), dtype=int32, numpy=
array([[[1, 2, 3, 4],
[1, 2, 3, 4]]], dtype=int32)>
What tf.gather did was to look from axis 1, and picks up index 0 (ofc, two times i.e. [0, 0]). If you were to run tf.gather(activations1, [0, 0, 0, 0, 0], axis=1).shape, you'd get TensorShape([1, 5, 4]).
Your Error
Now let's try to trigger the error that you're getting.
tf.gather(activations1, [0, 2], axis=1)
InvalidArgumentError: indices[1] = 2 is not in [0, 2) [Op:GatherV2]
What happened here was that when tf.gather looks from axis 1 perspective, there's no item (column if you will) with index = 2.
I guess this is what the documentation is hinting at by
param:<indices> The index Tensor. Must be one of the following types: int32, int64. Must be in range [0, params.shape[axis]).
Your (potential) solution
From the dimensions of indices, and that of the expected result from your question, I am not sure if the above was very obvious to you.
tf.gather(activations, indices=[0, 1, 2, 3], axis=2) or anything with indices within the range of indices in [0, activations.shape[2]) i.e. [0, 4) would work. Anything else would give you the error that you're getting.
There's a verbatim answer below in case that's your expected result.
I am writing a program that is suppose to be able to import numpy arrays of some higher dimension, e.g. something like an array a:
a = numpy.zeros([3,5,7,2])
Further, each dimension will correspond to some physical dimension, e.g. frequency, distance, ... and I will also import arrays with information about these dimensions, e.g. for a above:
freq = [1,2,3]
time = [0,1,2,3,4,5,6]
distance = [0,0,0,4,1]
angle = [0,180]
Clearly from this example and the signature it can be figured out that freq belong to dimension 0, time to dimension 2 and so on. But since this is not known in advance, I can take a frequency slice like
a_f1 = a[1,:,:,:]
since I do not know which dimension the frequency is indexed.
So, what I would like is to have some way to chose which dimension to index with an index; in some Python'ish code something like
a_f1 = a.get_slice([0,], [[1],])
This is suppose to return the slice with index 1 from dimension 0 and the full other dimensions.
Doing
a_p = a[0, 1:, ::2, :-1]
would then correspond to something like
a_p = a.get_slice([0, 1, 2, 3], [[0,], [1,2,3,4], [0,2,4,6], [0,]])
You can fairly easily construct a tuple of indices, using slice objects where needed, and then use this to index into your array. The basic is recipe is this:
indices = {
0: # put here whatever you want to get on dimension 0,
1: # put here whatever you want to get on dimension 1,
# leave out whatever dimensions you want to get all of
}
ix = [indices.get(dim, slice(None)) for dim in range(arr.ndim)]
arr[ix]
Here I have done it with a dictionary since I think that makes it easier to see which dimension goes with which indexer.
So with your example data:
x = np.zeros([3,5,7,2])
We do this:
indices = {0: 1}
ix = [indices.get(dim, slice(None)) for dim in range(x.ndim)]
>>> x[ix].shape
(5L, 7L, 2L)
Because your array is all zeros, I'm just showing the shape of the result to indicate that it is what we want. (Even if it weren't all zeros, it's hard to read a 3D array in text form.)
For your second example:
indices = {
0: 0,
1: slice(1, None),
2: slice(None, None, 2),
3: slice(None, -1)
}
ix = [indices.get(dim, slice(None)) for dim in range(x.ndim)]
>>> x[ix].shape
(4L, 4L, 1L)
You can see that the shape corresponds to the number of values in your a_p example. One thing to note is that the first dimension is gone, since you only specified one value for that index. The last dimension still exists, but with a length of one, because you specified a slice that happens to just get one element. (This is the same reason that some_list[0] gives you a single value, but some_list[:1] gives you a one-element list.)
You can use advanced indexing to achieve this.
The index for each dimension needs to be shaped appropriately so that the indices will broadcast correctly across the array. For example, the index for the first dimension of a 3-d array needs to be shaped (x, 1, 1) so that it will broadcast across the first dimension. The index for the second dimension of a 3-d array needs to be shaped (1, y, 1) so that it will broadcast across the second dimension.
import numpy as np
a = np.zeros([3,5,7,2])
b = a[0, 1:, ::2, :-1]
indices = [[0,], [1,2,3,4], [0,2,4,6], [0,]]
def get_aslice(a, indices):
n_dim_ = len(indices)
index_array = [np.array(thing) for thing in indices]
idx = []
# reshape the arrays by adding single-dimensional entries
# based on the position in the index array
for d, thing in enumerate(index_array):
shape = [1] * n_dim_
shape[d] = thing.shape[0]
#print(d, shape)
idx.append(thing.reshape(shape))
c = a[idx]
# to remove leading single-dimensional entries from the shape
#while c.shape[0] == 1:
# c = np.squeeze(c, 0)
# To remove all single-dimensional entries from the shape
#c = np.squeeze(c).shape
return c
For a as an input, it returns an array with shape (1,4,4,1) your a_p example has a shape of (4,4,1). If the extra dimensions need to be removed un-comment the np.squeeze lines in the function.
Now I feel silly. While reading the docs slower I noticed numpy has an indexing routine that does what you want - numpy.ix_
>>> a = numpy.zeros([3,5,7,2])
>>> indices = [[0,], [1,2,3,4], [0,2,4,6], [0,]]
>>> index_arrays = np.ix_(*indices)
>>> a_p = a[index_arrays]
>>> a_p.shape
(1, 4, 4, 1)
>>> a_p = np.squeeze(a_p)
>>> a_p.shape
(4, 4)
>>>
I want to do something like this.
Let's say we have a tensor A.
A = [[1,0],[0,4]]
And I want to get nonzero values and their indices from it.
Nonzero values: [1,4]
Nonzero indices: [[0,0],[1,1]]
There are similar operations in Numpy.
np.flatnonzero(A) return indices that are non-zero in the flattened A.
x.ravel()[np.flatnonzero(x)] extract elements according to non-zero indices.
Here's a link for these operations.
How can I do somthing like above Numpy operations in Tensorflow with python?
(Whether a matrix is flattened or not doesn't really matter.)
You can achieve same result in Tensorflow using not_equal and where methods.
zero = tf.constant(0, dtype=tf.float32)
where = tf.not_equal(A, zero)
where is a tensor of the same shape as A holding True or False, in the following case
[[True, False],
[False, True]]
This would be sufficient to select zero or non-zero elements from A. If you want to obtain indices you can use wheremethod as follows:
indices = tf.where(where)
where tensor has two True values so indices tensor will have two entries. where tensor has rank of two, so entries will have two indices:
[[0, 0],
[1, 1]]
#assume that an array has 0, 3.069711, 3.167817.
mask = tf.greater(array, 0)
non_zero_array = tf.boolean_mask(array, mask)
What about using sparse tensors.
>>> A = [[1,0],[0,4]]
>>> sparse = tf.sparse.from_dense(A)
>>> sparse.values.numpy(), sparse.indices.numpy()
(array([1, 4], dtype=int32), array([[0, 0],
[1, 1]]))
Right now, my function uses argmax:
p = tf.stop_gradient(tf.argmax(prev, 1))
I have tried using the following, but the dimn are incompatible:
p = tf.stop_gradient(tf.nn.top_k(prev, 2)[1])
raise ValueError("Linear is expecting 2D arguments: %s" % str(shapes))
ValueError: Linear is expecting 2D arguments: [[None, 2, 1024], [None, 1024]]
My TF version might be 0.5, which is why top_k only has 2 args.
Check the documentation for tf.nn.top_k(). The function returns values and indices. So something like below should work.
values, indices = tf.nn.top_k(prev,2)
p = tf.stop_gradient(indices[1])
p = tf.stop_gradient(indices[1])
output tf.Tensor(962, shape=(), dtype=int32)
But I need output in this form:
tf.Tensor([962], shape=(1,), dtype=int32)
In my function, sometimes I get a result that is a 1 dimensional numpy array in 2D form, so that it's shape is nx1 (n,1). Other times, I might get it in the form 1xn array.shape = (1,n)
Other times, I get just a numpy array whose shape is (n,).
when I run the following tests, I get an error on the one hand, and a false positive on the other (since the length of a shape attribute is always greater than 1, apparently):
y_predicted = forest.predict(testX)
if y_predicted.shape[1] != None:
y_predicted = y_predicted.T[0]
and
y_predicted = forest.predict(testX)
if len(y_predicted.shape) > 1:
y_predicted = y_predicted.T[0]
I just need to make sure the final shape of y is always in the form (n,) rather than (n,1) or (1,n)...
You should use numpy.squeeze:
numpy.squeeze(a) removes single-dimensional entries from the shape of an array.
Example:
>>> x = np.array([[1,2,3]])
>>> x.shape
(1, 3)
>>> np.squeeze(x)
array([1, 2, 3])
>>> np.squeeze(x).shape
(3,)