Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/keras/src/engine/input_layer.py: 17%

138 statements  

« prev     ^ index     » next       coverage.py v7.4.0, created at 2024-01-03 07:57 +0000

1# Copyright 2015 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 

16"""Input layer code (`Input` and `InputLayer`).""" 

17 

18import tensorflow.compat.v2 as tf 

19 

20from keras.src import backend 

21from keras.src.distribute import distributed_training_utils 

22from keras.src.engine import base_layer 

23from keras.src.engine import keras_tensor 

24from keras.src.engine import node as node_module 

25from keras.src.saving import serialization_lib 

26from keras.src.saving.legacy.saved_model import layer_serialization 

27from keras.src.utils import tf_utils 

28from keras.src.utils import traceback_utils 

29 

30# isort: off 

31from tensorflow.python.util.tf_export import keras_export 

32 

33 

34def _assert_other_arg_none(arg_name, arg): 

35 if arg is not None: 

36 raise ValueError( 

37 "When `type_spec` is not None, all other args " 

38 "except `name` must be None, " 

39 "but %s is not None." % arg_name 

40 ) 

41 

42 

43@keras_export("keras.layers.InputLayer") 

44class InputLayer(base_layer.Layer): 

45 """Layer to be used as an entry point into a Network (a graph of layers). 

46 

47 It can either wrap an existing tensor (pass an `input_tensor` argument) 

48 or create a placeholder tensor (pass arguments `input_shape`, and 

49 optionally, `dtype`). 

50 

51 It is generally recommend to use the Keras Functional model via `Input`, 

52 (which creates an `InputLayer`) without directly using `InputLayer`. 

53 

54 When using `InputLayer` with the Keras Sequential model, it can be skipped 

55 by moving the `input_shape` parameter to the first layer after the 

56 `InputLayer`. 

57 

58 This class can create placeholders for `tf.Tensors`, `tf.SparseTensors`, and 

59 `tf.RaggedTensors` by choosing `sparse=True` or `ragged=True`. Note that 

60 `sparse` and `ragged` can't be configured to `True` at the same time. 

61 Usage: 

62 

63 ```python 

64 # With explicit InputLayer. 

65 model = tf.keras.Sequential([ 

66 tf.keras.layers.InputLayer(input_shape=(4,)), 

67 tf.keras.layers.Dense(8)]) 

68 model.compile(tf.keras.optimizers.RMSprop(0.001), loss='mse') 

69 model.fit(np.zeros((10, 4)), 

70 np.ones((10, 8))) 

71 

72 # Without InputLayer and let the first layer to have the input_shape. 

73 # Keras will add a input for the model behind the scene. 

74 model = tf.keras.Sequential([ 

75 tf.keras.layers.Dense(8, input_shape=(4,))]) 

76 model.compile(tf.keras.optimizers.RMSprop(0.001), loss='mse') 

77 model.fit(np.zeros((10, 4)), 

78 np.ones((10, 8))) 

79 ``` 

80 

81 Args: 

82 input_shape: Shape tuple (not including the batch axis), or 

83 `TensorShape` instance (not including the batch axis). 

84 batch_size: Optional input batch size (integer or `None`). 

85 dtype: Optional datatype of the input. When not provided, the Keras 

86 default `float` type will be used. 

87 input_tensor: Optional tensor to use as layer input. If set, the layer 

88 will use the `tf.TypeSpec` of this tensor rather 

89 than creating a new placeholder tensor. 

90 sparse: Boolean, whether the placeholder created is meant to be sparse. 

91 Defaults to `False`. 

92 ragged: Boolean, whether the placeholder created is meant to be ragged. 

93 In this case, values of `None` in the `shape` argument represent 

94 ragged dimensions. For more information about `tf.RaggedTensor`, see 

95 [this guide](https://www.tensorflow.org/guide/ragged_tensor). 

96 Defaults to `False`. 

97 type_spec: A `tf.TypeSpec` object to create Input from. This 

98 `tf.TypeSpec` represents the entire batch. When provided, all other 

99 args except name must be `None`. 

100 name: Optional name of the layer (string). 

101 """ 

102 

103 @traceback_utils.filter_traceback 

104 def __init__( 

105 self, 

106 input_shape=None, 

107 batch_size=None, 

108 dtype=None, 

109 input_tensor=None, 

110 sparse=None, 

111 name=None, 

112 ragged=None, 

113 type_spec=None, 

114 **kwargs, 

115 ): 

116 self._init_input_shape = input_shape 

117 self._init_batch_size = batch_size 

118 self._init_dtype = dtype 

119 self._init_sparse = sparse 

120 self._init_ragged = ragged 

121 self._init_type_spec = type_spec 

122 

123 strategy = tf.distribute.get_strategy() 

124 if ( 

125 strategy 

126 and batch_size is not None 

127 and distributed_training_utils.global_batch_size_supported(strategy) 

128 ): 

129 if batch_size % strategy.num_replicas_in_sync != 0: 

130 raise ValueError( 

131 "The `batch_size` argument ({}) must be divisible by " 

132 "the number of replicas ({})".format( 

133 batch_size, strategy.num_replicas_in_sync 

134 ) 

135 ) 

136 batch_size = batch_size // strategy.num_replicas_in_sync 

137 

138 if "batch_input_shape" in kwargs: 

139 batch_input_shape = kwargs.pop("batch_input_shape") 

140 if input_shape and batch_input_shape: 

141 raise ValueError( 

142 "Only provide the input_shape OR " 

143 "batch_input_shape argument to " 

144 "InputLayer, not both at the same time." 

145 ) 

146 # Set the input shape and batch size from the batch_input_shape. 

147 # Note that batch_input_shape can be None (unknown rank) or [] 

148 # (scalar), in which case the batch size must be None. 

149 if batch_input_shape: 

150 batch_size = batch_input_shape[0] 

151 input_shape = batch_input_shape[1:] 

152 if kwargs: 

153 raise ValueError( 

154 f"Unrecognized keyword arguments: {list(kwargs.keys())}" 

155 ) 

156 

157 if sparse and ragged: 

158 raise ValueError( 

159 "Cannot set both sparse and ragged to True in a Keras input." 

160 ) 

161 

162 if not name: 

163 prefix = "input" 

164 name = prefix + "_" + str(backend.get_uid(prefix)) 

165 

166 if not dtype: 

167 if input_tensor is None: 

168 dtype = backend.floatx() 

169 else: 

170 dtype = backend.dtype(input_tensor) 

171 elif input_tensor is not None and input_tensor.dtype != dtype: 

172 raise ValueError( 

173 "`input_tensor.dtype` differs from `dtype`. Received: " 

174 f"input_tensor.dtype={input_tensor.dtype} " 

175 f"but expected dtype={dtype}" 

176 ) 

177 super().__init__(dtype=dtype, name=name) 

178 self.built = True 

179 self.sparse = True if sparse else False 

180 self.ragged = True if ragged else False 

181 self.batch_size = batch_size 

182 self.supports_masking = True 

183 

184 if isinstance(input_shape, tf.TensorShape): 

185 input_shape = tuple(input_shape.as_list()) 

186 elif isinstance(input_shape, int): 

187 input_shape = (input_shape,) 

188 

189 if type_spec is not None: 

190 args_that_must_be_none = [ 

191 ("(input_)shape", self._init_input_shape), 

192 ("batch_size", self._init_batch_size), 

193 ("dtype", self._init_dtype), 

194 ("input_tensor", input_tensor), 

195 ("sparse", self._init_sparse), 

196 ("ragged", self._init_ragged), 

197 ] 

198 for arg_name, arg in args_that_must_be_none: 

199 _assert_other_arg_none(arg_name, arg) 

200 if not tf.compat.v1.executing_eagerly_outside_functions(): 

201 raise ValueError( 

202 "Creating Keras inputs from a type_spec is only " 

203 "supported when eager execution is enabled." 

204 ) 

205 # Needed for type_spec deserialization since TypeSpec objects 

206 # are not Keras-native (not automatically deserialized). 

207 if isinstance(type_spec, dict): 

208 type_spec = serialization_lib.deserialize_keras_object( 

209 type_spec 

210 ) 

211 input_tensor = keras_tensor.keras_tensor_from_type_spec(type_spec) 

212 if isinstance(input_tensor, keras_tensor.SparseKerasTensor): 

213 self.sparse = True 

214 if isinstance(input_tensor, keras_tensor.RaggedKerasTensor): 

215 self.ragged = True 

216 self.is_placeholder = True 

217 try: 

218 self._batch_input_shape = tuple(input_tensor.shape.as_list()) 

219 except ValueError: 

220 # If the shape cannot be represented as a tuple (e.g. unknown 

221 # rank) 

222 self._batch_input_shape = None 

223 elif input_tensor is None: 

224 if input_shape is not None: 

225 batch_input_shape = (batch_size,) + tuple(input_shape) 

226 else: 

227 batch_input_shape = None 

228 graph = backend.get_graph() 

229 with graph.as_default(): 

230 input_tensor = backend.placeholder( 

231 shape=batch_input_shape, 

232 dtype=dtype, 

233 name=self.name, 

234 sparse=sparse, 

235 ragged=ragged, 

236 ) 

237 

238 self.is_placeholder = True 

239 self._batch_input_shape = batch_input_shape 

240 else: 

241 if tf.compat.v1.executing_eagerly_outside_functions(): 

242 if not isinstance(input_tensor, keras_tensor.KerasTensor): 

243 input_tensor = keras_tensor.keras_tensor_from_tensor( 

244 input_tensor 

245 ) 

246 else: 

247 if not tf_utils.is_symbolic_tensor(input_tensor): 

248 raise ValueError( 

249 "You should not pass an EagerTensor to `Input`. " 

250 "For example, instead of creating an " 

251 "`InputLayer`, you should instantiate your model " 

252 "and directly call it on your input." 

253 ) 

254 self.is_placeholder = False 

255 try: 

256 self._batch_input_shape = tuple(input_tensor.shape.as_list()) 

257 except ValueError: 

258 # If the shape cannot be represented as a tuple (e.g. unknown 

259 # rank) 

260 self._batch_input_shape = None 

261 # Create an input node. 

262 input_tensor._keras_mask = None 

263 node_module.Node(layer=self, outputs=input_tensor) 

264 

265 # Store type spec 

266 if isinstance(input_tensor, keras_tensor.KerasTensor) or ( 

267 tf_utils.is_extension_type(input_tensor) 

268 ): 

269 self._type_spec = input_tensor._type_spec 

270 else: 

271 self._type_spec = tf.TensorSpec( 

272 shape=input_tensor.shape, 

273 dtype=input_tensor.dtype, 

274 name=self.name, 

275 ) 

276 

277 def get_config(self): 

278 if self._init_type_spec is not None: 

279 config = {"name": self.name, "type_spec": self._init_type_spec} 

280 else: 

281 config = { 

282 "batch_input_shape": self._batch_input_shape, 

283 "dtype": self.dtype, 

284 "sparse": self.sparse, 

285 "ragged": self.ragged, 

286 "name": self.name, 

287 } 

288 return config 

289 

290 @property 

291 def _trackable_saved_model_saver(self): 

292 return layer_serialization.InputLayerSavedModelSaver(self) 

293 

294 

295@keras_export("keras.Input", "keras.layers.Input") 

296@traceback_utils.filter_traceback 

297def Input( 

298 shape=None, 

299 batch_size=None, 

300 name=None, 

301 dtype=None, 

302 sparse=None, 

303 tensor=None, 

304 ragged=None, 

305 type_spec=None, 

306 **kwargs, 

307): 

308 """`Input()` is used to instantiate a Keras tensor. 

309 

310 A Keras tensor is a symbolic tensor-like object, which we augment with 

311 certain attributes that allow us to build a Keras model just by knowing the 

312 inputs and outputs of the model. 

313 

314 For instance, if `a`, `b` and `c` are Keras tensors, 

315 it becomes possible to do: 

316 `model = Model(input=[a, b], output=c)` 

317 

318 Args: 

319 shape: A shape tuple (integers), not including the batch size. 

320 For instance, `shape=(32,)` indicates that the expected input 

321 will be batches of 32-dimensional vectors. Elements of this tuple 

322 can be None; 'None' elements represent dimensions where the shape is 

323 not known. 

324 batch_size: optional static batch size (integer). 

325 name: An optional name string for the layer. 

326 Should be unique in a model (do not reuse the same name twice). 

327 It will be autogenerated if it isn't provided. 

328 dtype: The data type expected by the input, as a string 

329 (`float32`, `float64`, `int32`...) 

330 sparse: A boolean specifying whether the placeholder to be created is 

331 sparse. Only one of 'ragged' and 'sparse' can be True. Note that, 

332 if `sparse` is False, sparse tensors can still be passed into the 

333 input - they will be densified with a default value of 0. 

334 tensor: Optional existing tensor to wrap into the `Input` layer. 

335 If set, the layer will use the `tf.TypeSpec` of this tensor rather 

336 than creating a new placeholder tensor. 

337 ragged: A boolean specifying whether the placeholder to be created is 

338 ragged. Only one of 'ragged' and 'sparse' can be True. In this case, 

339 values of 'None' in the 'shape' argument represent ragged 

340 dimensions. For more information about RaggedTensors, see 

341 [this guide](https://www.tensorflow.org/guide/ragged_tensor). 

342 type_spec: A `tf.TypeSpec` object to create the input placeholder from. 

343 When provided, all other args except name must be None. 

344 **kwargs: deprecated arguments support. Supports `batch_shape` and 

345 `batch_input_shape`. 

346 

347 Returns: 

348 A `tensor`. 

349 

350 Example: 

351 

352 ```python 

353 # this is a logistic regression in Keras 

354 x = Input(shape=(32,)) 

355 y = Dense(16, activation='softmax')(x) 

356 model = Model(x, y) 

357 ``` 

358 

359 Note that even if eager execution is enabled, 

360 `Input` produces a symbolic tensor-like object (i.e. a placeholder). 

361 This symbolic tensor-like object can be used with lower-level 

362 TensorFlow ops that take tensors as inputs, as such: 

363 

364 ```python 

365 x = Input(shape=(32,)) 

366 y = tf.square(x) # This op will be treated like a layer 

367 model = Model(x, y) 

368 ``` 

369 

370 (This behavior does not work for higher-order TensorFlow APIs such as 

371 control flow and being directly watched by a `tf.GradientTape`). 

372 

373 However, the resulting model will not track any variables that were 

374 used as inputs to TensorFlow ops. All variable usages must happen within 

375 Keras layers to make sure they will be tracked by the model's weights. 

376 

377 The Keras Input can also create a placeholder from an arbitrary 

378 `tf.TypeSpec`, e.g: 

379 

380 ```python 

381 x = Input(type_spec=tf.RaggedTensorSpec(shape=[None, None], 

382 dtype=tf.float32, ragged_rank=1)) 

383 y = x.values 

384 model = Model(x, y) 

385 ``` 

386 When passing an arbitrary `tf.TypeSpec`, it must represent the signature of 

387 an entire batch instead of just one example. 

388 

389 Raises: 

390 ValueError: If both `sparse` and `ragged` are provided. 

391 ValueError: If both `shape` and (`batch_input_shape` or `batch_shape`) are 

392 provided. 

393 ValueError: If `shape`, `tensor` and `type_spec` are None. 

394 ValueError: If arguments besides `type_spec` are non-None while 

395 `type_spec` is passed. 

396 ValueError: if any unrecognized parameters are provided. 

397 """ 

398 if sparse and ragged: 

399 raise ValueError( 

400 "Cannot set both `sparse` and `ragged` to `True` in a " 

401 "Keras `Input`." 

402 ) 

403 

404 has_spec_name = ( 

405 name is None and type_spec is not None and hasattr(type_spec, "name") 

406 ) 

407 

408 if has_spec_name: 

409 name = type_spec.name 

410 

411 input_layer_config = { 

412 "name": name, 

413 "dtype": dtype, 

414 "sparse": sparse, 

415 "ragged": ragged, 

416 "input_tensor": tensor, 

417 "type_spec": type_spec, 

418 } 

419 

420 batch_input_shape = kwargs.pop( 

421 "batch_input_shape", kwargs.pop("batch_shape", None) 

422 ) 

423 if shape is not None and batch_input_shape is not None: 

424 raise ValueError( 

425 "Only provide the `shape` OR `batch_input_shape` argument " 

426 "to Input, not both at the same time." 

427 ) 

428 if ( 

429 batch_input_shape is None 

430 and shape is None 

431 and tensor is None 

432 and type_spec is None 

433 ): 

434 raise ValueError( 

435 "Please provide to Input a `shape` " 

436 "or a `tensor` or a `type_spec` argument. Note that " 

437 "`shape` does not include the batch " 

438 "dimension." 

439 ) 

440 if kwargs: 

441 raise ValueError( 

442 f"Unrecognized keyword arguments: {list(kwargs.keys())}" 

443 ) 

444 

445 if batch_input_shape: 

446 shape = batch_input_shape[1:] 

447 input_layer_config.update({"batch_input_shape": batch_input_shape}) 

448 else: 

449 input_layer_config.update( 

450 {"batch_size": batch_size, "input_shape": shape} 

451 ) 

452 input_layer = InputLayer(**input_layer_config) 

453 

454 # Return tensor including `_keras_history`. 

455 # Note that in this case train_output and test_output are the same pointer. 

456 outputs = input_layer._inbound_nodes[0].outputs 

457 if isinstance(outputs, list) and len(outputs) == 1: 

458 output = outputs[0] 

459 else: 

460 output = outputs 

461 if has_spec_name and hasattr(output, "_name"): 

462 output._name = input_layer.name 

463 return output 

464