TensorFlow, Interactively

advanced · ~25 min

Keras Functional API, Deeper

Beyond a single stack of layers: multi-input/output models, shared layers, and skip connections you can't build with Sequential.

This module builds on About Keras. Feel free to jump ahead anyway.

The Keras basics module introduced the Functional API as "what you reach for when Sequential's one-input-one-output stack isn't enough." This module is about what that actually buys you.

The core idea is simple: instead of Sequential([...]) building an implicit chain, you call layers directly as functions on tensors, and wire the graph yourself.

inputs = keras.Input(shape=(2,))
x = layers.Dense(16, activation="relu")(inputs)
outputs = layers.Dense(1, activation="sigmoid")(x)
model = keras.Model(inputs=inputs, outputs=outputs)

That's identical to a two-layer Sequential — but because you're holding onto the actual tensors (inputs, x, outputs) instead of just appending to a list, you can do things a straight stack can't express at all:

  • Multiple inputs or outputs — a model that takes both an image and some tabular metadata, or one that predicts a class and a numeric score from the same shared trunk.
  • Shared layers — apply the same layer instance to two different inputs (e.g. encoding two sentences with one shared embedding for a similarity model), so they're forced to learn the same representation.
  • Non-linear topologies — a layer's output feeding into more than one place, or two branches merging back together. This is what a skip connection is: layers.Add()([x, block(x)]) instead of just block(x).
🔍 Deep dive: Why skip connections make deep networks trainable

Stack enough plain layers and gradients flowing backward through all of them tend to shrink (or occasionally blow up) multiplicatively — the classic vanishing gradient problem. A skip connection gives the gradient a second path — straight through the Add(), with derivative 1 — so even a very deep stack has at least one route back to early layers that doesn't get squashed. This is the core idea behind ResNet, and it's a big part of why networks with hundreds of layers became trainable at all. The playground below builds an 8-block deep network two ways — with and without skip connections around each block — and trains both, live, on the same hard dataset.

Production note

layers.Add() requires its inputs to be the same shape (it's an elementwise sum) — that's why residual blocks keep the same number of units throughout. layers.Concatenate() is the merge to reach for when shapes differ or you want the model to see both signals side by side instead of summed.

Beginner tip

If the "without skip connections" model in the playground looks like it's barely learning at all while the "with skip connections" one solves the task cleanly, that's not a rigged demo — 8 plain layers deep is genuinely a hard optimization problem for a small MLP, which is exactly the failure mode skip connections exist to fix.

Playground

Step 0no skip acc: 0% · with skip acc: 0%

8 layers deep, no skip connections

8 layers deep, with skip connections

  • no skip
  • with skip
steptraining loss