Multiply two placeholder with different rank in tensorflow - python

I have two placholders with the following dimensions
x_ph = tf.placeholder(tf.float32,[None, 4])
and
y_ph = tf.placeholder(tf.float32,[None, 4, 3])
I want to multiply each element of x with one row of y_ph to get an output of shape (None, 4, 3).
Example of output I am looking,
x = np.random.uniform(-1,1, (2,2))
z = np.random.uniform(-1,1, (2,2,3))
x, z
([[ 0.27083503, -0.13795923],[ 0.8436118 , 0.00771057]])
([[[ 0.51905276, 0.01173655, -0.57335926],
[ 0.42347431, -0.05438272, 0.21042366]]
[[ 0.91347706, -0.28086164, 0.54952429],
[ 0.41551953, -0.6207727 , 0.32066292]]]))
I want to do following operation:
result = np.zeros((2,3))
for i in range(2):
for j in range(2):
result[i] += x[i,j]*z[i,j,:]
print(result)
[[ 0.08215548 0.01068127 -0.18431566]
[ 0.77382391 -0.2417247 0.46605767]]
Any way to do it in tensorflow?

Add one dimension at the end of x_ph so you can use broadcasting to multiply both tensors:
import tensorflow as tf
x_ph = tf.placeholder(tf.float32,[None, 4])
y_ph = tf.placeholder(tf.float32,[None, 4, 3])
result = tf.expand_dims(x_ph, -1) * y_ph

Related

How to convert numpy code into tensorflow?

I am trying to convert numpy code into tensorflow graph format. But somewhere I am missing an understanding of dimensionality.
Here is numpy code:
def delta_to_boxes3d(deltas, anchors, coordinate='lidar'):
# Input:
# deltas: (N, w, l, 14)(200,240,14)
# feature_map_shape: (w, l)
# anchors: (w, l, 2, 7)(200,240,2,7)
# Ouput:
# boxes3d: (N, w*l*2, 7)
#anchros shape 9200,240,2,7)
anchors_reshaped = anchors.reshape(-1, 7) #(96000,7)
deltas = deltas.reshape(-1, 7) #(96000,7)
anchors_d = np.sqrt(anchors_reshaped[:, 4]**2 + anchors_reshaped[:, 5]**2)
boxes3d = np.zeros_like(deltas)
boxes3d[..., [0, 1]] = deltas[..., [0, 1]] * \
anchors_d[:, np.newaxis] + anchors_reshaped[..., [0, 1]] #in this line I have the problem
boxes3d[..., [2]] = deltas[..., [2]] * \
1.73 + anchors_reshaped[..., [2]] #ANCHOR_H = 1.73
boxes3d[..., [3, 4, 5]] = np.exp(
deltas[..., [3, 4, 5]]) * anchors_reshaped[..., [3, 4, 5]]
boxes3d[..., 6] = deltas[..., 6] + anchors_reshaped[..., 6]
return boxes3d
Here is the code which I have been trying:
def delta_boxes3d():
anchors = tf.placeholder(tf.float32,shape=[None,None,2,7],name="anchor") #check the anchor type later
anchors_reshaped = tf.reshape(anchors,shape=[96000,7])
delta = tf.placeholder(tf.float32,shape=[None,None,14],name="delta")
anchors_d = tf.sqrt(tf.add(tf.pow(anchors_reshaped[:,4],2),tf.pow(anchors_reshaped[:,5],2))) #96000
deltas = tf.reshape(delta,[96000,7])
x_shape = tf.shape(deltas)
boxes3d_ = tf.multiply(deltas[:,0:2],tf.add(tf.expand_dims(anchors_d,-1),anchors_reshaped[:,0:2]))
boxes3d = tf.ones(x_shape[:-1]) + boxes3d_
elta_ = np.random.rand(200,240,14)
anchor_ = np.random.rand(200,240,2,7)
sess = tf.Session()
sess.run(tf.global_variables_initializer())
result = sess.run(boxes3d1,feed_dict={anchors:anchor_,delta:delta_}) #(96000,7) #need to get boxes3d
print(result.shape)
I am getting below error:
ValueError: Dimensions must be equal, but are 96000 and 2 for '{{node add_2}} = AddV2[T=DT_FLOAT](ones, Mul)' with input shapes: [96000], [96000,2].
Could someone help me with this?
Thanks in advance
The error comes from the line boxes3d = tf.ones(x_shape[:-1]) + boxes3d_.
You are trying to add shapes (96000,) and (96000,2), which you can't without expanding dims. If you want to add a scalar, you can do boxes3d = 1 + boxes3d.
In the example above, you want to do multiplication by scalar followed by addition.
Note that in the NumPy example in the highlighted line, you do the multiplication first and then addition. In TensorFlow, you made it the other way around (possibly by mistake).
I rewrote your NumPy example to Tensorflow 2 so that both functions return the same output.
def delta_boxes3d(deltas, anchors):
deltas = tf.constant(deltas)
anchors = tf.constant(anchors)
anchors_reshaped = tf.reshape(anchors, shape=[96000, 7])
anchors_d = tf.sqrt(tf.add(tf.pow(anchors_reshaped[:, 4], 2), tf.pow(anchors_reshaped[:, 5], 2))) # 96000
deltas = tf.reshape(deltas, [96000, 7])
boxes3d_01 = tf.add(tf.multiply(deltas[:, 0:2], tf.expand_dims(anchors_d, -1)), anchors_reshaped[:, 0:2])
boxes3d_2 = deltas[..., 2:3] * 1.73 + anchors_reshaped[..., 2:3]
boxes3d_345 = tf.exp(deltas[..., 3:6]) * anchors_reshaped[..., 3:6]
boxes3d_6 = deltas[..., 6:7] + anchors_reshaped[..., 6:7]
boxes3d = tf.concat([boxes3d_01, boxes3d_2, boxes3d_345, boxes3d_6], axis=-1)
return boxes3d
deltas = np.random.rand(200, 240, 14)
anchors = np.random.rand(200, 240, 2, 7)
print(delta_to_boxes3d(deltas, anchors))
print(delta_boxes3d(deltas, anchors))
You can notice I created smaller arrays first, and then I concatenated them. This is because Tensorflow won't allow me to modify EagerTensors.
Notice the difference between deltas[..., 2] and deltas[..., 2:3]. The second one doesn't reduce the last dimension. They return shapes (96000,), and (96000,1) respectively.

Multidimensional Tensor slicing

First things first: I'm relatively new to TensorFlow.
I'm trying to implement a custom layer in tensorflow.keras and I'm having relatively hard time when I try to achieve the following:
I've got 3 Tensors (x,y,z) of shape (?,49,3,3,32) [where ? is the batch size]
On each Tensor I compute the sum over the 3rd and 4th axes [thus I end up with 3 Tensors of shape (?,49,32)]
By doing an argmax (A)on the above 3 Tensors (?,49,32) I get a single (?,49,32) Tensor
Now I want to use this tensor to select slices from the initial x,y,z Tensors in the following form:
Each element in the last dimension of A corresponds to the selected Tensor.
(aka: 0 = X, 1 = Y, 2 = Z)
The index of the last dimension of A corresponds to the slice that I would like to extract from the Tensor last dimension.
I've tried to achieve the above using tf.gather but I had no luck. Then I tried using a series of tf.map_fn, which is ugly and computationally costly.
To simplify the above:
let's say we've got an A array of shape (3,3,3,32). Then the numpy equivalent of what I try to achieve is this:
import numpy as np
x = np.random.rand(3,3,32)
y = np.random.rand(3,3,32)
z = np.random.rand(3,3,32)
x_sums = np.sum(np.sum(x,axis=0),0);
y_sums = np.sum(np.sum(y,axis=0),0);
z_sums = np.sum(np.sum(z,axis=0),0);
max_sums = np.argmax([x_sums,y_sums,z_sums],0)
A = np.array([x,y,z])
tmp = []
for i in range(0,len(max_sums)):
tmp.append(A[max_sums[i],:,:,i)
output = np.transpose(np.stack(tmp))
Any suggestions?
ps: I tried tf.gather_nd but I had no luck
This is how you can do something like that with tf.gather_nd:
import tensorflow as tf
# Make example data
tf.random.set_seed(0)
b = 10 # Batch size
x = tf.random.uniform((b, 49, 3, 3, 32))
y = tf.random.uniform((b, 49, 3, 3, 32))
z = tf.random.uniform((b, 49, 3, 3, 32))
# Stack tensors together
data = tf.stack([x, y, z], axis=2)
# Put reduction axes last
data_t = tf.transpose(data, (0, 1, 5, 2, 3, 4))
# Reduce
s = tf.reduce_sum(data_t, axis=(4, 5))
# Find largest sums
idx = tf.argmax(s, 3)
# Make gather indices
data_shape = tf.shape(data_t, idx.dtype)
bb, ii, jj = tf.meshgrid(*(tf.range(data_shape[i]) for i in range(3)), indexing='ij')
# Gather result
output_t = tf.gather_nd(data_t, tf.stack([bb, ii, jj, idx], axis=-1))
# Reorder axes
output = tf.transpose(output_t, (0, 1, 3, 4, 2))
print(output.shape)
# TensorShape([10, 49, 3, 3, 32])

tensorflow scatter_nd with empty tensors?

I am trying to smush two tensors together. scatter_nd is perfect for this occasion, and I have written the following function to accomplish my task. It basically just does 2 scatter_nds ad puts them together.
def tf_munge(t, i, r, j, axis=0):
#insert tensor t at indices i and tensor r at indices j on axis `axis`.
#requires: i.shape[0] == t.shape[axis] && j.shape[0] == r.shape[axis] && t.shape[k] == r.shape[k] ∀k != axis
i = tf.expand_dims(i, -1)
j = tf.expand_dims(j, -1)
rank_indices = tf.range(tf.rank(t))
roller = tf.roll(rank_indices, -axis, 0)
rolled_t = tf.transpose(t, roller)
rolled_r = tf.transpose(r, roller)
scatter_shape = tf.concat((tf.shape(i)[0:1] + tf.shape(j)[0:1], tf.shape(rolled_t)[1:]), axis=0)
scattered = tf.scatter_nd(i, rolled_t, scatter_shape) + tf.scatter_nd(j, rolled_r, scatter_shape)
return tf.transpose(scattered, tf.roll(rank_indices, axis, 0))
It works as expected, generally. However, it fails whenever both r and t are empty along some axis. I have two code "paths" depending on a boolean wherein I split my tensor and perform different operations depending on whether that boolean is true or false. Sometimes, that boolean is false for 0 rows. In this case, I end up doing things to an empty tensor. One of those things is this attempted scattering. The error actually references the output shape (scatter_shape in the above code) claiming that:
ValueError: Indices and updates specified for empty output shape for 'ScatterNd_4' (op: 'ScatterNd')
with input shapes: [3,1], [3,0,2], [3] and with input tensors computed as partial shapes: input[2] = [5,0,2].
Note that the axis that is empty is different than the axis along which I'm scattering. Here is a working example:
foo = tf.ones((3,1,2))
bar = tf.ones((2,1,2))*2
i = tf.constant([1,3,4])
j = tf.constant([0,2])
tf_munge(foo,i,bar,j,axis=0)
#Output: <tf.Tensor 'transpose_13:0' shape=(5, 1, 2) dtype=float32>
Here is a failing example:
foo = tf.ones((3,0,2))
bar = tf.ones((2,0,2))*2
tf_munge(foo,i,bar,j,axis=0)
#Output: The error above
The expected output here would obviously be an empty tensor of shape (5,0,2).
I thought about using a conditional on the shape of the input, but tf.cond executes both pathways. How can I handle this situation when I have an empty tensor with scatter_nd?
You can do that more simply with tf.gather in a way that works for all cases:
import tensorflow as tf
def tf_munge(t, i, r, j, axis=0):
tr = tf.concat([t, r], axis=axis)
idx = tf.argsort(tf.concat([i, j], axis=0))
return tf.gather(tr, idx, axis=axis)
with tf.Graph().as_default(), tf.Session() as sess:
foo = tf.ones((3, 1, 2))
bar = tf.ones((2, 1, 2)) * 2
i = tf.constant([1, 3, 4])
j = tf.constant([0, 2])
out = tf_munge(foo, i, bar, j, axis=0)
print(sess.run(out))
# [[[2. 2.]]
#
# [[1. 1.]]
#
# [[2. 2.]]
#
# [[1. 1.]]
#
# [[1. 1.]]]
foo2 = tf.ones((3, 0, 2))
bar2 = tf.ones((2, 0, 2)) * 2
out2 = tf_munge(foo2, i, bar2, j, axis=0)
print(sess.run(out2))
# []

How to get median of column positive elements in a tensor/matrix?

Specifically given a 2-D matrix, how to find median for every column's positive elements?
Mathematically speaking: return B, where B[i] = median({A[j, i] | A[j, i] > 0})
I know that median can by computed by  tf.contrib.distributions.percentile
tf.boolean_mask(A, tf.greater(A, 0)) outputs a 1-D list instead of a matrix.
tf.boolean_mask() indeed returns a 1-D tensor, as otherwise the resulting tensor with dimensions kept would be sparse (c.f. columns having a different number of positive elements).
As I do not know of any median function for sparse matrices, the only alternative coming to mind is to loop over the columns, e.g. using tf.map_fn():
import tensorflow as tf
A = tf.convert_to_tensor([[ 1, 0, 20, 5],
[-1, 1, 10, 0],
[-2, 1, -10, 2],
[ 0, 2, 20, 1]])
positive_median_fn = lambda x: tf.contrib.distributions.percentile(tf.boolean_mask(x, tf.greater(x, 0)), q=50)
A_t = tf.matrix_transpose(A) # tf.map_fn is applied along 1st dim, so we need to transpose A
res = tf.map_fn(fn=positive_median_fn, elems=A_t)
with tf.Session() as sess:
print(sess.run(res))
# [ 1 1 20 2]
Note: this snippet doesn't cover the case when a column contains no positive elements. tf.contrib.distributions.percentile() would return an error if its input tensor is empty. A condition on the shape of tf.boolean_mask(x, tf.greater(x, 0)) could for instance be used (e.g. with tf.where())
You could loop over the column slices and filter like this.
inputlist = [[5 , -10 ] ,
[10 , 3 ] ,
[15 , -5 ]]
x = tf.Variable(initial_value=inputlist)
sess = tf.Session()
sess.run(tf.global_variables_initializer())
for i in range(x.get_shape().as_list()[1]) : #loop over columns
print( sess.run(tf.contrib.distributions.percentile(tf.gather(x[:,i],
tf.where(tf.greater(x[:,i],
0))),
50.0)))

Extracting specific elements from a tensor in tensorflow

I'm using tensorflow on python
I have a data tensor of shape [?, 5, 37], and a idx tensor of shape [?, 5]
I'd like to extract elements from data and get an output of shape [?, 5] such that:
output[i][j] = data[i][j][idx[i, j]] for all i in range(?) and j in range(5)
It looks loke the tf.gather_nd() function is the closest to my needs, but I don't see how to use it it my case...
Thanks !
EDIT : I managed to do it with gather_nd as shown below, but is there a better option ? (it seems a bit heavy-handed)
nRows = tf.shape(length_label)[0] ==> ?
nCols = tf.constant(MAX_LENGTH_INPUT + 1, dtype=tf.int32) ==> 5
m1 = tf.reshape(tf.tile(tf.range(nCols), [nRows]),
shape=[nRows, nCols])
m2 = tf.transpose(tf.reshape(tf.tile(tf.range(nRows), [nCols]),
shape=[nCols, nRows]))
indices = tf.pack([m2, m1, idx], axis=-1)
# indices should be of shape [?, 5, 3] with indices[i,j]==[i,j,idx[i,j]]
output = tf.gather_nd(data, indices=indices)
I managed to do it with gather_nd as shown below
nRows = tf.shape(length_label)[0] # ==> ?
nCols = tf.constant(MAX_LENGTH_INPUT + 1, dtype=tf.int32) # ==> 5
m1 = tf.reshape(tf.tile(tf.range(nCols), [nRows]),
shape=[nRows, nCols])
m2 = tf.transpose(tf.reshape(tf.tile(tf.range(nRows), [nCols]),
shape=[nCols, nRows]))
indices = tf.pack([m2, m1, idx], axis=-1)
# indices should be of shape [?, 5, 3] with indices[i,j]==[i,j,idx[i,j]]
output = tf.gather_nd(data, indices=indices)

Categories

Resources