From d38475eb2e3b37437f5002407e0bafe2ffea1d6d Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Fri, 27 Mar 2020 10:29:03 -0700 Subject: [PATCH] Clarifies meaning of is_tensor in the documentation. Also clarifies documentation of TensorLike. Makes RaggedTensor subclass TensorLike. Also renames _TensorLike to TensorLike since we expect more usages of it. PiperOrigin-RevId: 303357719 Change-Id: Ieea5b0941e2149e2ad4670e449c72c471685cf37 --- .../autograph/operators/control_flow.py | 7 ++---- .../data/kernel_tests/memory_cleanup_test.py | 2 +- tensorflow/python/framework/indexed_slices.py | 8 ++++-- tensorflow/python/framework/ops.py | 18 ++++++------- tensorflow/python/framework/sparse_tensor.py | 3 ++- tensorflow/python/framework/tensor_like.py | 9 +++---- tensorflow/python/framework/tensor_util.py | 25 ++++++++----------- .../python/keras/engine/training_utils.py | 17 +++---------- tensorflow/python/ops/ragged/ragged_tensor.py | 3 +-- .../v1/tensorflow.-indexed-slices.pbtxt | 2 +- .../golden/v1/tensorflow.-ragged-tensor.pbtxt | 1 - .../golden/v1/tensorflow.-sparse-tensor.pbtxt | 2 +- .../api/golden/v1/tensorflow.-tensor.pbtxt | 2 +- .../v1/tensorflow.sparse.-sparse-tensor.pbtxt | 2 +- .../v2/tensorflow.-indexed-slices.pbtxt | 2 +- .../golden/v2/tensorflow.-ragged-tensor.pbtxt | 1 - .../golden/v2/tensorflow.-sparse-tensor.pbtxt | 2 +- .../api/golden/v2/tensorflow.-tensor.pbtxt | 2 +- .../v2/tensorflow.sparse.-sparse-tensor.pbtxt | 2 +- 19 files changed, 44 insertions(+), 66 deletions(-) diff --git a/tensorflow/python/autograph/operators/control_flow.py b/tensorflow/python/autograph/operators/control_flow.py index 40493f07a2d..a1ef5eeedab 100644 --- a/tensorflow/python/autograph/operators/control_flow.py +++ b/tensorflow/python/autograph/operators/control_flow.py @@ -340,11 +340,8 @@ def for_stmt(iter_, extra_test, body, get_state, set_state, symbol_names, opts): """ if tensor_util.is_tensor(iter_): if tensors.is_range_tensor(iter_): - _tf_range_for_stmt(iter_, extra_test, body, get_state, set_state, - symbol_names, opts) - elif isinstance(iter_, ragged_tensor.RaggedTensor): - _tf_ragged_for_stmt(iter_, extra_test, body, get_state, set_state, - symbol_names, opts) + _tf_range_for_stmt( + iter_, extra_test, body, get_state, set_state, symbol_names, opts) else: _known_len_tf_for_stmt( iter_, extra_test, body, get_state, set_state, symbol_names, opts) diff --git a/tensorflow/python/data/kernel_tests/memory_cleanup_test.py b/tensorflow/python/data/kernel_tests/memory_cleanup_test.py index 8ba9d4c925a..4917d6ec163 100644 --- a/tensorflow/python/data/kernel_tests/memory_cleanup_test.py +++ b/tensorflow/python/data/kernel_tests/memory_cleanup_test.py @@ -116,7 +116,7 @@ class MemoryCleanupTest(test_base.DatasetTestBase, parameterized.TestCase): gc.collect() tensors = [ - o for o in gc.get_objects() if isinstance(o, tensor_like.TensorLike) + o for o in gc.get_objects() if isinstance(o, tensor_like._TensorLike) ] self.assertEmpty(tensors, "%d Tensors are still alive." % len(tensors)) diff --git a/tensorflow/python/framework/indexed_slices.py b/tensorflow/python/framework/indexed_slices.py index c1b3a1775ec..abf90547e50 100644 --- a/tensorflow/python/framework/indexed_slices.py +++ b/tensorflow/python/framework/indexed_slices.py @@ -54,9 +54,13 @@ tensor_util = LazyLoader( "tensor_util", globals(), "tensorflow.python.framework.tensor_util") +# pylint: disable=protected-access +_TensorLike = tensor_like._TensorLike +# pylint: enable=protected-access + @tf_export("IndexedSlices") -class IndexedSlices(tensor_like.TensorLike, composite_tensor.CompositeTensor): +class IndexedSlices(_TensorLike, composite_tensor.CompositeTensor): """A sparse representation of a set of tensor slices at given indices. This class is a simple wrapper for a pair of `Tensor` objects: @@ -305,7 +309,7 @@ def internal_convert_to_tensor_or_indexed_slices(value, """ if isinstance(value, ops.EagerTensor) and not context.executing_eagerly(): return ops.convert_to_tensor(value, dtype=dtype, name=name, as_ref=as_ref) - elif isinstance(value, tensor_like.TensorLike): + elif isinstance(value, _TensorLike): if dtype and not dtypes.as_dtype(dtype).is_compatible_with(value.dtype): raise ValueError( "Tensor conversion requested dtype %s for Tensor with dtype %s: %r" % diff --git a/tensorflow/python/framework/ops.py b/tensorflow/python/framework/ops.py index 9c752f557c5..0df275f9093 100644 --- a/tensorflow/python/framework/ops.py +++ b/tensorflow/python/framework/ops.py @@ -94,6 +94,7 @@ _api_usage_gauge = monitoring.BoolGauge( # pylint: disable=protected-access +_TensorLike = tensor_like._TensorLike _DTYPES_INTERN_TABLE = dtypes._INTERN_TABLE # pylint: enable=protected-access @@ -289,7 +290,7 @@ def disable_tensor_equality(): @tf_export("Tensor") -class Tensor(tensor_like.TensorLike): +class Tensor(_TensorLike): """A tensor is a multidimensional array of elements represented by a `tf.Tensor` object. All elements are of a single known data type. @@ -5982,12 +5983,9 @@ def _assert_same_graph(original_item, item): Raises: ValueError: if graphs do not match. """ - original_graph = getattr(original_item, "graph", None) - graph = getattr(item, "graph", None) - if original_graph and graph and original_graph is not graph: - raise ValueError( - "%s must be from the same graph as %s (graphs are %s and %s)." % - (item, original_item, graph, original_graph)) + if original_item.graph is not item.graph: + raise ValueError("%s must be from the same graph as %s." % + (item, original_item)) def _get_graph_from_inputs(op_input_list, graph=None): @@ -6041,16 +6039,16 @@ def _get_graph_from_inputs(op_input_list, graph=None): # TODO(josh11b): Note that we exclude subclasses of Tensor. Need to clean this # up. graph_element = None - if (isinstance(op_input, (Operation, tensor_like.TensorLike)) and + if (isinstance(op_input, (Operation, _TensorLike)) and ((not isinstance(op_input, Tensor)) or type(op_input) == Tensor)): # pylint: disable=unidiomatic-typecheck graph_element = op_input - elif isinstance(op_input, Tensor): + else: graph_element = _as_graph_element(op_input) if graph_element is not None: if not graph: original_graph_element = graph_element - graph = getattr(graph_element, "graph", None) + graph = graph_element.graph elif original_graph_element is not None: _assert_same_graph(original_graph_element, graph_element) elif graph_element.graph is not graph: diff --git a/tensorflow/python/framework/sparse_tensor.py b/tensorflow/python/framework/sparse_tensor.py index a175f80a6c3..9af74ed569d 100644 --- a/tensorflow/python/framework/sparse_tensor.py +++ b/tensorflow/python/framework/sparse_tensor.py @@ -38,13 +38,14 @@ from tensorflow.python.ops import gen_sparse_ops from tensorflow.python.util.tf_export import tf_export # pylint: disable=protected-access +_TensorLike = tensor_like._TensorLike _eval_using_default_session = ops._eval_using_default_session _override_helper = ops._override_helper # pylint: enable=protected-access @tf_export("sparse.SparseTensor", "SparseTensor") -class SparseTensor(tensor_like.TensorLike, composite_tensor.CompositeTensor): +class SparseTensor(_TensorLike, composite_tensor.CompositeTensor): """Represents a sparse tensor. TensorFlow represents a sparse tensor as three separate dense tensors: diff --git a/tensorflow/python/framework/tensor_like.py b/tensorflow/python/framework/tensor_like.py index e8fe2f2fc05..5fc2d3dd01f 100644 --- a/tensorflow/python/framework/tensor_like.py +++ b/tensorflow/python/framework/tensor_like.py @@ -19,10 +19,7 @@ from __future__ import division from __future__ import print_function -class TensorLike(object): - """TF-specific types TF operations are expected to natively support. - - Do not check this with isinstance directly; prefer instead using - `tf.is_tensor` to check whether converting to a tensor is necessary. - """ +# NOTE(ebrevdo): Do not subclass this. If you do, I will break you on purpose. +class _TensorLike(object): + """Internal cls for grouping Tensor, SparseTensor, ..., for is_instance.""" pass diff --git a/tensorflow/python/framework/tensor_util.py b/tensorflow/python/framework/tensor_util.py index 30cac68bfc5..895fedc3649 100644 --- a/tensorflow/python/framework/tensor_util.py +++ b/tensorflow/python/framework/tensor_util.py @@ -978,23 +978,17 @@ def constant_value_as_shape(tensor): # pylint: disable=invalid-name @tf_export("is_tensor") def is_tensor(x): # pylint: disable=invalid-name - """Checks whether `x` is a TF-native type that can be passed to many TF ops. + """Checks whether `x` is a tensor or "tensor-like". - Use is_tensor to differentiate types that can ingested by TensorFlow ops - without any conversion (e.g., `tf.Tensor`, `tf.SparseTensor`, and - `tf.RaggedTensor`) from types that need to be converted into tensors before - they are ingested (e.g., numpy `ndarray` and Python scalars). + If `is_tensor(x)` returns `True`, it is safe to assume that `x` is a tensor or + can be converted to a tensor using `ops.convert_to_tensor(x)`. - For example, in the following code block: + Usage example: - ```python - if not tf.is_tensor(t): - t = tf.convert_to_tensor(t) - return t.dtype - ``` - - we check to make sure that `t` is a tensor (and convert it if not) before - accessing its `shape` and `dtype`. + >>> tf.is_tensor(tf.constant([[1,2,3],[4,5,6],[7,8,9]])) + True + >>> tf.is_tensor("Hello World") + False Args: x: A python object to check. @@ -1002,7 +996,8 @@ def is_tensor(x): # pylint: disable=invalid-name Returns: `True` if `x` is a tensor or "tensor-like", `False` if not. """ - return (isinstance(x, (tensor_like.TensorLike, core.Tensor)) or + # TODO(mdan): Remove these. Only keep core.Tensor. + return (isinstance(x, (tensor_like._TensorLike, core.Tensor)) or # pylint: disable=protected-access ops.is_dense_tensor_like(x) or getattr(x, "is_tensor_like", False)) diff --git a/tensorflow/python/keras/engine/training_utils.py b/tensorflow/python/keras/engine/training_utils.py index 680f33f75a5..d901c4986db 100644 --- a/tensorflow/python/keras/engine/training_utils.py +++ b/tensorflow/python/keras/engine/training_utils.py @@ -56,7 +56,6 @@ from tensorflow.python.ops import array_ops from tensorflow.python.ops import gen_array_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops.losses import util as tf_losses_utils -from tensorflow.python.ops.ragged import ragged_tensor from tensorflow.python.platform import tf_logging as logging from tensorflow.python.util import nest from tensorflow.python.util import tf_inspect @@ -1050,21 +1049,11 @@ def has_symbolic_tensors(ls): def has_tensors(ls): - """Returns true if `ls` contains tensors.""" - # Note: at some point in time ragged tensors didn't count as tensors, so this - # returned false for ragged tensors. Making this return true fails some tests - # which would then require a steps_per_epoch argument. if isinstance(ls, (list, tuple)): - return any( - tensor_util.is_tensor(v) and - not isinstance(v, ragged_tensor.RaggedTensor) for v in ls) + return any(tensor_util.is_tensor(v) for v in ls) if isinstance(ls, dict): - return any( - tensor_util.is_tensor(v) and - not isinstance(v, ragged_tensor.RaggedTensor) - for _, v in six.iteritems(ls)) - return tensor_util.is_tensor(ls) and not isinstance( - ls, ragged_tensor.RaggedTensor) + return any(tensor_util.is_tensor(v) for _, v in six.iteritems(ls)) + return tensor_util.is_tensor(ls) def get_metric_name(metric, weighted=False): diff --git a/tensorflow/python/ops/ragged/ragged_tensor.py b/tensorflow/python/ops/ragged/ragged_tensor.py index a3a2a59204f..a5bdb5fb977 100644 --- a/tensorflow/python/ops/ragged/ragged_tensor.py +++ b/tensorflow/python/ops/ragged/ragged_tensor.py @@ -30,7 +30,6 @@ from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import sparse_tensor -from tensorflow.python.framework import tensor_like from tensorflow.python.framework import tensor_shape from tensorflow.python.framework import tensor_spec from tensorflow.python.framework import type_spec @@ -55,7 +54,7 @@ _convert_row_partition = RowPartition._convert_row_partition @tf_export("RaggedTensor") -class RaggedTensor(composite_tensor.CompositeTensor, tensor_like.TensorLike): +class RaggedTensor(composite_tensor.CompositeTensor): """Represents a ragged tensor. A `RaggedTensor` is a tensor with one or more *ragged dimensions*, which are diff --git a/tensorflow/tools/api/golden/v1/tensorflow.-indexed-slices.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.-indexed-slices.pbtxt index e9e805426a1..780f5e5cb6a 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.-indexed-slices.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.-indexed-slices.pbtxt @@ -1,7 +1,7 @@ path: "tensorflow.IndexedSlices" tf_class { is_instance: "" - is_instance: "" + is_instance: "" is_instance: "" is_instance: "" member { diff --git a/tensorflow/tools/api/golden/v1/tensorflow.-ragged-tensor.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.-ragged-tensor.pbtxt index 44a66874e70..920a48a2294 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.-ragged-tensor.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.-ragged-tensor.pbtxt @@ -2,7 +2,6 @@ path: "tensorflow.RaggedTensor" tf_class { is_instance: "" is_instance: "" - is_instance: "" is_instance: "" member { name: "dtype" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.-sparse-tensor.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.-sparse-tensor.pbtxt index d71812ce83a..261a6ae35a4 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.-sparse-tensor.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.-sparse-tensor.pbtxt @@ -1,7 +1,7 @@ path: "tensorflow.SparseTensor" tf_class { is_instance: "" - is_instance: "" + is_instance: "" is_instance: "" is_instance: "" member { diff --git a/tensorflow/tools/api/golden/v1/tensorflow.-tensor.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.-tensor.pbtxt index 33742e3b867..fd80af51d2a 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.-tensor.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.-tensor.pbtxt @@ -1,7 +1,7 @@ path: "tensorflow.Tensor" tf_class { is_instance: "" - is_instance: "" + is_instance: "" is_instance: "" member { name: "OVERLOADABLE_OPERATORS" diff --git a/tensorflow/tools/api/golden/v1/tensorflow.sparse.-sparse-tensor.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.sparse.-sparse-tensor.pbtxt index a3ea216468e..a563015afe3 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.sparse.-sparse-tensor.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.sparse.-sparse-tensor.pbtxt @@ -1,7 +1,7 @@ path: "tensorflow.sparse.SparseTensor" tf_class { is_instance: "" - is_instance: "" + is_instance: "" is_instance: "" is_instance: "" member { diff --git a/tensorflow/tools/api/golden/v2/tensorflow.-indexed-slices.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.-indexed-slices.pbtxt index e9e805426a1..780f5e5cb6a 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.-indexed-slices.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.-indexed-slices.pbtxt @@ -1,7 +1,7 @@ path: "tensorflow.IndexedSlices" tf_class { is_instance: "" - is_instance: "" + is_instance: "" is_instance: "" is_instance: "" member { diff --git a/tensorflow/tools/api/golden/v2/tensorflow.-ragged-tensor.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.-ragged-tensor.pbtxt index 44a66874e70..920a48a2294 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.-ragged-tensor.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.-ragged-tensor.pbtxt @@ -2,7 +2,6 @@ path: "tensorflow.RaggedTensor" tf_class { is_instance: "" is_instance: "" - is_instance: "" is_instance: "" member { name: "dtype" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.-sparse-tensor.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.-sparse-tensor.pbtxt index d71812ce83a..261a6ae35a4 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.-sparse-tensor.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.-sparse-tensor.pbtxt @@ -1,7 +1,7 @@ path: "tensorflow.SparseTensor" tf_class { is_instance: "" - is_instance: "" + is_instance: "" is_instance: "" is_instance: "" member { diff --git a/tensorflow/tools/api/golden/v2/tensorflow.-tensor.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.-tensor.pbtxt index 33742e3b867..fd80af51d2a 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.-tensor.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.-tensor.pbtxt @@ -1,7 +1,7 @@ path: "tensorflow.Tensor" tf_class { is_instance: "" - is_instance: "" + is_instance: "" is_instance: "" member { name: "OVERLOADABLE_OPERATORS" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.sparse.-sparse-tensor.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.sparse.-sparse-tensor.pbtxt index a3ea216468e..a563015afe3 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.sparse.-sparse-tensor.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.sparse.-sparse-tensor.pbtxt @@ -1,7 +1,7 @@ path: "tensorflow.sparse.SparseTensor" tf_class { is_instance: "" - is_instance: "" + is_instance: "" is_instance: "" is_instance: "" member {