tensorflow: tf.make_ndarray() throws an AttributeError: 'Tensor' object has no attribute 'tensor_shape'

System information

  • Have I written custom code (as opposed to using a stock example script provided in TensorFlow): yes
  • OS Platform and Distribution: Linux Ubuntu 16.04
  • TensorFlow installed from (source or binary): binary
  • TensorFlow version (use command below): 1.12.2
  • Python version: 3.5
  • CUDA/cuDNN version: V8.0.61
  • GPU model and memory: Tesla P100-PCIE, 12193MiB

Describe the current behavior When calling tf.make_ndarray() on a tensor (<class 'tensorflow.python.framework.ops.Tensor'>, dtype uint8, shape (?, 1000, 1500)), I get the following error:

File "/home/.../python3.5/site-packages/tensorflow/python/framework/tensor_util.py", line 563, in MakeNdarray shape = [d.size for d in tensor.tensor_shape.dim] AttributeError: 'Tensor' object has no attribute 'tensor_shape'

I call the function inside a function given to tf.Dataset.map(), which is not executing eagerly. However, the error also occurs when using eager execution (as tested by using a for-loop on the dataset instead of .map()).

Describe the expected behavior I expect tf.make_ndarray() to return a NumPy array as advertised 😃

Code to reproduce the issue My dataset: https://we.tl/t-rZYNJ6I9oU

from __future__ import absolute_import, division, print_function
import tensorflow as tf
import numpy as np
import os
import sys

def decode_png_mask(image_buffer):

    """
    Takes string of bytes encoding a PNG and produces a tensor image
    """
    image = tf.squeeze(tf.image.decode_image(image_buffer, channels=1), axis=2)
    image.set_shape([None, None])
    image = tf.greater(image, 0)
    image = tf.cast(image, dtype=tf.uint8)
    return image

def masks_to_onehots(tag_masks, tag_class_indices, num_classes):

    def onehotify(pixel_tag_masks):
        nonzero_indices = np.flatnonzero(pixel_tag_masks)
        tag_mask_sizes_suppressed = [tag_mask_sizes[i] if i in nonzero_indices else 9999999 for i in range(len(tag_mask_sizes))]
        smallest_mask_index = np.argmin(tag_mask_sizes_suppressed)
        class_index = tag_class_indices[smallest_mask_index]
        onehot = eye[class_index]
        return onehot

    eye = np.eye(num_classes)
    tag_masks = tf.make_ndarray(tag_masks) #tag_masks = tag_masks.numpy()
    tag_class_indices = tf.make_ndarray(tag_class_indices) #tag_class_indices = tag_class_indices.numpy()
    tag_mask_sizes = np.sum(tag_masks, axis=(1, 2))
    image_masks = np.transpose(tag_masks, axes=[1, 2, 0])
    onehots = np.apply_along_axis(onehotify, axis=2, arr=image_masks)
    #onehots = np.array([[eye[tag_class_indices[np.argmin([tag_mask_sizes[i] if i in np.flatnonzero(pixel_tag_masks) else 9999999 for i in range(len(tag_mask_sizes))])]] for pixel_tag_masks in image_masks[height]] for height in range(image_masks.shape[0])])
    return tf.convert_to_tensor(onehots)

def parse_example(example_proto, width, height, num_classes):
    features = {
        'image/encoded': tf.FixedLenFeature((), tf.string),
        'image/height': tf.FixedLenFeature((), tf.int64),
        'image/width': tf.FixedLenFeature((), tf.int64),
        'image/filename': tf.FixedLenFeature((), tf.string),
        'image/object/bbox/xmin': tf.VarLenFeature(tf.float32),
        'image/object/bbox/xmax': tf.VarLenFeature(tf.float32),
        'image/object/bbox/ymin': tf.VarLenFeature(tf.float32),
        'image/object/bbox/ymax': tf.VarLenFeature(tf.float32),
        'image/object/class/label': tf.VarLenFeature(tf.int64),
        'image/object/class/text': tf.VarLenFeature(tf.string),
        'image/object/mask': tf.VarLenFeature(tf.string),
        'image/depth': tf.FixedLenFeature((), tf.string)
    }

    parsed_example = tf.parse_single_example(example_proto, features)

    # Decode image
    image = tf.image.decode_jpeg(parsed_example['image/encoded'])
    parsed_example['image/encoded'] = image

    tag_masks = tf.sparse.to_dense(parsed_example['image/object/mask'], default_value="")
    tag_masks = tf.map_fn(decode_png_mask, tag_masks, dtype=tf.uint8)
    tag_masks = tf.reshape(tag_masks, shape=tf.stack([-1, height, width]), name='tag_masks')

    # All segmentation now have their mask in mask, their labelmap index in classes_indices and their tagname in classes_text
    tag_class_indices = tf.sparse.to_dense(parsed_example['image/object/class/label'])
    tag_class_names = tf.sparse.to_dense(parsed_example['image/object/class/text'], default_value="")
    onehots = masks_to_onehots(tag_masks, tag_class_indices, num_classes)
    parsed_example['image/labels'] = onehots

    return parsed_example

tf.enable_eager_execution()

NUM_CLASSES = 21

tfrecord_train = "/path/to/tf.record"
dataset_train = tf.data.TFRecordDataset(tfrecord_train)

# Read image widht/height from the TFRecord file
iterator = dataset_train.make_one_shot_iterator()
next_element = iterator.get_next()
parsed_element = np.fromstring(next_element.numpy(), dtype=np.uint8)
example = tf.train.Example.FromString(parsed_element)
height = example.features.feature['image/height'].int64_list.value[0]
width = example.features.feature['image/width'].int64_list.value[0]

dataset_train = dataset_train.map(lambda x: parse_example(x, width, height, NUM_CLASSES))

Other info / logs Full traceback:

Traceback (most recent call last): File "/home/local/CYCLOMEDIA001/ebos/Workspace/all_cf/src/experimental/cityfusion/tasks/material_segmentation/semantic_fpn.py", line 114, in <module> dataset_train = dataset_train.map(lambda x: parse_example(x, width, height, NUM_CLASSES)) File "/home/.../python3.5/site-packages/tensorflow/python/data/ops/dataset_ops.py", line 1038, in map return MapDataset(self, map_func) File "/home/.../python3.5/site-packages/tensorflow/python/data/ops/dataset_ops.py", line 2611, in __init__ map_func, "Dataset.map()", input_dataset) File "/home/.../python3.5/site-packages/tensorflow/python/data/ops/dataset_ops.py", line 1860, in __init__ self._function.add_to_graph(ops.get_default_graph()) File "/home/.../python3.5/site-packages/tensorflow/python/framework/function.py", line 479, in add_to_graph self._create_definition_if_needed() File "/home/.../python3.5/site-packages/tensorflow/python/framework/function.py", line 335, in _create_definition_if_needed self._create_definition_if_needed_impl() File "/home/.../python3.5/site-packages/tensorflow/python/framework/function.py", line 344, in _create_definition_if_needed_impl self._capture_by_value, self._caller_device) File "/home/.../python3.5/site-packages/tensorflow/python/framework/function.py", line 864, in func_graph_from_py_func outputs = func(*func_graph.inputs) File "/home/.../python3.5/site-packages/tensorflow/python/data/ops/dataset_ops.py", line 1794, in tf_data_structured_function_wrapper ret = func(*nested_args) File "/path/to/script.py", line 114, in <lambda> dataset_train = dataset_train.map(lambda x: parse_example(x, width, height, NUM_CLASSES)) File "path/to/script.py", line 86, in parse_example onehots = masks_to_onehots(tag_masks, tag_class_indices, num_classes) File "/path/to/script.py", line 25, in masks_to_onehots tag_masks = tf.make_ndarray(tag_masks) #tag_masks = tag_masks.numpy() File "/home/.../python3.5/site-packages/tensorflow/python/framework/tensor_util.py", line 563, in MakeNdarray shape = [d.size for d in tensor.tensor_shape.dim] AttributeError: 'Tensor' object has no attribute 'tensor_shape'

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 1
  • Comments: 24 (8 by maintainers)

Most upvoted comments

The handling of this issue speaks volumes of why the research community is moving from tensorflow to pytorch.

I am having the same issue, trying to use tf.make_ndarray in tf.Dataset.map call

I am also stuck with the same bug. And yes it is a bug since this is 100% non-pythonic. Please fix it. Eager execution in Tensorflow2+ is a lie that made me waste more time than it should. It should be instead “eager execution sometimes”

tf.make_ndarray is used to convert TensorProto values into NumPy arrays, not tf.Tensor objects. These values are generally the constants used in a graph. For example, in graph mode, when you use tf.constant, you create a Const operation with an attribute value holding the constant value that the operation will produce. That attribute is stored as a TensorProto. Hence, you can “extract” the value of a Const operation as a NumPy array like this (TF 1.x):

import tensorflow as tf

A = tf.constant(5)
C = tf.make_ndarray(A.op.get_attr('value'))
print(C, type(C))
# 5 <class 'numpy.ndarray'>

In general, though, you cannot convert arbitrary tensors into NumPy arrays in graph mode, as their values will depend on the values of the variables and the fed inputs within a particular session.

Why is this issue still closed?

can this issue be re-opened? I am experiencing the same problem as described above.

tf.make_ndarray is used to convert TensorProto values into NumPy arrays, not tf.Tensor objects. These values are generally the constants used in a graph. For example, in graph mode, when you use tf.constant, you create a Const operation with an attribute value holding the constant value that the operation will produce. That attribute is stored as a TensorProto. Hence, you can “extract” the value of a Const operation as a NumPy array like this (TF 1.x):

import tensorflow as tf

A = tf.constant(5)
C = tf.make_ndarray(A.op.get_attr('value'))
print(C, type(C))
# 5 <class 'numpy.ndarray'>

In general, though, you cannot convert arbitrary tensors into NumPy arrays in graph mode, as their values will depend on the values of the variables and the fed inputs within a particular session.

Why is this a thing though?

I know this should not happen. Unfortunately the code is quite long and complex, my workaround was to pre-process the data with numpy before feeding it to tensorflow, so I couldn’t really solve this issue.

On other forums I found solutions which involved changing NumPy version and/or TF version.

All in all it’s very frustrating that such a simple operation required so much time and effort, on PyTorch this is something that never gave me headaches, not even close.

This issue keeps popping up in my inbox and I don’t even know why as there really is no bug to solve here. I already explained this above, but let’s go through it again for TensorFlow 2.

tf.make_ndarray is NOT a general function to convert tensors to NumPy arrays (in spite to what the admittedly misleading documentation of the function might say). What it does is it converts a TensorProto, which is a low-level binary representation of a tensor, into a NumPy array. Most typical TensorFlow code will never use TensorProto objects (directly), so tf.make_ndarray is just not a useful function for most people. Depending on the case, it may be possible to build a NumPy array from a tensor with it, but, again, its purpose is not to convert tensors to NumPy arrays in general, and it should rarely or never be used.

If you want to convert a TensorFlow tensor into a NumPy array, you need to be mindful that TensorFlow code may run in eager mode or graph mode. Graph mode is less “convenient” to use, but it is important for performance, optimisation, serialisation and other reasons. Although it is rare to explicitly enter into graph mode as a library user, it is very normal to write code that runs in graph mode, like the code in a Keras model, for example. It is not always straightforward to know what mode your code is running, especially since TensorFlow aims to hide this complexity from the library users, but the complexity is still there and it is important to understand it.

If you are in eager mode, you can just do .numpy() on your tensor. In graph mode, though, you CANNOT obtain the NumPy array corresponding to a tensor, because tensors do not hold any value in particular in graph mode, but they rather expresses a symbolic intermediate results (with some exceptions like tf.constant, which is possible to convert into a NumPy array even in graph mode, because it always has the same value). If you want to do a NumPy operation with your tensor in graph mode (which would not be differentiable in TensorFlow btw), you need to temporarily switch to eager mode within graph mode, which you can do with tf.py_function. There, your tensors will be eager tensors and you can call .numpy() on them.

NB: I am not a TensorFlow developer nor have any affiliation of any kind with anyone involved with its development.

Since TensorFlow 1.0, tf.Tensor now has a tf.Tensor.shape property, which returns the same value as tf.Tensor.get_shape(). In versions prior to TensorFlow 1.0 tf.Tensor doesn’t have a .shape property. You should use the Tensor.get_shape() method instead: Hope this helps.

Did anyone manage to solve this one? It’s unbelievable the amount of hassle you need to go through to perform such a simple operation. Really unbelievable.

@ymodak No, because .numpy() only works in eager execution, and code ran through tf.Dataset.map() isn’t executed eagerly. This is the reason I try to rely on .make_ndarray(). Both tag_masks and tag_class_indices are <class 'tensorflow.python.framework.ops.Tensor'>.

I already moved on though. I abandoned TFRecords because they are getting me absolutely nowhere. However, other people might run into this same problem.

I am able to reproduce the issue reported here on my system with same configuration. Got the error AttributeError: ‘Tensor’ object has no attribute ‘tensor_shape’