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
« 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# ==============================================================================
16"""Input layer code (`Input` and `InputLayer`)."""
18import tensorflow.compat.v2 as tf
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
30# isort: off
31from tensorflow.python.util.tf_export import keras_export
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 )
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).
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`).
51 It is generally recommend to use the Keras Functional model via `Input`,
52 (which creates an `InputLayer`) without directly using `InputLayer`.
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`.
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:
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)))
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 ```
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 """
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
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
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 )
157 if sparse and ragged:
158 raise ValueError(
159 "Cannot set both sparse and ragged to True in a Keras input."
160 )
162 if not name:
163 prefix = "input"
164 name = prefix + "_" + str(backend.get_uid(prefix))
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
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,)
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 )
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)
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 )
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
290 @property
291 def _trackable_saved_model_saver(self):
292 return layer_serialization.InputLayerSavedModelSaver(self)
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.
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.
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)`
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`.
347 Returns:
348 A `tensor`.
350 Example:
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 ```
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:
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 ```
370 (This behavior does not work for higher-order TensorFlow APIs such as
371 control flow and being directly watched by a `tf.GradientTape`).
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.
377 The Keras Input can also create a placeholder from an arbitrary
378 `tf.TypeSpec`, e.g:
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.
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 )
404 has_spec_name = (
405 name is None and type_spec is not None and hasattr(type_spec, "name")
406 )
408 if has_spec_name:
409 name = type_spec.name
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 }
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 )
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)
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