23

Consider the following code:

x = tf.placeholder("float", shape=[42, 4])
y = tf.zeros([42, 4], "float")
xy_stacked = tf.concat(1, [x, y])

print(x.get_shape())
print(y.get_shape())
print(xy_stacked.get_shape())

This will produce the following output, as expected:

TensorShape([Dimension(42), Dimension(4)])
TensorShape([Dimension(42), Dimension(4)])
TensorShape([Dimension(42), Dimension(8)])

However, what if the placeholder has a dynamic dimension that is determined at run-time by the value passed to feed_dict=, as placeholders often do:

x = tf.placeholder("float", shape=[None, 4])
y = tf.zeros([None, 4], "float")
xy_stacked = tf.concat(1, [x, y])

This will produce an error for tf.zeros([None, 4], "float"). Apparently Dimension(None) is not allowed for tf.zeros:

TypeError                                 Traceback (most recent call last)
<ipython-input-24-277eca38a392> in <module>()
      2 
      3 x = tf.placeholder("float", shape=[None, 4])
----> 4 y = tf.zeros([None, 4], "float")
      5 xy_stacked = tf.concat(1, [x, y])
      6 
[...]

/usr/local/lib/python3.4/dist-packages/numpy/core/_methods.py in _prod(a, axis, dtype, out, keepdims)
     33 
     34 def _prod(a, axis=None, dtype=None, out=None, keepdims=False):
---> 35     return umr_prod(a, axis, dtype, out, keepdims)
     36 
     37 def _any(a, axis=None, dtype=None, out=None, keepdims=False):

TypeError: unsupported operand type(s) for *: 'NoneType' and 'int'

I have figured out that it does not produce an error if I set the first dimension of my zeros tensor to non-None, such as 1:

x = tf.placeholder("float", shape=[None, 4])
y = tf.zeros([1, 4], "float")
xy_stacked = tf.concat(1, [x, y])

but then the resulting xy_stacked tensor is truncated to this size:

TensorShape([Dimension(None), Dimension(4)])
TensorShape([Dimension(1), Dimension(4)])
TensorShape([Dimension(1), Dimension(8)])

How can I pad the placeholder tensor with zeros so I get a tensor of shape TensorShape([Dimension(None), Dimension(8)]) in this example?

The only "solutions" I found so far is either something like the following:

x = tf.placeholder("float", shape=[None, 4])
y = 0 * x
xy_stacked = tf.concat(1, [x, y])

Or simply declaring y as a placeholder and always passing a zero array of the right size.

But neither looks like a clean solution to the problem and hacks like that get out of hand quickly in an application more complex than this simple example..

I'm using tensorflow-0.6.0-py3.

Guy Coder
  • 24,501
  • 8
  • 71
  • 136
CliffordVienna
  • 7,995
  • 1
  • 37
  • 57
  • what happens with `y=tf.zeros_like(x)`? – user728291 Jan 11 '16 at 11:36
  • @user728291 then I get something of shape `TensorShape([Dimension(None), Dimension(None)])` for `y` and `xy_stacked`!? The `0 * x` hack I mentioned in the question seems to makes more sense if `x` actually should have the same shape as `y`. In my real app everything is more complicated of course and I have to construct the zeros tensor by slicing, duplicating and concatenating the result of `0 * ` to get a tensor of the shape I need. – CliffordVienna Jan 11 '16 at 11:55

1 Answers1

41

The recommended way to make a zero tensor with the same shape as another tensor is to use the tf.zeros_like() op:

x = tf.placeholder(tf.float32, shape=[None, 4])
y = tf.zeros_like(x)

The resulting tensor y appears to have the shape [None, None] according to Tensor.get_shape(), but at runtime it will expand to the same shape as x:

print y.get_shape()
# ==> TensorShape([Dimension(None), Dimension(None)])

sess = tf.Session()
y_result = sess.run(y, feed_dict={x: np.random.rand(4, 4)})

print y_result.shape
# ==> (4, 4)

The [None, None] static shape is returned because shape inference hasn't been specialized for tf.zeros_like(). I've filed a GitHub issue for that and it should be fixed soon.


EDIT: In your comment, you asked how to deal with the case where the zero tensor had a shape based on, but different from the original tensor. This is also possible, using tf.shape() and tf.stack() to build the dimensions, and tf.fill() to produce the zero tensor:

x = tf.placeholder(tf.float32, shape=[None, 4])

# Use tf.shape() to get the runtime size of `x` in the 0th dimension.
zeros_dims = tf.stack([tf.shape(x)[0], 7])

y = tf.fill(zeros_dims, 0.0)

sess = tf.Session()
y_result = sess.run(y, feed_dict={x: np.random.rand(4, 4)})
print y_result.shape
# ==> (4, 7)
mrry
  • 125,488
  • 26
  • 399
  • 400
  • 1
    What do I do if the 2nd dimension does not match? E.g. I want `x` to have `shape=[None, 4]` and `y` to have `shape=[None, 7]`. (In my app the 2nd dimensions of those tensors are independent configuration parameters. I can't even tell in advance which one is larger.) Sorry for not mentioning that in the question in the first place.. – CliffordVienna Jan 11 '16 at 16:06
  • 2
    Updated my answer to cover your case. – mrry Jan 11 '16 at 16:26
  • Note that on tf 1.x+ tf.pack was renamed to tf.stack – Yuval Atzmon Oct 15 '17 at 11:31