20

I am trying to process a tensor of variable size, in a python way that would be something like:

# X is of shape [m, n]
for x in X:
    process(x)

I have tried to use tf.scan, the thing is that I want to process every sub-tensor, so I have tried to use a nested scan, but I was enable to do it, because tf.scan work with the accumulator, if not found it will take the first entry of the elems as initializer, which I don't want to do. As an example, suppose I want to add one to every element of my tensor (this is just an example), and I want to process it element by element. If I run the code bellow, I will only have one added to a sub-tensor, because scan consider the first tensor as initializer, along with the first element of every sub-tensor.

import numpy as np
import tensorflow as tf

batch_x = np.random.randint(0, 10, size=(5, 10))
x = tf.placeholder(tf.float32, shape=[None, 10])

def inner_loop(x_in):
    return tf.scan(lambda _, x_: x_ + 1, x_in)

outer_loop = tf.scan(lambda _, input_: inner_loop(input_), x, back_prop=True)

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    rs = sess.run(outer_loop, feed_dict={x: batch_x})

Any suggestions ?

Mohamed Lakhal
  • 466
  • 1
  • 4
  • 11

2 Answers2

17

To loop over a tensor you could try tf.unstack

Unpacks the given dimension of a rank-R tensor into rank-(R-1) tensors.

So adding 1 to each tensor would look something like:

import tensorflow as tf
x = tf.placeholder(tf.float32, shape=(None, 10))
x_unpacked = tf.unstack(x) # defaults to axis 0, returns a list of tensors

processed = [] # this will be the list of processed tensors
for t in x_unpacked:
    # do whatever
    result_tensor = t + 1
    processed.append(result_tensor)

output = tf.concat(processed, 0)

with tf.Session() as sess:
    print(sess.run([output], feed_dict={x: np.zeros((5, 10))}))

Obviously you can further unpack each tensor from the list to process it, down to single elements. To avoid lots of nested unpacking though, you could maybe try flattening x with tf.reshape(x, [-1]) first, and then loop over it like

flattened_unpacked = tf.unstack(tf.reshape(x, [-1])
for elem in flattened_unpacked:
    process(elem)

In this case elem is a scalar.

Martin
  • 594
  • 1
  • 8
  • 32
Dzjkb
  • 714
  • 6
  • 10
  • 1
    I have tried this before, and it raise ValueError exception, because we are working with a None tensor as dim_0 – Mohamed Lakhal Apr 10 '17 at 16:36
  • 3
    Oh right sorry, I missed that `unstack` doesn't work with None dimensions. I found this question though, [Using tf.unpack() when first dimension of Variable is None](http://stackoverflow.com/questions/39446313/using-tf-unpack-when-first-dimension-of-variable-is-none) which seems to be answered quite well. – Dzjkb Apr 10 '17 at 17:06
  • Thanks for this - it was not clear to me that one could iterate over a list of tensors in normal Python fashion. In my case, I used tf.split instead of unstack – Alex Wulff Apr 30 '21 at 13:48
13

Most of tensorflow built-in functions could be applied elementwise. So you could just pass a tensor into a function. Like:

outer_loop = inner_loop(x)

However, if you have some function that could not be applied this way (it's really tempting to see that function), you could use map_fn.

Say, your function simply adds 1 to every element of a tensor (or whatever):

inputs = tf.placeholder...

def my_elementwise_func(x):
    return x + 1

def recursive_map(inputs):
   if tf.shape(inputs).ndims > 0:
       return tf.map_fn(recursive_map, inputs)
   else:
       return my_elementwise_func(inputs)

result = recursive_map(inputs)  
Dmitriy Danevskiy
  • 3,119
  • 1
  • 11
  • 15
  • My problem is more general than that, and I have been looking to find solutions but so far I am unsuccessful. I want to perform operations on each entry of the Tensor, the solution above does only element-wise operations. Any further suggestions ? – Mohamed Lakhal Apr 11 '17 at 18:01
  • Could you provide an example of a function which operates on each entry of a tensor? – Dmitriy Danevskiy Apr 11 '17 at 19:34
  • I want to process each entry according to some rules, say for example I want: sum(pos_xi) + 2 * sum(neg_xi). Matrix = [[1, -2, 3], [-5, 0, 1]] First entry: (1+3) + 2*(-2) = 0 Second entry: (0+1) + 2*(-5) = -9 Result = [0, -9] – Mohamed Lakhal Apr 11 '17 at 20:42
  • 1
    In this particular case tf.reduce_sum(a, 1), where a is tf.maximum(t, 0) - 2*tf.maximum(-t, 0) should work. Tensorflow has a lot of functions that perform reduce operations (sum, mean, max, min, etc.) and elementwise operations as well. By properly combining these functions it's possible to achieve desired result in the vast majority of situations. – Dmitriy Danevskiy Apr 12 '17 at 08:06
  • 1
    After a bit of search, it seems that as you pointed to perform any king of processing you have to combine operations on your tensor, and find a way to vectorize your calculation. For that part you have to squeeze your mind and get your hand dirty ! – Mohamed Lakhal Apr 12 '17 at 13:58