The functional API

Keras · Essentials · 12 min

Most real architectures branch or merge; the functional API expresses them while keeping summary, plotting and saving.

In the functional API you create symbolic inputs with keras.Input, call layers on them like functions, and wrap the result in keras.Model(inputs=..., outputs=...). Each call returns a symbolic KerasTensor with a known shape, so shape errors appear while you build the model, not during training.

It handles what Sequential cannot:

  • Multiple inputs, such as text plus tabular metadata, merged with layers.concatenate or layers.add.
  • Multiple outputs, each with its own loss (and optional loss_weights), passed to compile as a dict keyed by output layer names.
  • Skip connections (x = layers.add([x, shortcut])), as in ResNets.
  • Shared layers: calling the same layer object on two inputs reuses its weights, as in siamese networks.

The model is a static graph of layers, so Keras can show it with summary() or keras.utils.plot_model, serialise it fully, and let you extract intermediate outputs with keras.Model(model.input, model.get_layer("name").output).

When feeding data, pass dicts keyed by input names ({"text": ..., "meta": ...}) for clarity. Functional models are themselves layers, so you can call one model inside another.

Example

import keras
from keras import layers

text_in = keras.Input(shape=(100,), dtype="int32", name="text")   # token ids
meta_in = keras.Input(shape=(5,), name="meta")

t = layers.Embedding(10_000, 32)(text_in)
t = layers.GlobalAveragePooling1D()(t)
m = layers.Dense(16, activation="relu")(meta_in)
h = layers.concatenate([t, m])

priority = layers.Dense(1, activation="sigmoid", name="priority")(h)
dept = layers.Dense(4, name="dept")(h)                           # logits

model = keras.Model(inputs=[text_in, meta_in], outputs=[priority, dept])
model.compile(optimizer="adam",
              loss={"priority": "binary_crossentropy",
                    "dept": keras.losses.SparseCategoricalCrossentropy(from_logits=True)})
print([tuple(o.shape) for o in model.outputs])   # [(None, 1), (None, 4)]
# model.fit({"text": x_text, "meta": x_meta}, {"priority": y_p, "dept": y_d})

Remember

  • Call layers on keras.Input tensors, then keras.Model(inputs, outputs)
  • Supports multi-input/output, skip connections and shared layers
  • Name inputs and outputs and pass dicts to compile and fit

Watch out for

  • Creating a new layer object for each branch when you meant to share weights.

Check yourself

  1. How do you make two inputs share the same Dense weights in the functional API?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: keras.io/guides/functional_api/