Moving Beyond the Stack: Building Non-Linear Topologies with the Keras Functional API
Stop limiting your neural networks to linear stacks. Learn how to use the Keras Functional API to build multi-input and multi-output architectures for complex data.
17 Apr 2026, 21:01 UTC

The Limitation of Sequential Stacks
Most developers start with the Sequential API because it is intuitive: you stack layers like LEGO bricks, and data flows linearly from top to bottom. However, real-world data rarely fits a single line. If you need to process an image and its metadata simultaneously, or if you want a single model to predict both a category and a numerical value, a linear stack fails.
The Keras Functional API solves this by treating layers as functions. Instead of adding layers to a list, you define tensors as inputs and pass them through layers to produce new tensors. This allows you to build Directed Acyclic Graphs (DAGs)—models with multiple inputs, multiple outputs, or shared layers.
Handling Multi-Input Architectures
Multi-input models are essential when your feature sets have different natures. For example, you might have a high-dimensional image (processed via Convolutional layers) and a set of categorical user data (processed via Dense layers). You cannot simply concatenate these at the start because they require different preprocessing.
In the Functional API, you define separate keras.Input objects for each source. You process them independently through their own "branches" and then merge them using a layer like Concatenate or Add. This ensures each data type is transformed into a compatible latent representation before they are combined for the final decision.
Branching for Multi-Output Predictions
Multi-output models allow a single "backbone" to extract general features that serve multiple goals. This is often more efficient than training two separate models, as the backbone learns a more robust representation of the data by satisfying two different loss functions.
To implement this, you take the output of your final shared layer and pass it into two or more separate "heads." For instance, one head might end in a softmax activation for classification, while another ends in a linear activation for regression. When compiling, you can assign different loss functions to each output, allowing the model to balance its learning priorities.
Implementation: A Multi-Input, Multi-Output Model
The following example demonstrates a model that takes two inputs (a feature vector and a category) and produces two outputs (a price prediction and a quality rating). This assumes Keras 3.x or TensorFlow 2.x.
import keras
from keras import layers
# Input 1: Numerical features (e.g., 10 dimensions)
input_num = keras.Input(shape=(10,), name="num_input")
x1 = layers.Dense(16, activation="relu")(input_num)
# Input 2: Categorical features (e.g., 5 dimensions)
input_cat = keras.Input(shape=(5,), name="cat_input")
x2 = layers.Dense(16, activation="relu")(input_cat)
# Merge the branches
merged = layers.Concatenate()([x1, x2])
shared_dense = layers.Dense(32, activation="relu")(merged)
# Output 1: Regression (Price)
output_price = layers.Dense(1, name="price")(shared_dense)
# Output 2: Classification (Quality Rating)
output_quality = layers.Dense(3, activation="softmax", name="quality")(shared_dense)
# Instantiate the model by defining the graph boundaries
model = keras.Model(inputs=[input_num, input_cat], outputs=[output_price, output_quality])
model.compile(
optimizer="adam",
loss={"price": "mse", "quality": "categorical_crossentropy"},
metrics={"quality": "accuracy"}
)
# Verification: Check the graph topology
model.summary()
Verification and Risks
To verify the model, run model.summary(). Look for the Connected to column to ensure the Concatenate layer is receiving tensors from both x1 and x2. A common risk in the Functional API is the shape mismatch. If you attempt to Add() two tensors with different dimensions, Keras will throw a ValueError during the first forward pass. Always check that the tensors being merged share the same axis dimensions.
Trade-offs and Constraints
While the Functional API is powerful, it introduces complexity in data pipeline management. You can no longer pass a single NumPy array to model.fit(); you must pass a list or a dictionary of inputs matching the names defined in your Input layers.
Additionally, if you use shared layers (calling the same layer instance on two different tensors), remember that the weights are shared. Gradients from both paths will accumulate during backpropagation, which can lead to unstable training if the two paths represent fundamentally different data distributions.
Actionable Summary
Use the Functional API when your model requires a non-linear flow. To ensure success:
- Define each input source with a unique
keras.Input. - Use
Concatenatefor merging different feature sets. - Name your output layers to easily map specific loss functions during
compile(). - Validate the connectivity using
model.summary()before feeding real data.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.