Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/keras/src/engine/sequential.py: 20%
270 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"""Home of the `Sequential` model."""
18import copy
20import tensorflow.compat.v2 as tf
22from keras.src import layers as layer_module
23from keras.src.engine import base_layer
24from keras.src.engine import functional
25from keras.src.engine import input_layer
26from keras.src.engine import training
27from keras.src.engine import training_utils
28from keras.src.saving import serialization_lib
29from keras.src.saving.legacy import serialization as legacy_serialization
30from keras.src.saving.legacy.saved_model import model_serialization
31from keras.src.utils import generic_utils
32from keras.src.utils import layer_utils
33from keras.src.utils import tf_inspect
34from keras.src.utils import tf_utils
35from keras.src.utils import traceback_utils
37# isort: off
38from tensorflow.python.util.tf_export import keras_export
40SINGLE_LAYER_OUTPUT_ERROR_MSG = (
41 "All layers in a Sequential model should have "
42 "a single output tensor. For multi-output "
43 "layers, use the functional API."
44)
47@keras_export("keras.Sequential", "keras.models.Sequential")
48class Sequential(functional.Functional):
49 """`Sequential` groups a linear stack of layers into a `tf.keras.Model`.
51 `Sequential` provides training and inference features on this model.
53 Examples:
55 ```python
56 # Optionally, the first layer can receive an `input_shape` argument:
57 model = tf.keras.Sequential()
58 model.add(tf.keras.layers.Dense(8, input_shape=(16,)))
59 # Afterwards, we do automatic shape inference:
60 model.add(tf.keras.layers.Dense(4))
62 # This is identical to the following:
63 model = tf.keras.Sequential()
64 model.add(tf.keras.Input(shape=(16,)))
65 model.add(tf.keras.layers.Dense(8))
67 # Note that you can also omit the `input_shape` argument.
68 # In that case the model doesn't have any weights until the first call
69 # to a training/evaluation method (since it isn't yet built):
70 model = tf.keras.Sequential()
71 model.add(tf.keras.layers.Dense(8))
72 model.add(tf.keras.layers.Dense(4))
73 # model.weights not created yet
75 # Whereas if you specify the input shape, the model gets built
76 # continuously as you are adding layers:
77 model = tf.keras.Sequential()
78 model.add(tf.keras.layers.Dense(8, input_shape=(16,)))
79 model.add(tf.keras.layers.Dense(4))
80 len(model.weights)
81 # Returns "4"
83 # When using the delayed-build pattern (no input shape specified), you can
84 # choose to manually build your model by calling
85 # `build(batch_input_shape)`:
86 model = tf.keras.Sequential()
87 model.add(tf.keras.layers.Dense(8))
88 model.add(tf.keras.layers.Dense(4))
89 model.build((None, 16))
90 len(model.weights)
91 # Returns "4"
93 # Note that when using the delayed-build pattern (no input shape specified),
94 # the model gets built the first time you call `fit`, `eval`, or `predict`,
95 # or the first time you call the model on some input data.
96 model = tf.keras.Sequential()
97 model.add(tf.keras.layers.Dense(8))
98 model.add(tf.keras.layers.Dense(1))
99 model.compile(optimizer='sgd', loss='mse')
100 # This builds the model for the first time:
101 model.fit(x, y, batch_size=32, epochs=10)
102 ```
103 """
105 @tf.__internal__.tracking.no_automatic_dependency_tracking
106 @traceback_utils.filter_traceback
107 def __init__(self, layers=None, name=None):
108 """Creates a `Sequential` model instance.
110 Args:
111 layers: Optional list of layers to add to the model.
112 name: Optional name for the model.
113 """
114 # Skip the init in FunctionalModel since model doesn't have input/output
115 # yet
116 super(functional.Functional, self).__init__(name=name, autocast=False)
117 base_layer.keras_api_gauge.get_cell("Sequential").set(True)
118 self.supports_masking = True
119 self._compute_output_and_mask_jointly = True
120 self._auto_track_sub_layers = False
121 self._inferred_input_shape = None
122 self._has_explicit_input_shape = False
123 self._input_dtype = None
124 self._layer_call_argspecs = {}
125 self._created_nodes = set()
126 # Flag that indicate whether the sequential network topology has been
127 # created. It is false when there isn't any layer, or the layers don't
128 # have an input shape.
129 self._graph_initialized = False
131 # Unfortunately some Sequential models using custom layers or
132 # FeatureColumn layers have multiple inputs. This is fundamentally
133 # incompatible with most of the Sequential API, and we have to disable a
134 # number of features for such models.
135 self._use_legacy_deferred_behavior = False
137 # Add to the model any layers passed to the constructor.
138 if layers:
139 if not isinstance(layers, (list, tuple)):
140 layers = [layers]
141 for layer in layers:
142 self.add(layer)
144 @property
145 def layers(self):
146 # Historically, `sequential.layers` only returns layers that were added
147 # via `add`, and omits the auto-generated `InputLayer` that comes at the
148 # bottom of the stack.
149 # `Trackable` manages the `_layers` attributes and does filtering
150 # over it.
151 layers = super().layers
152 if layers and isinstance(layers[0], input_layer.InputLayer):
153 return layers[1:]
154 return layers[:]
156 @tf.__internal__.tracking.no_automatic_dependency_tracking
157 @traceback_utils.filter_traceback
158 def add(self, layer):
159 """Adds a layer instance on top of the layer stack.
161 Args:
162 layer: layer instance.
164 Raises:
165 TypeError: If `layer` is not a layer instance.
166 ValueError: In case the `layer` argument does not
167 know its input shape.
168 ValueError: In case the `layer` argument has
169 multiple output tensors, or is already connected
170 somewhere else (forbidden in `Sequential` models).
171 """
172 # If we are passed a Keras tensor created by keras.Input(), we can
173 # extract the input layer from its keras history and use that without
174 # any loss of
175 # generality.
176 if hasattr(layer, "_keras_history"):
177 origin_layer = layer._keras_history[0]
178 if isinstance(origin_layer, input_layer.InputLayer):
179 layer = origin_layer
181 if isinstance(layer, tf.Module):
182 if not isinstance(layer, base_layer.Layer):
183 layer = functional.ModuleWrapper(layer)
184 else:
185 raise TypeError(
186 "The added layer must be an instance of class Layer. "
187 f"Received: layer={layer} of type {type(layer)}."
188 )
190 tf_utils.assert_no_legacy_layers([layer])
191 if not self._is_layer_name_unique(layer):
192 raise ValueError(
193 "All layers added to a Sequential model "
194 f'should have unique names. Name "{layer.name}" is already '
195 "the name of a layer in this model. Update the `name` argument "
196 "to pass a unique name."
197 )
199 self.built = False
200 set_inputs = False
201 self._maybe_create_attribute("_self_tracked_trackables", [])
202 if not self._self_tracked_trackables:
203 if isinstance(layer, input_layer.InputLayer):
204 # Case where the user passes an Input or InputLayer layer via
205 # `add`.
206 set_inputs = True
207 else:
208 batch_shape, dtype = training_utils.get_input_shape_and_dtype(
209 layer
210 )
211 if batch_shape:
212 # Instantiate an input layer.
213 x = input_layer.Input(
214 batch_shape=batch_shape,
215 dtype=dtype,
216 name=layer.name + "_input",
217 )
218 # This will build the current layer
219 # and create the node connecting the current layer
220 # to the input layer we just created.
221 layer(x)
222 set_inputs = True
224 if set_inputs:
225 outputs = tf.nest.flatten(layer._inbound_nodes[-1].outputs)
226 if len(outputs) != 1:
227 raise ValueError(SINGLE_LAYER_OUTPUT_ERROR_MSG)
228 self.outputs = outputs
229 self.inputs = layer_utils.get_source_inputs(self.outputs[0])
230 self.built = True
231 self._has_explicit_input_shape = True
233 elif self.outputs:
234 # If the model is being built continuously on top of an input layer:
235 # refresh its output.
236 output_tensor = layer(self.outputs[0])
237 if len(tf.nest.flatten(output_tensor)) != 1:
238 raise ValueError(SINGLE_LAYER_OUTPUT_ERROR_MSG)
239 self.outputs = [output_tensor]
240 self.built = True
242 if set_inputs or self._graph_initialized:
243 self._init_graph_network(self.inputs, self.outputs)
244 self._graph_initialized = True
245 else:
246 self._self_tracked_trackables.append(layer)
247 self._handle_deferred_layer_dependencies([layer])
249 self._layer_call_argspecs[layer] = tf_inspect.getfullargspec(layer.call)
251 @tf.__internal__.tracking.no_automatic_dependency_tracking
252 @traceback_utils.filter_traceback
253 def pop(self):
254 """Removes the last layer in the model.
256 Raises:
257 TypeError: if there are no layers in the model.
258 """
259 if not self.layers:
260 raise TypeError("There are no layers in the model.")
262 layer = self._self_tracked_trackables.pop()
263 self._layer_call_argspecs.pop(layer)
264 if not self.layers:
265 self.outputs = None
266 self.inputs = None
267 self.built = False
268 self._inferred_input_shape = None
269 self._has_explicit_input_shape = False
270 self._graph_initialized = False
271 elif self._graph_initialized:
272 self.layers[-1]._outbound_nodes = []
273 self.outputs = [self.layers[-1].output]
274 self._init_graph_network(self.inputs, self.outputs)
275 self.built = True
277 @tf.__internal__.tracking.no_automatic_dependency_tracking
278 def _build_graph_network_for_inferred_shape(
279 self, input_shape, input_dtype=None
280 ):
281 if input_shape is None or not self.layers:
282 return
283 if (
284 not tf.__internal__.tf2.enabled()
285 or not tf.compat.v1.executing_eagerly_outside_functions()
286 ):
287 # This behavior is disabled in V1 or when eager execution is
288 # disabled.
289 return
290 if (
291 not self._has_explicit_input_shape
292 and not self._use_legacy_deferred_behavior
293 ):
294 # Determine whether the input shape is novel, i.e. whether the model
295 # should be rebuilt.
296 input_shape = tuple(input_shape)
297 if self._inferred_input_shape is None:
298 new_shape = input_shape
299 else:
300 new_shape = relax_input_shape(
301 self._inferred_input_shape, input_shape
302 )
303 if (
304 new_shape is not None
305 and new_shape != self._inferred_input_shape
306 ):
307 # A novel shape has been received: we need to rebuild the model.
308 # In case we are inside a graph function, we step out of it.
309 with tf.init_scope():
310 inputs = input_layer.Input(
311 batch_shape=new_shape,
312 dtype=input_dtype,
313 name=self.layers[0].name + "_input",
314 )
315 layer_input = inputs
316 created_nodes = set()
317 for layer in self.layers:
318 # Clear nodes previously created via this method. This
319 # prevents node accumulation and ensures that e.g.
320 # `layer.output` is always connected to `model.inputs`
321 # (this is important e.g. for the feature extraction use
322 # case). We don't just do `layer._inbound_nodes = []`
323 # in order not to break shared layers added to
324 # Sequential models (which is technically illegal as per
325 # the `add()` docstring, but wasn't previously
326 # disabled).
327 clear_previously_created_nodes(
328 layer, self._created_nodes
329 )
330 try:
331 # Create Functional API connection by calling the
332 # current layer
333 layer_output = layer(layer_input)
334 except: # noqa: E722
335 # Functional API calls may fail for a number of
336 # reasons: 1) The layer may be buggy. In this case
337 # it will be easier for the user to debug if we fail
338 # on the first call on concrete data, instead of our
339 # own call on a symbolic input. 2) The layer is
340 # dynamic (graph-incompatible) and hasn't overridden
341 # `compute_output_shape`. In this case, it is
342 # impossible to build a graph network. 3) The layer
343 # is otherwise incompatible with the Functional API
344 # (e.g. this is the case for some probabilistic
345 # layers that rely on hacks and that do not return
346 # tensors). In all these cases, we should avoid
347 # creating a graph network (or we simply can't).
348 self._use_legacy_deferred_behavior = True
349 return
350 if len(tf.nest.flatten(layer_output)) != 1:
351 raise ValueError(SINGLE_LAYER_OUTPUT_ERROR_MSG)
352 # Keep track of nodes just created above
353 track_nodes_created_by_last_call(layer, created_nodes)
354 layer_input = layer_output
355 outputs = layer_output
356 self._created_nodes = created_nodes
357 try:
358 # Initialize a graph Network. This call will never fail
359 # for a stack of valid Keras layers. However some users
360 # have layers that are fundamentally incompatible with
361 # the Functional API, which do not return tensors. In
362 # this case, we fall back to the legacy deferred
363 # behavior.
364 # TODO(fchollet): consider raising here, as we should
365 # not be supporting such layers.
366 self._init_graph_network(inputs, outputs)
367 self._graph_initialized = True
368 except: # noqa: E722
369 self._use_legacy_deferred_behavior = True
370 self._inferred_input_shape = new_shape
372 @generic_utils.default
373 def build(self, input_shape=None):
374 if self._graph_initialized:
375 self._init_graph_network(self.inputs, self.outputs)
376 else:
377 if input_shape is None:
378 raise ValueError("You must provide an `input_shape` argument.")
379 self._build_graph_network_for_inferred_shape(input_shape)
380 if not self.built:
381 input_shape = tuple(input_shape)
382 self._build_input_shape = input_shape
383 super().build(input_shape)
384 self.built = True
386 def call(self, inputs, training=None, mask=None):
387 # If applicable, update the static input shape of the model.
388 if not self._has_explicit_input_shape:
389 if not tf.is_tensor(inputs) and not isinstance(inputs, tf.Tensor):
390 # This is a Sequential with multiple inputs. This is technically
391 # an invalid use case of Sequential, but we tolerate it for
392 # backwards compatibility.
393 self._use_legacy_deferred_behavior = True
394 self._build_input_shape = tf.nest.map_structure(
395 _get_shape_tuple, inputs
396 )
397 else:
398 self._build_graph_network_for_inferred_shape(
399 inputs.shape, inputs.dtype
400 )
402 if self._graph_initialized:
403 if not self.built:
404 self._init_graph_network(self.inputs, self.outputs)
405 return super().call(inputs, training=training, mask=mask)
407 outputs = inputs # handle the corner case where self.layers is empty
408 for layer in self.layers:
409 # During each iteration, `inputs` are the inputs to `layer`, and
410 # `outputs` are the outputs of `layer` applied to `inputs`. At the
411 # end of each iteration `inputs` is set to `outputs` to prepare for
412 # the next layer.
413 kwargs = {}
414 argspec = self._layer_call_argspecs[layer].args
415 if "mask" in argspec:
416 kwargs["mask"] = mask
417 if "training" in argspec:
418 kwargs["training"] = training
420 outputs = layer(inputs, **kwargs)
422 inputs = outputs
424 def _get_mask_from_keras_tensor(kt):
425 return getattr(kt, "_keras_mask", None)
427 mask = tf.nest.map_structure(_get_mask_from_keras_tensor, outputs)
428 return outputs
430 def compute_output_shape(self, input_shape):
431 shape = input_shape
432 for layer in self.layers:
433 shape = layer.compute_output_shape(shape)
434 return shape
436 def compute_mask(self, inputs, mask):
437 # TODO(omalleyt): b/123540974 This function is not really safe to call
438 # by itself because it will duplicate any updates and losses in graph
439 # mode by `call`ing the Layers again.
440 outputs = self.call(inputs, mask=mask)
441 return getattr(outputs, "_keras_mask", None)
443 def get_config(self):
444 layer_configs = []
445 serialize_obj_fn = serialization_lib.serialize_keras_object
446 if getattr(self, "use_legacy_config", None):
447 serialize_obj_fn = legacy_serialization.serialize_keras_object
448 for layer in super().layers:
449 # `super().layers` include the InputLayer if available (it is
450 # filtered out of `self.layers`). Note that
451 # `self._self_tracked_trackables` is managed by the tracking
452 # infrastructure and should not be used.
453 layer_configs.append(serialize_obj_fn(layer))
454 config = training.Model.get_config(self)
455 config["name"] = self.name
456 config["layers"] = copy.deepcopy(layer_configs)
457 if not self._is_graph_network and self._build_input_shape is not None:
458 config["build_input_shape"] = self._build_input_shape
459 return config
461 @classmethod
462 def from_config(cls, config, custom_objects=None):
463 if "name" in config:
464 name = config["name"]
465 build_input_shape = config.get("build_input_shape")
466 layer_configs = config["layers"]
467 else:
468 name = None
469 layer_configs = config
470 model = cls(name=name)
471 for layer_config in layer_configs:
472 use_legacy_format = "module" not in layer_config
473 layer = layer_module.deserialize(
474 layer_config,
475 custom_objects=custom_objects,
476 use_legacy_format=use_legacy_format,
477 )
478 model.add(layer)
480 if (
481 not model.inputs
482 and build_input_shape
483 and isinstance(build_input_shape, (tuple, list))
484 ):
485 model.build(build_input_shape)
487 return model
489 @property
490 def input_spec(self):
491 if hasattr(self, "_manual_input_spec"):
492 return self._manual_input_spec
493 if self._has_explicit_input_shape:
494 return super().input_spec
495 return None
497 @input_spec.setter
498 def input_spec(self, value):
499 self._manual_input_spec = value
501 @property
502 def _trackable_saved_model_saver(self):
503 return model_serialization.SequentialSavedModelSaver(self)
505 def _is_layer_name_unique(self, layer):
506 for ref_layer in self.layers:
507 if layer.name == ref_layer.name and ref_layer is not layer:
508 return False
509 return True
511 def _assert_weights_created(self):
512 if self._graph_initialized:
513 return
514 # When the graph has not been initialized, use the Model's
515 # implementation to to check if the weights has been created.
516 super(functional.Functional, self)._assert_weights_created()
519def _get_shape_tuple(t):
520 if hasattr(t, "shape"):
521 shape = t.shape
522 if isinstance(shape, tuple):
523 return shape
524 if shape.rank is not None:
525 return tuple(shape.as_list())
526 return None
527 return None
530def relax_input_shape(shape_1, shape_2):
531 if shape_1 is None or shape_2 is None:
532 return None
533 if len(shape_1) != len(shape_2):
534 return None
535 return tuple(None if d1 != d2 else d1 for d1, d2 in zip(shape_1, shape_2))
538def clear_previously_created_nodes(layer, created_nodes):
539 """Remove nodes from `created_nodes` from the layer's inbound_nodes."""
540 for node in layer._inbound_nodes:
541 prev_layers = node.inbound_layers
542 for prev_layer in tf.nest.flatten(prev_layers):
543 prev_layer._outbound_nodes = [
544 n for n in prev_layer._outbound_nodes if n not in created_nodes
545 ]
546 layer._inbound_nodes = [
547 n for n in layer._inbound_nodes if n not in created_nodes
548 ]
551def track_nodes_created_by_last_call(layer, created_nodes):
552 """Adds to `created_nodes` the nodes created by the last call to `layer`."""
553 if not layer._inbound_nodes:
554 return
555 created_nodes.add(layer._inbound_nodes[-1])
556 prev_layers = layer._inbound_nodes[-1].inbound_layers
557 for prev_layer in tf.nest.flatten(prev_layers):
558 if prev_layer._outbound_nodes:
559 created_nodes.add(prev_layer._outbound_nodes[-1])