Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/tensorflow/python/framework/indexed_slices.py: 46%
155 statements
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-03 07:57 +0000
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-03 07:57 +0000
1# Copyright 2019 The TensorFlow Authors. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14# ==============================================================================
15"""Indexed slices."""
17# pylint: disable=g-bad-name
18import collections
19import warnings
21import numpy as np
23from tensorflow.core.protobuf import struct_pb2
24from tensorflow.python import tf2
25from tensorflow.python.eager import context
26from tensorflow.python.framework import composite_tensor
27from tensorflow.python.framework import composite_tensor_gradient
28from tensorflow.python.framework import dtypes
29from tensorflow.python.framework import ops
30from tensorflow.python.framework import tensor_conversion_registry
31from tensorflow.python.framework import tensor_shape
32from tensorflow.python.framework import tensor_spec
33from tensorflow.python.framework import tensor_util
34from tensorflow.python.framework import type_spec
35from tensorflow.python.ops import gen_math_ops
36from tensorflow.python.saved_model import nested_structure_coder
37from tensorflow.python.types import internal
38from tensorflow.python.util.compat import collections_abc
39from tensorflow.python.util.tf_export import tf_export
42class IndexedSlicesCompositeTensorGradient(
43 composite_tensor_gradient.CompositeTensorGradient):
44 """CompositeTensorGradient for IndexedSlices."""
46 def get_gradient_components(self, value):
47 return value
49 def replace_gradient_components(self, value, component_grads):
50 return component_grads
53# TODO(mdan): Should IndexedSlices be a "tensor"?
54@tf_export("IndexedSlices")
55class IndexedSlices(
56 internal.IndexedSlices,
57 internal.NativeObject,
58 composite_tensor.CompositeTensor):
59 """A sparse representation of a set of tensor slices at given indices.
61 This class is a simple wrapper for a pair of `Tensor` objects:
63 * `values`: A `Tensor` of any dtype with shape `[D0, D1, ..., Dn]`.
64 * `indices`: A 1-D integer `Tensor` with shape `[D0]`.
66 An `IndexedSlices` is typically used to represent a subset of a larger
67 tensor `dense` of shape `[LARGE0, D1, .. , DN]` where `LARGE0 >> D0`.
68 The values in `indices` are the indices in the first dimension of
69 the slices that have been extracted from the larger tensor.
71 The dense tensor `dense` represented by an `IndexedSlices` `slices` has
73 ```python
74 dense[slices.indices[i], :, :, :, ...] = slices.values[i, :, :, :, ...]
75 ```
77 The `IndexedSlices` class is used principally in the definition of
78 gradients for operations that have sparse gradients
79 (e.g. `tf.gather`).
81 >>> v = tf.Variable([[0.,1, 2], [2, 3, 4], [4, 5, 6], [6, 7, 8]])
82 >>> with tf.GradientTape() as tape:
83 ... r = tf.gather(v, [1,3])
84 >>> index_slices = tape.gradient(r,v)
85 >>> index_slices
86 <...IndexedSlices object ...>
87 >>> index_slices.indices.numpy()
88 array([1, 3], dtype=int32)
89 >>> index_slices.values.numpy()
90 array([[1., 1., 1.],
91 [1., 1., 1.]], dtype=float32)
93 Contrast this representation with
94 `tf.sparse.SparseTensor`,
95 which uses multi-dimensional indices and scalar values.
96 """
98 def __init__(self, values, indices, dense_shape=None):
99 """Creates an `IndexedSlices`."""
100 self._values = values
101 self._indices = indices
102 self._dense_shape = dense_shape
104 @property
105 def values(self):
106 """A `Tensor` containing the values of the slices."""
107 return self._values
109 @property
110 def indices(self):
111 """A 1-D `Tensor` containing the indices of the slices."""
112 return self._indices
114 @property
115 def dense_shape(self):
116 """A 1-D `Tensor` containing the shape of the corresponding dense tensor."""
117 return self._dense_shape
119 @property
120 def shape(self):
121 """Gets the `tf.TensorShape` representing the shape of the dense tensor.
123 Returns:
124 A `tf.TensorShape` object.
125 """
126 if self._dense_shape is None:
127 return tensor_shape.TensorShape(None)
129 return tensor_util.constant_value_as_shape(self._dense_shape)
131 @property
132 def name(self):
133 """The name of this `IndexedSlices`."""
134 return self.values.name
136 @property
137 def device(self):
138 """The name of the device on which `values` will be produced, or `None`."""
139 return self.values.device
141 @property
142 def op(self):
143 """The `Operation` that produces `values` as an output."""
144 return self.values.op
146 @property
147 def dtype(self):
148 """The `DType` of elements in this tensor."""
149 return self.values.dtype
151 @property
152 def graph(self):
153 """The `Graph` that contains the values, indices, and shape tensors."""
154 return self._values.graph
156 def __str__(self):
157 return "IndexedSlices(indices=%s, values=%s%s)" % (
158 self._indices, self._values,
159 (", dense_shape=%s" %
160 (self._dense_shape,)) if self._dense_shape is not None else "")
162 def __neg__(self):
163 return IndexedSlices(-self.values, self.indices, self.dense_shape)
165 __composite_gradient__ = IndexedSlicesCompositeTensorGradient()
167 @property
168 def _type_spec(self):
169 indices_shape = self._indices.shape.merge_with(self._values.shape[:1])
170 dense_shape = tensor_shape.TensorShape([None]).concatenate(
171 self._values.shape[1:])
172 if self._dense_shape is not None:
173 dense_shape_dtype = self._dense_shape.dtype
174 dense_shape = dense_shape.merge_with(
175 tensor_util.constant_value_as_shape(self._dense_shape))
176 else:
177 dense_shape_dtype = None
178 return IndexedSlicesSpec(dense_shape, self.dtype, self._indices.dtype,
179 dense_shape_dtype, indices_shape)
181 def _shape_invariant_to_type_spec(self, shape):
182 # From tf.while_loop docs: "If a loop variable is an IndexedSlices, the
183 # shape invariant must be a shape invariant of the values tensor of the
184 # IndexedSlices. It means the shapes of the three tensors of the
185 # IndexedSlices are (shape, [shape[0]], [shape.ndims])."
186 indices_shape = shape[:1]
187 dense_shape = tensor_shape.TensorShape([None]).concatenate(shape[1:])
188 if self._dense_shape is None:
189 dense_shape_dtype = None
190 else:
191 dense_shape_dtype = self._dense_shape.dtype
192 return IndexedSlicesSpec(dense_shape, self.dtype, self._indices.dtype,
193 dense_shape_dtype, indices_shape)
195 def consumers(self):
196 return self._consumers()
199IndexedSlicesValue = collections.namedtuple(
200 "IndexedSlicesValue", ["values", "indices", "dense_shape"])
203@tf_export("IndexedSlicesSpec")
204class IndexedSlicesSpec(type_spec.TypeSpec):
205 """Type specification for a `tf.IndexedSlices`."""
207 __slots__ = ["_shape", "_values_dtype", "_indices_dtype",
208 "_dense_shape_dtype", "_indices_shape"]
210 value_type = property(lambda self: IndexedSlices)
212 def __init__(self, shape=None, dtype=dtypes.float32,
213 indices_dtype=dtypes.int64, dense_shape_dtype=None,
214 indices_shape=None):
215 """Constructs a type specification for a `tf.IndexedSlices`.
217 Args:
218 shape: The dense shape of the `IndexedSlices`, or `None` to allow any
219 dense shape.
220 dtype: `tf.DType` of values in the `IndexedSlices`.
221 indices_dtype: `tf.DType` of the `indices` in the `IndexedSlices`. One
222 of `tf.int32` or `tf.int64`.
223 dense_shape_dtype: `tf.DType` of the `dense_shape` in the `IndexedSlices`.
224 One of `tf.int32`, `tf.int64`, or `None` (if the `IndexedSlices` has
225 no `dense_shape` tensor).
226 indices_shape: The shape of the `indices` component, which indicates
227 how many slices are in the `IndexedSlices`.
228 """
229 self._shape = tensor_shape.as_shape(shape)
230 self._values_dtype = dtypes.as_dtype(dtype)
231 self._indices_dtype = dtypes.as_dtype(indices_dtype)
232 if dense_shape_dtype is None:
233 self._dense_shape_dtype = None
234 else:
235 self._dense_shape_dtype = dtypes.as_dtype(dense_shape_dtype)
236 self._indices_shape = tensor_shape.as_shape(indices_shape).with_rank(1)
238 def _serialize(self):
239 return (self._shape, self._values_dtype, self._indices_dtype,
240 self._dense_shape_dtype, self._indices_shape)
242 @property
243 def _component_specs(self):
244 value_shape = self._indices_shape.concatenate(self._shape[1:])
245 specs = [
246 tensor_spec.TensorSpec(value_shape, self._values_dtype),
247 tensor_spec.TensorSpec(self._indices_shape, self._indices_dtype)]
248 if self._dense_shape_dtype is not None:
249 specs.append(
250 tensor_spec.TensorSpec([self._shape.ndims], self._dense_shape_dtype))
251 return tuple(specs)
253 def _to_components(self, value):
254 if value.dense_shape is None:
255 return (value.values, value.indices)
256 else:
257 return (value.values, value.indices, value.dense_shape)
259 def _from_components(self, tensor_list):
260 if (all(isinstance(t, np.ndarray) for t in tensor_list) and
261 not tf2.enabled()):
262 if len(tensor_list) == 2:
263 return IndexedSlicesValue(tensor_list[0], tensor_list[1], None)
264 else:
265 return IndexedSlicesValue(*tensor_list)
266 else:
267 return IndexedSlices(*tensor_list)
270nested_structure_coder.register_codec(
271 nested_structure_coder.BuiltInTypeSpecCodec(
272 IndexedSlicesSpec, struct_pb2.TypeSpecProto.INDEXED_SLICES_SPEC
273 )
274)
277@tf_export(v1=["convert_to_tensor_or_indexed_slices"])
278def convert_to_tensor_or_indexed_slices(value, dtype=None, name=None):
279 """Converts the given object to a `Tensor` or an `IndexedSlices`.
281 If `value` is an `IndexedSlices` or `SparseTensor` it is returned
282 unmodified. Otherwise, it is converted to a `Tensor` using
283 `convert_to_tensor()`.
285 Args:
286 value: An `IndexedSlices`, `SparseTensor`, or an object that can be consumed
287 by `convert_to_tensor()`.
288 dtype: (Optional.) The required `DType` of the returned `Tensor` or
289 `IndexedSlices`.
290 name: (Optional.) A name to use if a new `Tensor` is created.
292 Returns:
293 A `Tensor`, `IndexedSlices`, or `SparseTensor` based on `value`.
295 Raises:
296 ValueError: If `dtype` does not match the element type of `value`.
297 """
298 return internal_convert_to_tensor_or_indexed_slices(
299 value=value, dtype=dtype, name=name, as_ref=False)
302def internal_convert_to_tensor_or_indexed_slices(value,
303 dtype=None,
304 name=None,
305 as_ref=False):
306 """Converts the given object to a `Tensor` or an `IndexedSlices`.
308 If `value` is an `IndexedSlices` or `SparseTensor` it is returned
309 unmodified. Otherwise, it is converted to a `Tensor` using
310 `convert_to_tensor()`.
312 Args:
313 value: An `IndexedSlices`, `SparseTensor`, or an object that can be consumed
314 by `convert_to_tensor()`.
315 dtype: (Optional.) The required `DType` of the returned `Tensor` or
316 `IndexedSlices`.
317 name: (Optional.) A name to use if a new `Tensor` is created.
318 as_ref: True if the caller wants the results as ref tensors.
320 Returns:
321 A `Tensor`, `IndexedSlices`, or `SparseTensor` based on `value`.
323 Raises:
324 ValueError: If `dtype` does not match the element type of `value`.
325 """
326 if isinstance(value, ops.EagerTensor) and not context.executing_eagerly():
327 return ops.convert_to_tensor(value, dtype=dtype, name=name, as_ref=as_ref)
328 # TODO(mdan): Name says tensor_or_indexed_slices. So do explicitly just that?
329 elif isinstance(value, internal.NativeObject):
330 if dtype and not dtypes.as_dtype(dtype).is_compatible_with(value.dtype):
331 raise ValueError(
332 "Incompatible tensor conversion requested to `dtype` "
333 f"{dtypes.as_dtype(dtype).name} for `value` ({value}) with dtype"
334 f" {value.dtype.name}.")
335 return value
336 else:
337 return ops.convert_to_tensor(value, dtype=dtype, name=name, as_ref=as_ref)
340def internal_convert_n_to_tensor_or_indexed_slices(values,
341 dtype=None,
342 name=None,
343 as_ref=False):
344 """Converts `values` to a list of `Tensor` or `IndexedSlices` objects.
346 Any `IndexedSlices` or `SparseTensor` objects in `values` are returned
347 unmodified.
349 Args:
350 values: An iterable of `None`, `IndexedSlices`, `SparseTensor`, or objects
351 that can be consumed by `convert_to_tensor()`.
352 dtype: (Optional.) The required `DType` of the returned `Tensor` or
353 `IndexedSlices`.
354 name: (Optional.) A name prefix to used when a new `Tensor` is created, in
355 which case element `i` will be given the name `name + '_' + i`.
356 as_ref: True if the caller wants the results as ref tensors.
358 Returns:
359 A list of `Tensor`, `IndexedSlices`, `SparseTensor` and/or `None` objects.
361 Raises:
362 TypeError: If no conversion function is registered for an element in
363 `values`.
364 RuntimeError: If a registered conversion function returns an invalid
365 value.
366 """
367 if not isinstance(values, collections_abc.Iterable):
368 raise TypeError("Argument `values` must be iterable.")
369 ret = []
370 for i, value in enumerate(values):
371 if value is None:
372 ret.append(value)
373 else:
374 n = None if name is None else "%s_%d" % (name, i)
375 ret.append(
376 internal_convert_to_tensor_or_indexed_slices(
377 value, dtype=dtype, name=n, as_ref=as_ref))
378 return ret
381def convert_n_to_tensor_or_indexed_slices(values, dtype=None, name=None):
382 """Converts `values` to a list of `Output` or `IndexedSlices` objects.
384 Any `IndexedSlices` or `SparseTensor` objects in `values` are returned
385 unmodified.
387 Args:
388 values: A list of `None`, `IndexedSlices`, `SparseTensor`, or objects that
389 can be consumed by `convert_to_tensor()`.
390 dtype: (Optional.) The required `DType` of the returned `Tensor`
391 `IndexedSlices`.
392 name: (Optional.) A name prefix to used when a new `Tensor` is created, in
393 which case element `i` will be given the name `name + '_' + i`.
395 Returns:
396 A list of `Tensor`, `IndexedSlices`, and/or `SparseTensor` objects.
398 Raises:
399 TypeError: If no conversion function is registered for an element in
400 `values`.
401 RuntimeError: If a registered conversion function returns an invalid
402 value.
403 """
404 return internal_convert_n_to_tensor_or_indexed_slices(
405 values=values, dtype=dtype, name=name, as_ref=False)
408# Warn the user if we convert a sparse representation to dense with at
409# least this number of elements.
410_LARGE_SPARSE_NUM_ELEMENTS = 100000000
413def _indexed_slices_to_tensor(value, dtype=None, name=None, as_ref=False):
414 """Converts an IndexedSlices object `value` to a Tensor.
416 NOTE(mrry): This function is potentially expensive.
418 Args:
419 value: An ops.IndexedSlices object.
420 dtype: The dtype of the Tensor to be returned.
421 name: Optional name to use for the returned Tensor.
422 as_ref: True if a ref is requested.
424 Returns:
425 A dense Tensor representing the values in the given IndexedSlices.
427 Raises:
428 ValueError: If the IndexedSlices does not have the same dtype.
429 """
430 _ = as_ref
431 if dtype and not dtype.is_compatible_with(value.dtype):
432 raise ValueError(
433 f"Incompatible tensor conversion requested to `dtype` {dtype.name} for "
434 f"IndexedSlices ({value}) with dtype {value.dtype.name}")
435 if value.dense_shape is None:
436 raise ValueError(
437 "Tensor conversion requested for IndexedSlices for argument `value` "
438 f"without dense_shape: {value!s}")
439 # TODO(mrry): Consider adding static shape information to
440 # IndexedSlices, to avoid using numpy here.
441 if not context.executing_eagerly():
442 dense_shape_value = tensor_util.constant_value(value.dense_shape)
443 if dense_shape_value is not None:
444 num_elements = np.prod(dense_shape_value)
445 if num_elements >= _LARGE_SPARSE_NUM_ELEMENTS:
446 warnings.warn(
447 "Converting sparse IndexedSlices to a dense Tensor with %d "
448 "elements. This may consume a large amount of memory." %
449 num_elements)
450 return gen_math_ops.unsorted_segment_sum(
451 value.values, value.indices, value.dense_shape[0], name=name)
454tensor_conversion_registry.register_tensor_conversion_function(
455 IndexedSlices, _indexed_slices_to_tensor)