TensorFlow and Keras

Sequential and functional models

Keras gives you two ways to describe a network — a straight pipeline for stacks, and a wiring diagram for anything with branches.

Read these first

On this page 5
  1. Why two ways exist
  2. How it works
  3. A real example you have seen
  4. Remember this
  5. What to learn next

One lesson, three depths. Pick the one that fits you today — you can switch any time.

Beginner — No maths. Plain English.

Keras lets you describe a neural network two ways: as a straight stack of layers, or as a wiring diagram where data can branch and rejoin.

Think of a single-file queue at a railway ticket counter. Every person enters at one end, passes each window in order, and leaves at the other end. That is a Sequential model — data flows through layer 1, then layer 2, then layer 3, with no shortcuts.

Now think of a city road network instead. Roads fork, run in parallel, and merge again at junctions. That is the functional API — a way of building models where you draw the connections yourself, so data can split and meet again.

Why two ways exist

Most simple networks are queues. Input goes in, passes each layer once, prediction comes out. Sequential handles this with the least typing, and it is where everyone starts.

But many real models are not queues:

  • A model that takes two inputs — a photo and a text caption.
  • A model with two outputs — the price of a house and the confidence in that price.
  • A skip connection, where the original input jumps ahead and rejoins later. Modern image models depend on these.

A queue cannot describe a fork. The functional API exists for exactly these shapes.

How it works

Sequential:                      Functional:

input                            input ──────────────┐
  ↓                                ↓                 │
layer 1                          layer 1             │  (skip)
  ↓                                ↓                 │
layer 2                          merge  ←────────────┘
  ↓                                ↓
output                           output

In Sequential, you hand Keras a list and it connects everything for you. In functional, every layer is called like a function on the output of another layer. You describe the arrows yourself.

A real example you have seen

Google Translate's camera mode looks at an image and produces text. Under the hood, models like that combine an image branch and a text branch — two roads merging. No straight queue can express that. The teams who build them describe the wiring, which is what the functional API is for.

Remember this

  • Sequential = a straight stack. Least code. Covers most beginner models.
  • Functional = a wiring diagram. Needed for branches, merges, multiple inputs or outputs.
  • Both produce the same kind of object in the end — a Keras Model you train the same way.

What to learn next

Developer — Code and libraries.

Setup

bash
pip install tensorflow

Fair warning: the TensorFlow wheel is one of the largest on PyPI — a download of several hundred MB, varying by platform. Outputs below verified with TensorFlow 2.21 and Keras 3.12 on CPU. Since TensorFlow 2.16, tf.keras and import keras point at the same Keras 3, so import keras is the current style.

The Sequential way

sequential_model.py
import keras
from keras import layers

model = keras.Sequential([
    keras.Input(shape=(4,)),               # each sample has 4 features
    layers.Dense(8, activation="relu"),
    layers.Dense(1, activation="sigmoid"),
])
model.summary()
Output
Model: "sequential"
┌─────────────────────────────────┬────────────────────────┬───────────────┐
│ Layer (type)                    │ Output Shape           │       Param # │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense (Dense)                   │ (None, 8)              │            40 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_1 (Dense)                 │ (None, 1)              │             9 │
└─────────────────────────────────┴────────────────────────┴───────────────┘
 Total params: 49 (196.00 B)
 Trainable params: 49 (196.00 B)
 Non-trainable params: 0 (0.00 B)

keras.Input(shape=(4,)) declares the shape of one sample, without the batch dimension. The None in the summary is the batch size — unknown until you feed data. In Keras 3, passing input_shape= to the first layer is deprecated; a keras.Input at the top is the current way.

The param counts are worth checking by hand. First layer: 4 inputs × 8 neurons + 8 biases = 40. Second: 8 × 1 + 1 = 9. If your mental count disagrees with summary(), your mental picture of the model is wrong — this catch-a-bug trick costs ten seconds.

The functional way

functional_model.py
import numpy as np
import keras
from keras import layers

inputs = keras.Input(shape=(4,))
h = layers.Dense(8, activation="relu")(inputs)
merged = layers.Concatenate()([h, inputs])   # skip connection: raw inputs rejoin the flow
outputs = layers.Dense(1, activation="sigmoid")(merged)
model = keras.Model(inputs=inputs, outputs=outputs)

print([layer.name for layer in model.layers])
print("params:", model.count_params())

x = np.array([[0.1, 0.2, 0.3, 0.4]], dtype="float32")
print("prediction shape:", model.predict(x, verbose=0).shape)
Output
['input_layer', 'dense', 'concatenate', 'dense_1']
params: 53
prediction shape: (1, 1)

Read the pattern: layers.Dense(8, activation="relu")(inputs) is two calls. The first call creates the layer. The second call — the (inputs) part — connects it, and returns a symbolic tensor you pass onward. You are drawing arrows, one line per arrow.

The Concatenate line is the part Sequential cannot express. The raw inputs tensor gets used twice: once feeding the Dense layer, once jumping straight to the merge. The final Dense now sees 8 + 4 = 12 numbers per sample, which is why the count rose to 53 (12 × 1 + 1 = 13 for that layer).

keras.Model(inputs=..., outputs=...) then says: everything reachable between these two ends is the model.

Common mistakes

Forgetting the second pair of brackets. layers.Dense(8)(x) connects a layer; layers.Dense(8) only creates one. If your "model" is a list of unconnected layers, you typed the second form. The symptom: keras.Model(...) complains the output is not a Keras tensor.

Reusing a layer when you meant a new one. Calling the same layer object on two tensors shares one set of weights between both places. That is a real feature (Siamese networks use it on purpose) — but if you wanted two independent layers, create two objects.

Feeding the wrong feature count. Declare shape=(4,) and pass 5-feature data, and predict raises: Input 0 of layer "dense" is incompatible with the layer: expected axis -1 of input shape to have value 4. The fix is to read the error's shape arithmetic, not to reshape blindly.

Starting with functional for a plain stack. If your model has no branches, Sequential is shorter and harder to get wrong. Reach for functional when the shape of the problem demands it.

Try it yourself

Give the functional model a second output: a Dense(1) regression head connected to h, alongside the existing sigmoid head. Pass outputs=[out1, out2] to keras.Model. Print model.summary() and check where the graph forks.

What to learn next

Researcher — Mathematics and papers.

What a functional model actually is

A functional Keras model is a directed acyclic graph (DAG) $G = (V, E)$ where each node $v \in V$ is a layer invocation and each edge carries a tensor. Symbols: $V$ is the set of layer calls, $E$ the tensor-passing connections, and "acyclic" means no tensor can feed back into its own ancestry — recurrence lives inside layers (LSTM cells), never in the graph.

Calling a layer on a symbolic tensor does no arithmetic. It appends a node to the graph and returns a placeholder (KerasTensor) recording shape, dtype and provenance. keras.Model(inputs, outputs) performs a graph traversal from outputs back to inputs and topologically sorts the reachable subgraph. This is why unused branches silently vanish from summary() — they are unreachable in the backward traversal.

Because the whole architecture is data (a graph), Keras can serialise it to JSON, plot it, and statically check shapes at construction time. Subclassed models (class MyModel(keras.Model)) trade all of that away for Python control flow: the architecture only exists as bytecode, so get_config() and shape inference must be written by hand.

Cost of the abstraction

Graph construction is O(|V| + |E|) once, at build time; it adds nothing to per-batch runtime. Training and inference cost are set by the layers, not the wiring style — a Sequential and a functional model with identical layers compile to identical computations.

Lineage

  • Chollet (2015) — Keras released as a Theano-frontend; the functional API landed in 2015–16, generalising the original Graph container.
  • He et al. (2016), Deep residual learning for image recognition — the skip connection pattern that made non-sequential wiring mandatory. ResNet's identity shortcuts are Add() merges in Keras terms.
  • Abadi et al. (2016), TensorFlow: a system for large-scale machine learning — the dataflow-graph substrate Keras originally compiled to.
  • Keras 3 (2023) rebuilt the same three APIs — Sequential, functional, subclassing — over swappable backends (TensorFlow, JAX, PyTorch), which is possible precisely because the functional graph is backend-neutral data.

The three-API design is now the industry pattern: PyTorch's nn.Sequential mirrors Sequential, and its free-form nn.Module mirrors subclassing — compare how PyTorch structures models.

What to learn next

What to learn next

These follow on from what you just read.

  • TensorFlow and Keras

    compile, fit and evaluate

    Three calls take a Keras model from an untrained shell to a scored result — compile sets the rules, fit does the learning, evaluate checks it honestly.

  • TensorFlow and Keras

    Callbacks

    Callbacks are hooks that let code run at set moments during training — to stop early, save the best model, or log progress — without touching the training loop.

  • TensorFlow and Keras

    Custom layers, losses and metrics

    When the built-in parts stop fitting your problem, Keras lets you write your own layer, your own definition of wrong, and your own scorecard.