Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tf.test.TestCase not working properly with tf.map_fn #17694

Closed
bkungfoo opened this issue Mar 13, 2018 · 3 comments
Closed

tf.test.TestCase not working properly with tf.map_fn #17694

bkungfoo opened this issue Mar 13, 2018 · 3 comments
Assignees
Labels
type:support Support issues

Comments

@bkungfoo
Copy link

bkungfoo commented Mar 13, 2018

System information

  • Have I written custom code (as opposed to using a stock example script provided in TensorFlow): yes
  • OS Platform and Distribution (e.g., Linux Ubuntu 16.04): mac OSX
  • TensorFlow installed from (source or binary): source
  • TensorFlow version (use command below): 1.4.0 and 1.5.0
  • Python version: 3.6
  • Bazel version (if compiling from source): N/A
  • GCC/Compiler version (if compiling from source): N/A
  • CUDA/cuDNN version: None
  • GPU model and memory: None
  • Exact command to reproduce: Run the snippet below.

Describe the problem

When trying to run tf.test.TestCase with a tensor of strings, tf.map_fn() throws errors that a normal tf.Session.run() does not encounter.

I am trying to create unit tests with tf.test.TestCase, but could not find a way to test tf.map_fn without Tensorflow returning an error. (This occurs in both TF 1.4 and 1.5.) My guess is it has to do with how strings are treated by map_fn as tensors...

Source code / logs

Here is a very simple example:

    def identity_map(input):
      return input

    # Using tf.Session() and sess.run(). This runs without errors.
    input = ['123', 'abc']
    x = tf.map_fn(identity_map, input_tensor, dtype=tf.string)
    x = tf.stack(x)
    x = tf.reshape(tensor=x, shape=[-1])

    with tf.Session() as sess:
      result = sess.run(x, {input_tensor: input})
      print(result)

Now I create a unit test and try to run it:

    class SimpleTest(tf.test.TestCase):
      def testMapString(self):
        input = ['123', 'abc']
        with self.test_session():
          # Run map function
          x = tf.map_fn(identity_map, input, dtype=tf.string)
          x = tf.stack(x)
          x = tf.reshape(tensor=x, shape=[-1])
          result = x.eval()
          print(result)

I get this output. Any idea what went wrong?

Traceback (most recent call last):
...
  File "/Users/bfoo/venv3/strata/lib/python3.6/site-packages/tensorflow/python/ops/functional_ops.py", line 344, in map_fn
    n = array_ops.shape(elems_flat[0])[0]
  File "/Users/bfoo/venv3/strata/lib/python3.6/site-packages/tensorflow/python/ops/array_ops.py", line 538, in _SliceHelper
    name=name)
  File "/Users/bfoo/venv3/strata/lib/python3.6/site-packages/tensorflow/python/ops/array_ops.py", line 706, in strided_slice
    shrink_axis_mask=shrink_axis_mask)
  File "/Users/bfoo/venv3/strata/lib/python3.6/site-packages/tensorflow/python/ops/gen_array_ops.py", line 5430, in strided_slice
    name=name)
  File "/Users/bfoo/venv3/strata/lib/python3.6/site-packages/tensorflow/python/framework/op_def_library.py", line 787, in _apply_op_helper
    op_def=op_def)
  File "/Users/bfoo/venv3/strata/lib/python3.6/site-packages/tensorflow/python/framework/ops.py", line 2958, in create_op
    set_shapes_for_outputs(ret)
  File "/Users/bfoo/venv3/strata/lib/python3.6/site-packages/tensorflow/python/framework/ops.py", line 2209, in set_shapes_for_outputs
    shapes = shape_func(op)
  File "/Users/bfoo/venv3/strata/lib/python3.6/site-packages/tensorflow/python/framework/ops.py", line 2159, in call_with_requiring
    return call_cpp_shape_fn(op, require_shape_fn=True)
  File "/Users/bfoo/venv3/strata/lib/python3.6/site-packages/tensorflow/python/framework/common_shapes.py", line 627, in call_cpp_shape_fn
    require_shape_fn)
  File "/Users/bfoo/venv3/strata/lib/python3.6/site-packages/tensorflow/python/framework/common_shapes.py", line 691, in _call_cpp_shape_fn_impl
    raise ValueError(err.message)
ValueError: slice index 0 of dimension 0 out of bounds. for 'map_1/strided_slice' (op: 'StridedSlice') with input shapes: [0], [1], [1], [1] and with computed input tensors: input[1] = <0>, input[2] = <1>, input[3] = <1>.
@bkungfoo
Copy link
Author

I've discovered a solution, which is always wrap your array of strings as a np.array, e.g.

np.array(['123', 'abc']) instead of ['123', 'abc']

Would appreciate some clarity though on why one works and not the other. Thanks!

@asimshankar
Copy link
Contributor

In the code snippet provided, tf.map_fn is being called on input_tensor, which wasn't defined in the snippet. But given that it's being fed into the sess.run(), I'm guessing it corresponds to a placeholder tensor, which, I think, means the original code was more like this:

import tensorflow as tf

def identity_map(input):
  return input

# Using tf.Session() and sess.run(). This runs without errors.
input = ['123', 'abc']
input_tensor = tf.placeholder(tf.string)
x = tf.map_fn(identity_map, input_tensor, dtype=tf.string)
x = tf.stack(x)
x = tf.reshape(tensor=x, shape=[-1])

with tf.Session() as sess:
  result = sess.run(x, {input_tensor: input})
  print(result)

Writing the test the same way works as well:

class SimpleTest(tf.test.TestCase):
  def testMapString(self):
    input = ['123', 'abc']
    with self.test_session():
      # Run map function
      input_tensor = tf.placeholder(tf.string)
      x = tf.map_fn(identity_map, input_tensor, dtype=tf.string)
      x = tf.stack(x)
      x = tf.reshape(tensor=x, shape=[-1])
      result = x.eval(feed_dict={input_tensor:input})
      print(result)

The behavior you see in the test should be consistent with the behavior you see outside the test. Please do let me know if I'm mistaken. That said, coming to why you get an error with:

tf.map_fn(identity_map, ['123', '456'], dtype=tf.string)

but don't get the error with:

tf.map_fn(identity_map, tf.convert_to_tensor(['123', '456']), dtype=tf.string)

or

tf.map_fn(identity_map, np.array(['123', '456']), dtype=tf.string)

This is because in the latter two cases, the elems parameter to tf.map_fn is a single tensor (a vector of 2 elements). While the the first case, elems is being interpreted as a sequence of tensors (instead of a single tensor). tf.map_fn requires that elems be either a single Tensor with more than 1 dimension, or elems be a sequence of Tensors, each with more than 1 dimension.

In the erroneous case, elems is a sequence of scalar (0-dimensional) tensors, which is where the error is coming from.

The error message could certainly be improved, and I'll try to do that. But hopefully the response makes sense?

@asimshankar asimshankar added the type:support Support issues label Apr 3, 2018
case540 pushed a commit to case540/tensorflow that referenced this issue Apr 5, 2018
Fixes tensorflow#17694
Prior to this change, when tf.map_fn was provided with scalars, the error would
be something like:

Traceback (most recent call last):
  File "/tensorflow/python/kernel_tests/functional_ops_test.py", line 165, in testMapOverScalarErrors
    functional_ops.map_fn(lambda x: x, [1, 2])
  File "/tensorflow/python/ops/functional_ops.py", line 368, in map_fn
    n = elems_flat[0].shape[0].value or array_ops.shape(elems_flat[0])[0]
  File "/tensorflow/python/framework/tensor_shape.py", line 609, in __getitem__
    return self._dims[key]
IndexError: list index out of range
PiperOrigin-RevId: 191465183
@bkungfoo
Copy link
Author

bkungfoo commented Apr 5, 2018

Thanks, I think I got it. Basically, passing a list directly into sess.run() in a test_session() is treated as sequences of elements rather than a tensor of elements.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type:support Support issues
Projects
None yet
Development

No branches or pull requests

2 participants