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

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.""" 

16 

17# pylint: disable=g-bad-name 

18import collections 

19import warnings 

20 

21import numpy as np 

22 

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 

40 

41 

42class IndexedSlicesCompositeTensorGradient( 

43 composite_tensor_gradient.CompositeTensorGradient): 

44 """CompositeTensorGradient for IndexedSlices.""" 

45 

46 def get_gradient_components(self, value): 

47 return value 

48 

49 def replace_gradient_components(self, value, component_grads): 

50 return component_grads 

51 

52 

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. 

60 

61 This class is a simple wrapper for a pair of `Tensor` objects: 

62 

63 * `values`: A `Tensor` of any dtype with shape `[D0, D1, ..., Dn]`. 

64 * `indices`: A 1-D integer `Tensor` with shape `[D0]`. 

65 

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. 

70 

71 The dense tensor `dense` represented by an `IndexedSlices` `slices` has 

72 

73 ```python 

74 dense[slices.indices[i], :, :, :, ...] = slices.values[i, :, :, :, ...] 

75 ``` 

76 

77 The `IndexedSlices` class is used principally in the definition of 

78 gradients for operations that have sparse gradients 

79 (e.g. `tf.gather`). 

80 

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) 

92 

93 Contrast this representation with 

94 `tf.sparse.SparseTensor`, 

95 which uses multi-dimensional indices and scalar values. 

96 """ 

97 

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 

103 

104 @property 

105 def values(self): 

106 """A `Tensor` containing the values of the slices.""" 

107 return self._values 

108 

109 @property 

110 def indices(self): 

111 """A 1-D `Tensor` containing the indices of the slices.""" 

112 return self._indices 

113 

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 

118 

119 @property 

120 def shape(self): 

121 """Gets the `tf.TensorShape` representing the shape of the dense tensor. 

122 

123 Returns: 

124 A `tf.TensorShape` object. 

125 """ 

126 if self._dense_shape is None: 

127 return tensor_shape.TensorShape(None) 

128 

129 return tensor_util.constant_value_as_shape(self._dense_shape) 

130 

131 @property 

132 def name(self): 

133 """The name of this `IndexedSlices`.""" 

134 return self.values.name 

135 

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 

140 

141 @property 

142 def op(self): 

143 """The `Operation` that produces `values` as an output.""" 

144 return self.values.op 

145 

146 @property 

147 def dtype(self): 

148 """The `DType` of elements in this tensor.""" 

149 return self.values.dtype 

150 

151 @property 

152 def graph(self): 

153 """The `Graph` that contains the values, indices, and shape tensors.""" 

154 return self._values.graph 

155 

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 "") 

161 

162 def __neg__(self): 

163 return IndexedSlices(-self.values, self.indices, self.dense_shape) 

164 

165 __composite_gradient__ = IndexedSlicesCompositeTensorGradient() 

166 

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) 

180 

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) 

194 

195 def consumers(self): 

196 return self._consumers() 

197 

198 

199IndexedSlicesValue = collections.namedtuple( 

200 "IndexedSlicesValue", ["values", "indices", "dense_shape"]) 

201 

202 

203@tf_export("IndexedSlicesSpec") 

204class IndexedSlicesSpec(type_spec.TypeSpec): 

205 """Type specification for a `tf.IndexedSlices`.""" 

206 

207 __slots__ = ["_shape", "_values_dtype", "_indices_dtype", 

208 "_dense_shape_dtype", "_indices_shape"] 

209 

210 value_type = property(lambda self: IndexedSlices) 

211 

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`. 

216 

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) 

237 

238 def _serialize(self): 

239 return (self._shape, self._values_dtype, self._indices_dtype, 

240 self._dense_shape_dtype, self._indices_shape) 

241 

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) 

252 

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) 

258 

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) 

268 

269 

270nested_structure_coder.register_codec( 

271 nested_structure_coder.BuiltInTypeSpecCodec( 

272 IndexedSlicesSpec, struct_pb2.TypeSpecProto.INDEXED_SLICES_SPEC 

273 ) 

274) 

275 

276 

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`. 

280 

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()`. 

284 

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. 

291 

292 Returns: 

293 A `Tensor`, `IndexedSlices`, or `SparseTensor` based on `value`. 

294 

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) 

300 

301 

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`. 

307 

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()`. 

311 

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. 

319 

320 Returns: 

321 A `Tensor`, `IndexedSlices`, or `SparseTensor` based on `value`. 

322 

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) 

338 

339 

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. 

345 

346 Any `IndexedSlices` or `SparseTensor` objects in `values` are returned 

347 unmodified. 

348 

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. 

357 

358 Returns: 

359 A list of `Tensor`, `IndexedSlices`, `SparseTensor` and/or `None` objects. 

360 

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 

379 

380 

381def convert_n_to_tensor_or_indexed_slices(values, dtype=None, name=None): 

382 """Converts `values` to a list of `Output` or `IndexedSlices` objects. 

383 

384 Any `IndexedSlices` or `SparseTensor` objects in `values` are returned 

385 unmodified. 

386 

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`. 

394 

395 Returns: 

396 A list of `Tensor`, `IndexedSlices`, and/or `SparseTensor` objects. 

397 

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) 

406 

407 

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 

411 

412 

413def _indexed_slices_to_tensor(value, dtype=None, name=None, as_ref=False): 

414 """Converts an IndexedSlices object `value` to a Tensor. 

415 

416 NOTE(mrry): This function is potentially expensive. 

417 

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. 

423 

424 Returns: 

425 A dense Tensor representing the values in the given IndexedSlices. 

426 

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) 

452 

453 

454tensor_conversion_registry.register_tensor_conversion_function( 

455 IndexedSlices, _indexed_slices_to_tensor)