sequential groups a linear stack of layers into a model examples python model keras sequential model addkeras inputshape16 model addkeras layers dense8 note that you can also omit the initial input in that case the model doesn t have any weights until the first call to a trainingevaluation method since it isn t yet built model keras sequential model addkeras layers dense8 model addkeras layers dense4 model weights not created yet whereas if you specify an input the model gets built continuously as you are adding layers model keras sequential model addkeras inputshape16 model addkeras layers dense8 lenmodel weights returns 2 when using the delayedbuild pattern no input shape specified you can choose to manually build your model by calling buildbatchinputshape model keras sequential model addkeras layers dense8 model addkeras layers dense4 model buildnone 16 lenmodel weights returns 4 note that when using the delayedbuild pattern no input shape specified the model gets built the first time you call fit eval or predict or the first time you call the model on some input data model keras sequential model addkeras layers dense8 model addkeras layers dense1 model compileoptimizer sgd loss mse this builds the model for the first time model fitx y batchsize32 epochs10 adds a layer instance on top of the layer stack args layer layer instance legacy case if the first layer has an inputshape arg use it to build an inputlayer if we are passed a keras tensor keras input we extract the input layer from its keras history and use that removes the last layer in the model layer self layers pop self built false self functional none if rebuild self mayberebuild return layer def mayberebuildself self built false self functional none if isinstanceself layers0 inputlayer and lenself layers 1 inputshape self layers0 batchshape self buildinputshape def lockstateself unlike other layers sequential is mutable after build pass def buildself inputshapenone if not isinstanceinputshape tuple list do not attempt to build if the model does not have a single input tensor return if inputshape and not isinstanceinputshape0 int or inputshape0 is none do not attempt to build if the model does not have a single input tensor return if not self layers raise valueerror fsequential model self name cannot be built because it has no layers call model addlayer if isinstanceself layers0 inputlayer if self layers0 batchshape inputshape raise valueerror fsequential model self name has already been configured to use input shape fself layers0 batchshape you cannot build it fwith inputshape inputshape else dtype self layers0 computedtype self layers inputlayerbatchshapeinputshape dtypedtype self layers build functional model inputs self layers0 output x inputs for layer in self layers1 try x layerx except notimplementederror can happen if shape inference is not implemented todo consider reverting inbound nodes on layers processed return except typeerror as e signature inspect signaturelayer call positionalargs param for param in signature parameters values if param default inspect parameter empty if lenpositionalargs 1 raise valueerror layers added to a sequential model can only have a single positional argument fthe input tensor layer layer class name fhas multiple positional arguments positionalargs raise e outputs x self functional functionalinputsinputs outputsoutputs self built true def callself inputs trainingnone masknone if self functional return self functional callinputs trainingtraining maskmask fallback just apply the layer sequence this typically happens if inputs is a nested struct for layer in self layers during each iteration inputs are the inputs to layer and outputs are the outputs of layer applied to inputs at the end of each iteration inputs is set to outputs to prepare for the next layer kwargs if layer callhasmaskarg kwargsmask mask if layer callhastrainingarg and training is not none kwargstraining training outputs layerinputs kwargs inputs outputs def getmaskfromkerastensorkt return getattrkt kerasmask none mask tree mapstructuregetmaskfromkerastensor outputs return outputs property def layersself historically sequential layers only returns layers that were added via add and omits the autogenerated inputlayer that comes at the bottom of the stack layers self layers if layers and isinstancelayers0 inputlayer return layers1 return layers def computeoutputspecself inputs trainingnone masknone if self functional return self functional computeoutputspec inputs trainingtraining maskmask direct application for layer in self layers outputs layer computeoutputspec inputs trainingtraining ignore mask inputs outputs return outputs property def inputshapeself if self functional return self functional inputshape raise valueerror fsequential model self name has no defined input shape yet property def outputshapeself if self functional return self functional outputshape raise valueerror fsequential model self name has no defined output shape yet property def inputsself if self functional return self functional inputs raise valueerror fsequential model self name has no defined inputs yet property def outputsself if self functional return self functional outputs raise valueerror fsequential model self name has no defined outputs yet property def inputdtypeself sequential call will try to convert its inputs to the dtype expected by its input layer if any layers self layers if layers and isinstancelayers0 inputlayer return layers0 dtype return super inputdtype def islayernameuniqueself layer for reflayer in self layers if layer name reflayer name and reflayer is not layer return false return true def getconfigself serializefn serializationlib serializekerasobject if globalstate getglobalattributeuselegacyconfig false legacy format serialization used for h5 and savedmodel formats serializefn legacyserialization serializekerasobject layerconfigs for layer in super layers super layers include the inputlayer if available it is filtered out of self layers layerconfigs appendserializefnlayer config model getconfigself configname self name configlayers copy deepcopylayerconfigs if self functional is not none configbuildinputshape self layers0 batchshape return config classmethod def fromconfigcls config customobjectsnone if name in config name configname buildinputshape config getbuildinputshape layerconfigs configlayers else name none layerconfigs config model clsnamename for layerconfig in layerconfigs if module not in layerconfig legacy format deserialization no module key used for h5 and savedmodel formats layer savingutils modelfromconfig layerconfig customobjectscustomobjects else layer serializationlib deserializekerasobject layerconfig customobjectscustomobjects model addlayer if not model functional and buildinputshape and isinstancebuildinputshape tuple list model buildbuildinputshape return model sequential groups a linear stack of layers into a model examples python model keras sequential model add keras input shape 16 model add keras layers dense 8 note that you can also omit the initial input in that case the model doesn t have any weights until the first call to a training evaluation method since it isn t yet built model keras sequential model add keras layers dense 8 model add keras layers dense 4 model weights not created yet whereas if you specify an input the model gets built continuously as you are adding layers model keras sequential model add keras input shape 16 model add keras layers dense 8 len model weights returns 2 when using the delayed build pattern no input shape specified you can choose to manually build your model by calling build batch_input_shape model keras sequential model add keras layers dense 8 model add keras layers dense 4 model build none 16 len model weights returns 4 note that when using the delayed build pattern no input shape specified the model gets built the first time you call fit eval or predict or the first time you call the model on some input data model keras sequential model add keras layers dense 8 model add keras layers dense 1 model compile optimizer sgd loss mse this builds the model for the first time model fit x y batch_size 32 epochs 10 adds a layer instance on top of the layer stack args layer layer instance legacy case if the first layer has an input_shape arg use it to build an inputlayer if we are passed a keras tensor keras input we extract the input layer from its keras history and use that removes the last layer in the model unlike other layers sequential is mutable after build do not attempt to build if the model does not have a single input tensor do not attempt to build if the model does not have a single input tensor build functional model can happen if shape inference is not implemented todo consider reverting inbound nodes on layers processed fallback just apply the layer sequence this typically happens if inputs is a nested struct during each iteration inputs are the inputs to layer and outputs are the outputs of layer applied to inputs at the end of each iteration inputs is set to outputs to prepare for the next layer historically sequential layers only returns layers that were added via add and omits the auto generated inputlayer that comes at the bottom of the stack direct application ignore mask sequential __call__ will try to convert its inputs to the dtype expected by its input layer if any legacy format serialization used for h5 and savedmodel formats super layers include the inputlayer if available it is filtered out of self layers legacy format deserialization no module key used for h5 and savedmodel formats
import copy
import inspect

import tree

from keras.api_export import keras_export
from keras.backend.common import global_state
from keras.layers.core.input_layer import InputLayer
from keras.layers.layer import Layer
from keras.legacy.saving import saving_utils
from keras.legacy.saving import serialization as legacy_serialization
from keras.models.functional import Functional
from keras.models.model import Model
from keras.saving import serialization_lib


@keras_export(["keras.Sequential", "keras.models.Sequential"])
class Sequential(Model):
    

    def __init__(self, layers=None, trainable=True, name=None):
        super().__init__(trainable=trainable, name=name)
        self._functional = None
        self._layers = []
        if layers:
            for layer in layers:
                self.add(layer, rebuild=False)
            self._maybe_rebuild()

    def add(self, layer, rebuild=True):
        
        
        
        if not self._layers:
            if getattr(layer, "_input_shape_arg", None) is not None:
                self.add(InputLayer(shape=layer._input_shape_arg))

        
        
        if hasattr(layer, "_keras_history"):
            origin_layer = layer._keras_history[0]
            if isinstance(origin_layer, InputLayer):
                layer = origin_layer
        if not isinstance(layer, Layer):
            raise ValueError(
                "Only instances of `keras.Layer` can be "
                f"added to a Sequential model. Received: {layer} "
                f"(of type {type(layer)})"
            )
        if not self._is_layer_name_unique(layer):
            raise ValueError(
                "All layers added to a Sequential model "
                f"should have unique names. Name '{layer.name}' is already "
                "the name of a layer in this model. Update the `name` argument "
                "to pass a unique name."
            )
        if (
            isinstance(layer, InputLayer)
            and self._layers
            and isinstance(self._layers[0], InputLayer)
        ):
            raise ValueError(
                f"Sequential model '{self.name}' has already been configured "
                f"to use input shape {self._layers[0].batch_shape}. You cannot "
                f"add a different Input layer to it."
            )

        self._layers.append(layer)
        if rebuild:
            self._maybe_rebuild()
        else:
            self.built = False
            self._functional = None

    def pop(self, rebuild=True):
        
        layer = self._layers.pop()
        self.built = False
        self._functional = None
        if rebuild:
            self._maybe_rebuild()
        return layer

    def _maybe_rebuild(self):
        self.built = False
        self._functional = None
        if isinstance(self._layers[0], InputLayer) and len(self._layers) > 1:
            input_shape = self._layers[0].batch_shape
            self.build(input_shape)

    def _lock_state(self):
        
        pass

    def build(self, input_shape=None):
        if not isinstance(input_shape, (tuple, list)):
            
            
            return
        if input_shape and not (
            isinstance(input_shape[0], int) or input_shape[0] is None
        ):
            
            
            return
        if not self._layers:
            raise ValueError(
                f"Sequential model {self.name} cannot be built because it has "
                "no layers. Call `model.add(layer)`."
            )
        if isinstance(self._layers[0], InputLayer):
            if self._layers[0].batch_shape != input_shape:
                raise ValueError(
                    f"Sequential model '{self.name}' has already been "
                    "configured to use input shape "
                    f"{self._layers[0].batch_shape}. You cannot build it "
                    f"with input_shape {input_shape}"
                )
        else:
            dtype = self._layers[0].compute_dtype
            self._layers = [
                InputLayer(batch_shape=input_shape, dtype=dtype)
            ] + self._layers

        
        inputs = self._layers[0].output
        x = inputs
        for layer in self._layers[1:]:
            try:
                x = layer(x)
            except NotImplementedError:
                
                
                return
            except TypeError as e:
                signature = inspect.signature(layer.call)
                positional_args = [
                    param
                    for param in signature.parameters.values()
                    if param.default == inspect.Parameter.empty
                ]
                if len(positional_args) != 1:
                    raise ValueError(
                        "Layers added to a Sequential model "
                        "can only have a single positional argument, "
                        f"the input tensor. Layer {layer.__class__.__name__} "
                        f"has multiple positional arguments: {positional_args}"
                    )
                raise e
        outputs = x
        self._functional = Functional(inputs=inputs, outputs=outputs)
        self.built = True

    def call(self, inputs, training=None, mask=None):
        if self._functional:
            return self._functional.call(inputs, training=training, mask=mask)

        
        
        for layer in self.layers:
            
            
            
            
            kwargs = {}
            if layer._call_has_mask_arg:
                kwargs["mask"] = mask
            if layer._call_has_training_arg and training is not None:
                kwargs["training"] = training
            outputs = layer(inputs, **kwargs)
            inputs = outputs

            def _get_mask_from_keras_tensor(kt):
                return getattr(kt, "_keras_mask", None)

            mask = tree.map_structure(_get_mask_from_keras_tensor, outputs)
        return outputs

    @property
    def layers(self):
        
        
        
        layers = self._layers
        if layers and isinstance(layers[0], InputLayer):
            return layers[1:]
        return layers[:]

    def compute_output_spec(self, inputs, training=None, mask=None):
        if self._functional:
            return self._functional.compute_output_spec(
                inputs, training=training, mask=mask
            )
        
        for layer in self.layers:
            outputs = layer.compute_output_spec(
                inputs, training=training
            )  
            inputs = outputs
        return outputs

    @property
    def input_shape(self):
        if self._functional:
            return self._functional.input_shape
        raise ValueError(
            f"Sequential model '{self.name}' has no defined input shape yet."
        )

    @property
    def output_shape(self):
        if self._functional:
            return self._functional.output_shape
        raise ValueError(
            f"Sequential model '{self.name}' has no defined output shape yet."
        )

    @property
    def inputs(self):
        if self._functional:
            return self._functional.inputs
        raise ValueError(
            f"Sequential model '{self.name}' has no defined inputs yet."
        )

    @property
    def outputs(self):
        if self._functional:
            return self._functional.outputs
        raise ValueError(
            f"Sequential model '{self.name}' has no defined outputs yet."
        )

    @property
    def input_dtype(self):
        
        
        layers = self._layers
        if layers and isinstance(layers[0], InputLayer):
            return layers[0].dtype
        return super().input_dtype

    def _is_layer_name_unique(self, layer):
        for ref_layer in self._layers:
            if layer.name == ref_layer.name and ref_layer is not layer:
                return False
        return True

    def get_config(self):
        serialize_fn = serialization_lib.serialize_keras_object
        if global_state.get_global_attribute("use_legacy_config", False):
            
            serialize_fn = legacy_serialization.serialize_keras_object
        layer_configs = []
        for layer in super().layers:
            
            
            layer_configs.append(serialize_fn(layer))
        config = Model.get_config(self)
        config["name"] = self.name
        config["layers"] = copy.deepcopy(layer_configs)
        if self._functional is not None:
            config["build_input_shape"] = self._layers[0].batch_shape
        return config

    @classmethod
    def from_config(cls, config, custom_objects=None):
        if "name" in config:
            name = config["name"]
            build_input_shape = config.get("build_input_shape")
            layer_configs = config["layers"]
        else:
            name = None
            layer_configs = config
        model = cls(name=name)
        for layer_config in layer_configs:
            if "module" not in layer_config:
                
                
                layer = saving_utils.model_from_config(
                    layer_config,
                    custom_objects=custom_objects,
                )
            else:
                layer = serialization_lib.deserialize_keras_object(
                    layer_config,
                    custom_objects=custom_objects,
                )
            model.add(layer)
        if (
            not model._functional
            and build_input_shape
            and isinstance(build_input_shape, (tuple, list))
        ):
            model.build(build_input_shape)
        return model