Skip to content

14. Keras Sequential and Functional API

Learn how to design Deep Learning models using Keras's Sequential and Functional APIs, understand when each approach should be used, build complex computation graphs, work with multiple inputs and outputs, reuse layers, implement skip connections, and design production-ready neural network architectures.


๐ŸŽฏ Learning Objectives

After completing this chapter, you will be able to:

  • Understand the Keras Sequential API
  • Understand the Keras Functional API
  • Compare Sequential and Functional model construction
  • Build simple neural networks using Sequential
  • Build complex neural networks using the Functional API
  • Understand Keras symbolic tensors
  • Understand the relationship between inputs, layers, and outputs
  • Build models with multiple inputs
  • Build models with multiple outputs
  • Reuse layers across different parts of a model
  • Build branching architectures
  • Build merging architectures
  • Implement skip connections
  • Understand residual-style connections
  • Inspect Functional API model graphs
  • Visualize model architecture
  • Share layers between models
  • Build reusable model components
  • Understand model composition
  • Understand when to choose Sequential, Functional API, or subclassing
  • Design maintainable Keras architectures for enterprise Deep Learning systems

๐Ÿ“– Overview

Keras provides multiple ways to define neural network models.

The three major approaches are:

Sequential API
      โ”‚
      โ”œโ”€โ”€ Simple linear stacks
      โ”‚
Functional API
      โ”‚
      โ”œโ”€โ”€ Complex computation graphs
      โ”‚
Model Subclassing
      โ”‚
      โ””โ”€โ”€ Maximum customization

The first two approaches are especially important for most Deep Learning applications.

flowchart TD

    KERAS["Keras Model Construction"]

    KERAS --> SEQ["Sequential API"]
    KERAS --> FUNC["Functional API"]
    KERAS --> SUB["Model Subclassing"]

    SEQ --> SIMPLE["Simple Layer Stack"]

    FUNC --> COMPLEX["Complex Graphs"]

    SUB --> CUSTOM["Custom Architecture / Behavior"]

๐Ÿง  Why Multiple Model APIs?

Different neural networks have different architectural requirements.

A simple classifier may look like:

Input
  โ†“
Dense
  โ†“
Dense
  โ†“
Output

A more complex architecture may look like:

             โ”Œโ”€โ”€ Dense โ”€โ”€โ”
Input โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค            โ”œโ”€โ”€ Merge โ”€โ”€ Output
             โ””โ”€โ”€ Dense โ”€โ”€โ”€โ”˜

A residual network may contain:

Input โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  โ†“                   โ”‚
Layer                 โ”‚
  โ†“                   โ”‚
Layer                 โ”‚
  โ†“                   โ”‚
  โ””โ”€โ”€โ”€โ”€โ”€โ”€ Add โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The Sequential API is ideal for the first case.

The Functional API is designed for the second and third cases.


๐Ÿ— Sequential API

The Sequential API represents a simple linear stack of layers.

flowchart LR

    INPUT["Input"]

    L1["Dense"]

    L2["Dense"]

    L3["Dense"]

    OUTPUT["Output"]

    INPUT --> L1
    L1 --> L2
    L2 --> L3
    L3 --> OUTPUT

Each layer receives the output of the previous layer.


๐Ÿงช Basic Sequential Model

import tensorflow as tf


model = tf.keras.Sequential([

    tf.keras.Input(
        shape=(784,)
    ),

    tf.keras.layers.Dense(
        128,
        activation="relu"
    ),

    tf.keras.layers.Dense(
        64,
        activation="relu"
    ),

    tf.keras.layers.Dense(
        10,
        activation="softmax"
    )
])

The architecture is:

784 Features
     โ†“
Dense(128)
     โ†“
Dense(64)
     โ†“
Dense(10)
     โ†“
Softmax

๐Ÿง  Sequential Model Concept

The Sequential API can be viewed as:

[ f(x) = f_n( f_{n-1}( ... f_2( f_1(x) ) ... ) ) ]

Each layer transforms the output of the previous layer.


๐Ÿงฑ Adding Layers

Layers can also be added incrementally.

model = tf.keras.Sequential()

model.add(
    tf.keras.Input(
        shape=(784,)
    )
)

model.add(
    tf.keras.layers.Dense(
        128,
        activation="relu"
    )
)

model.add(
    tf.keras.layers.Dense(
        10,
        activation="softmax"
    )
)

๐Ÿง  When Sequential Works Well

Use Sequential when:

  • The architecture is linear
  • There is one input
  • There is one output
  • Every layer has exactly one input
  • Every layer produces exactly one output
  • There are no branching paths
  • There are no skip connections

Typical examples:

Simple Classification
Simple Regression
Basic MLP
Simple CNN
Basic Feed-Forward Network

โš  Sequential API Limitations

Sequential becomes unsuitable when the architecture contains:

Multiple Inputs
Multiple Outputs
Branching
Layer Sharing
Skip Connections
Non-linear Graphs

For these architectures, use the Functional API.


๐Ÿง  Functional API

The Functional API represents a neural network as a directed computation graph.

Instead of saying:

model.add(layer)

we explicitly connect tensors:

x = layer(inputs)

and finally create:

model = tf.keras.Model(
    inputs=inputs,
    outputs=outputs
)

๐Ÿ— Functional API Architecture

flowchart LR

    INPUT["Input Tensor"]

    D1["Dense 128"]

    D2["Dense 64"]

    OUT["Output"]

    INPUT --> D1
    D1 --> D2
    D2 --> OUT

The important difference is that the engineer explicitly defines the graph.


๐Ÿงช Basic Functional API Model

import tensorflow as tf


inputs = tf.keras.Input(
    shape=(784,)
)

x = tf.keras.layers.Dense(
    128,
    activation="relu"
)(inputs)

x = tf.keras.layers.Dense(
    64,
    activation="relu"
)(x)

outputs = tf.keras.layers.Dense(
    10,
    activation="softmax"
)(x)

model = tf.keras.Model(
    inputs=inputs,
    outputs=outputs
)

๐Ÿง  Functional API Mental Model

Think of:

inputs

as the starting node.

Each layer:

layer(x)

creates a new tensor.

Finally:

outputs

becomes the endpoint.

flowchart LR

    INPUT["inputs"]

    L1["Layer 1"]

    T1["Tensor"]

    L2["Layer 2"]

    T2["Tensor"]

    OUT["outputs"]

    INPUT --> L1
    L1 --> T1
    T1 --> L2
    L2 --> T2
    T2 --> OUT

๐Ÿง  Symbolic Tensors

When using the Functional API:

inputs = tf.keras.Input(
    shape=(784,)
)

inputs represents a symbolic tensor describing the expected computation.

It is not an actual batch of training data.

This distinction is important.

Functional API

Symbolic Tensor
      โ†“
Describes Computation
      โ†“
Keras Builds Graph

During training:

Actual Data
      โ†“
Computation Graph
      โ†“
Output

๐Ÿง  Sequential vs Functional

Feature Sequential Functional
Simple layer stack โœ… โœ…
One input โœ… โœ…
One output โœ… โœ…
Multiple inputs โŒ โœ…
Multiple outputs โŒ โœ…
Branching โŒ โœ…
Skip connections โŒ โœ…
Layer sharing โŒ โœ…
Complex graph โŒ โœ…
Easy to read โœ… โœ…
Complex architectures โŒ โœ…

๐Ÿ— Branching Architecture

Suppose an input should pass through two different paths.

flowchart TD

    INPUT["Input"]

    INPUT --> PATH1["Path 1<br>Dense 128"]

    INPUT --> PATH2["Path 2<br>Dense 64"]

    PATH1 --> MERGE["Concatenate"]

    PATH2 --> MERGE

    MERGE --> OUTPUT["Output"]

This cannot naturally be represented as a simple Sequential stack.

The Functional API handles it directly.


๐Ÿงช Branching Example

inputs = tf.keras.Input(
    shape=(100,)
)

branch_1 = tf.keras.layers.Dense(
    128,
    activation="relu"
)(inputs)

branch_2 = tf.keras.layers.Dense(
    64,
    activation="relu"
)(inputs)

merged = tf.keras.layers.Concatenate()(
    [
        branch_1,
        branch_2
    ]
)

outputs = tf.keras.layers.Dense(
    10,
    activation="softmax"
)(merged)

model = tf.keras.Model(
    inputs=inputs,
    outputs=outputs
)

๐Ÿง  Concatenation

Concatenation joins tensors along a specified dimension.

Conceptually:

Tensor A
[128 features]

        +

Tensor B
[64 features]

        โ†“

Merged Tensor
[192 features]

The resulting tensor can then be passed to another layer.


๐Ÿ”€ Add vs Concatenate

Two common merging operations are:

Add
Concatenate

Add

Requires compatible shapes.

A โ”€โ”€โ”
    โ”œโ”€โ”€ Add
B โ”€โ”€โ”˜

Concatenate

Combines feature dimensions.

A โ”€โ”€โ”
    โ”œโ”€โ”€ Concatenate
B โ”€โ”€โ”˜

๐Ÿงช Add Example

x1 = tf.keras.layers.Dense(
    128
)(inputs)

x2 = tf.keras.layers.Dense(
    128
)(inputs)

merged = tf.keras.layers.Add()(
    [
        x1,
        x2
    ]
)

The two tensors must have compatible shapes.


๐Ÿง  Skip Connections

Skip connections allow information to bypass one or more layers.

flowchart TD

    INPUT["Input"]

    L1["Layer 1"]

    L2["Layer 2"]

    ADD["Add"]

    INPUT --> L1
    L1 --> L2
    L2 --> ADD

    INPUT --> ADD

    ADD --> OUTPUT["Output"]

Mathematically:

[ y=F(x)+x ]

This is a core idea behind residual networks.


๐Ÿงช Skip Connection Example

inputs = tf.keras.Input(
    shape=(128,)
)

x = tf.keras.layers.Dense(
    128,
    activation="relu"
)(inputs)

x = tf.keras.layers.Dense(
    128
)(x)

x = tf.keras.layers.Add()(
    [
        x,
        inputs
    ]
)

outputs = tf.keras.layers.Activation(
    "relu"
)(x)

model = tf.keras.Model(
    inputs=inputs,
    outputs=outputs
)

The Functional API makes this architecture straightforward.


๐Ÿง  Layer Reuse

Functional API allows the same layer to be reused.

For example:

shared_layer = tf.keras.layers.Dense(
    64,
    activation="relu"
)

The same layer can process multiple inputs.

x1 = shared_layer(input_1)
x2 = shared_layer(input_2)

This means the layer's weights are shared.


๐Ÿ”„ Shared Layer Architecture

flowchart TD

    INPUT1["Input 1"]
    INPUT2["Input 2"]

    SHARED["Shared Dense Layer"]

    INPUT1 --> SHARED
    INPUT2 --> SHARED

    SHARED --> OUT1["Output 1"]
    SHARED --> OUT2["Output 2"]

This pattern is useful for:

  • Siamese networks
  • Metric learning
  • Shared feature extraction
  • Multi-input architectures

๐Ÿงช Multiple Inputs

Suppose a model receives:

Customer Profile
Transaction History

as separate inputs.

flowchart TD

    PROFILE["Customer Profile"]

    HISTORY["Transaction History"]

    PROFILE --> P["Profile Encoder"]

    HISTORY --> H["History Encoder"]

    P --> MERGE["Concatenate"]

    H --> MERGE

    MERGE --> OUTPUT["Risk Prediction"]

Functional API implementation:

profile_input = tf.keras.Input(
    shape=(20,),
    name="profile"
)

history_input = tf.keras.Input(
    shape=(50,),
    name="history"
)

profile_features = tf.keras.layers.Dense(
    64,
    activation="relu"
)(profile_input)

history_features = tf.keras.layers.Dense(
    128,
    activation="relu"
)(history_input)

merged = tf.keras.layers.Concatenate()(
    [
        profile_features,
        history_features
    ]
)

outputs = tf.keras.layers.Dense(
    1,
    activation="sigmoid"
)(merged)

model = tf.keras.Model(
    inputs=[
        profile_input,
        history_input
    ],
    outputs=outputs
)

๐Ÿง  Multiple Outputs

A model can also produce multiple outputs.

For example:

Input
  โ”‚
  โ”œโ”€โ”€ Classification Head
  โ”‚
  โ””โ”€โ”€ Regression Head
flowchart TD

    INPUT["Shared Input"]

    BACKBONE["Shared Feature Extractor"]

    INPUT --> BACKBONE

    BACKBONE --> CLASS["Classification Head"]

    BACKBONE --> REG["Regression Head"]

    CLASS --> CLASSOUT["Class Output"]

    REG --> REGOUT["Regression Output"]

๐Ÿงช Multi-Output Model

inputs = tf.keras.Input(
    shape=(100,)
)

features = tf.keras.layers.Dense(
    128,
    activation="relu"
)(inputs)

class_output = tf.keras.layers.Dense(
    10,
    activation="softmax",
    name="classification"
)(features)

reg_output = tf.keras.layers.Dense(
    1,
    name="regression"
)(features)

model = tf.keras.Model(
    inputs=inputs,
    outputs=[
        class_output,
        reg_output
    ]
)

๐Ÿง  Multi-Task Learning

A multi-output model can support multi-task learning.

For example:

Shared Representation
        โ”‚
   โ”Œโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”
   โ†“         โ†“
Task A      Task B

One model can learn:

Classification
+
Regression

or:

Object Detection
+
Object Classification

or:

Sentiment
+
Topic Classification

โš™๏ธ Compiling Multi-Output Models

Different outputs can have different losses.

model.compile(

    optimizer="adam",

    loss={
        "classification":
            "sparse_categorical_crossentropy",

        "regression":
            "mse"
    },

    metrics={
        "classification":
            ["accuracy"],

        "regression":
            ["mae"]
    }
)

๐Ÿง  Loss Weighting

Multiple tasks may contribute differently to the total loss.

Conceptually:

[ L_{total} = \lambda_1L_1 + \lambda_2L_2 ]

Example:

model.compile(

    optimizer="adam",

    loss={
        "classification": "sparse_categorical_crossentropy",
        "regression": "mse"
    },

    loss_weights={
        "classification": 1.0,
        "regression": 0.5
    }
)

๐Ÿง  Model Composition

Keras models can themselves behave like layers.

For example:

encoder = tf.keras.Model(
    inputs=encoder_input,
    outputs=encoded
)

decoder = tf.keras.Model(
    inputs=decoder_input,
    outputs=decoded
)

They can then be composed:

autoencoder_output = decoder(
    encoder(inputs)
)

This is extremely useful for:

  • Autoencoders
  • Encoder-decoder architectures
  • Transfer Learning
  • Reusable model components

๐Ÿ— Model-as-a-Layer Concept

flowchart LR

    INPUT["Input"]

    ENCODER["Encoder Model"]

    DECODER["Decoder Model"]

    OUTPUT["Output"]

    INPUT --> ENCODER
    ENCODER --> DECODER
    DECODER --> OUTPUT

๐Ÿง  Reusable Model Components

Instead of creating one huge model, break it into components:

Embedding
     โ†“
Encoder
     โ†“
Feature Extractor
     โ†“
Task Head

This improves:

  • Reusability
  • Testing
  • Maintenance
  • Experimentation

๐Ÿงช Building a Reusable Block

def dense_block(
    units,
    dropout_rate=0.2
):

    return tf.keras.Sequential([

        tf.keras.layers.Dense(
            units,
            activation="relu"
        ),

        tf.keras.layers.BatchNormalization(),

        tf.keras.layers.Dropout(
            dropout_rate
        )
    ])

Use:

block = dense_block(
    128
)

x = block(inputs)

๐Ÿง  Functional API and CNNs

The Functional API becomes particularly valuable for Computer Vision.

For example:

Input Image
     โ†“
Convolution
     โ†“
Pooling
     โ†“
Branch
 โ”Œโ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”
 โ†“       โ†“
CNN     CNN
 โ””โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”˜
     โ†“
 Merge
     โ†“
Classifier

This allows architectures beyond simple sequential CNN stacks.


๐Ÿง  Functional API and ResNet

Residual networks rely heavily on skip connections.

Conceptually:

flowchart TD

    INPUT["Input"]

    CONV1["Convolution"]

    CONV2["Convolution"]

    ADD["Add"]

    RELU["ReLU"]

    INPUT --> CONV1
    CONV1 --> CONV2
    CONV2 --> ADD

    INPUT --> ADD

    ADD --> RELU

The Functional API is naturally suited to this type of architecture.

ResNet is covered in detail in:

22. ResNet, Residual Connections and TorchVision


๐Ÿง  Functional API and Siamese Networks

Siamese networks often process two inputs through shared weights.

flowchart TD

    INPUT1["Input A"]

    INPUT2["Input B"]

    SHARED["Shared Encoder"]

    INPUT1 --> SHARED
    INPUT2 --> SHARED

    SHARED --> EMB1["Embedding A"]
    SHARED --> EMB2["Embedding B"]

    EMB1 --> DIST["Distance / Similarity"]

    EMB2 --> DIST

The Functional API is a natural fit because the same encoder can be reused.


๐Ÿง  Functional API and Attention

Attention architectures commonly contain:

Query
Key
Value

and multiple computation paths.

Functional graphs can represent such architectures more naturally than simple sequential stacks.

This becomes increasingly important when building:

  • Attention models
  • Transformers
  • Encoder-decoder systems
  • Multi-head architectures

๐Ÿง  Functional API Model Visualization

Keras can visualize the architecture.

tf.keras.utils.plot_model(
    model,
    show_shapes=True,
    show_layer_names=True
)

For complex architectures, this is extremely useful for verifying:

Input Shapes
Output Shapes
Connections
Branches
Layer Names

๐Ÿง  Model Summary

model.summary()

For a Functional model, the summary can reveal:

Layer
Output Shape
Parameters
Connections

This helps identify:

  • Unexpected parameter growth
  • Shape mismatches
  • Incorrect architecture
  • Large layers

๐Ÿ” Inspecting the Graph

Functional models expose:

model.inputs
model.outputs
model.layers

Example:

for layer in model.layers:

    print(
        layer.name,
        layer.output.shape
    )

๐Ÿง  Naming Layers

Meaningful layer names improve observability.

Instead of:

tf.keras.layers.Dense(128)

use:

tf.keras.layers.Dense(
    128,
    activation="relu",
    name="feature_projection"
)

This becomes especially useful in:

  • Debugging
  • Model visualization
  • Monitoring
  • Transfer Learning
  • Model inspection

๐Ÿข Production Architecture

A production Keras model should ideally have clear boundaries between:

Input Contract
      โ†“
Preprocessing
      โ†“
Feature Extraction
      โ†“
Task-Specific Head
      โ†“
Output Contract

Example:

flowchart LR

    INPUT["Input Contract"]

    PRE["Preprocessing"]

    FEATURES["Feature Extractor"]

    HEAD["Prediction Head"]

    OUTPUT["Output Contract"]

    INPUT --> PRE
    PRE --> FEATURES
    FEATURES --> HEAD
    HEAD --> OUTPUT

๐Ÿง  Functional API as Architecture-as-Code

The Functional API allows the model topology to be expressed directly in Python.

For example:

inputs = tf.keras.Input(
    shape=(128,)
)

x = layer_a(inputs)

x1 = layer_b(x)

x2 = layer_c(x)

merged = tf.keras.layers.Add()(
    [x1, x2]
)

outputs = layer_d(
    merged
)

model = tf.keras.Model(
    inputs,
    outputs
)

The code itself reflects the architecture:

Input
  โ†“
Layer A
  โ†“
 โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”
 โ†“             โ†“
Layer B       Layer C
 โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜
        โ†“
       Add
        โ†“
     Layer D
        โ†“
      Output

๐Ÿง  Sequential โ†’ Functional Migration

A Sequential model:

model = tf.keras.Sequential([

    tf.keras.Input(
        shape=(784,)
    ),

    tf.keras.layers.Dense(
        128,
        activation="relu"
    ),

    tf.keras.layers.Dense(
        10,
        activation="softmax"
    )
])

can be expressed using Functional API:

inputs = tf.keras.Input(
    shape=(784,)
)

x = tf.keras.layers.Dense(
    128,
    activation="relu"
)(inputs)

outputs = tf.keras.layers.Dense(
    10,
    activation="softmax"
)(x)

model = tf.keras.Model(
    inputs,
    outputs
)

The underlying neural network is equivalent.

The difference is the amount of architectural control.


๐Ÿง  When Should You Use Sequential?

Use Sequential when:

One Input
+
One Output
+
Linear Stack
+
No Branching
+
No Layer Sharing
+
No Skip Connections

Example:

Input
 โ†“
Dense
 โ†“
Dropout
 โ†“
Dense
 โ†“
Output

๐Ÿง  When Should You Use Functional API?

Use Functional API when you need:

Multiple Inputs
Multiple Outputs
Branches
Merges
Skip Connections
Shared Layers
Complex Graphs
Reusable Components

Examples:

ResNet
Siamese Networks
Multi-Task Models
Encoder-Decoder Models
Complex CNNs
Attention Networks
Transformers

๐Ÿง  When Should You Use Model Subclassing?

Model subclassing becomes useful when:

  • The architecture is highly dynamic
  • Forward behavior requires custom control flow
  • You need custom training behavior
  • The computation graph cannot be conveniently expressed using standard Functional patterns

Example:

class MyModel(
    tf.keras.Model
):

    def __init__(self):

        super().__init__()

        self.dense = tf.keras.layers.Dense(
            128,
            activation="relu"
        )

        self.output_layer = tf.keras.layers.Dense(
            10
        )

    def call(
        self,
        inputs
    ):

        x = self.dense(
            inputs
        )

        return self.output_layer(
            x
        )

Model subclassing is covered further in:

15. Custom Layers, Models and Training Loops


๐Ÿงญ Model API Decision Guide

flowchart TD

    START["Choose Keras Model API"]

    START --> SIMPLE{"Simple Linear Stack?"}

    SIMPLE -->|Yes| SEQ["Sequential API"]

    SIMPLE -->|No| COMPLEX{"Complex Graph?"}

    COMPLEX -->|Yes| FUNC["Functional API"]

    COMPLEX -->|No| CUSTOM{"Custom Dynamic Behavior?"}

    CUSTOM -->|Yes| SUB["Model Subclassing"]

    CUSTOM -->|No| FUNC

๐Ÿงช Practical Example โ€” Multi-Input Enterprise Model

Imagine a financial risk model receiving:

Customer Profile
+
Transaction Features
+
Behavioral Features

Architecture:

flowchart TD

    PROFILE["Customer Profile"]

    TRANS["Transaction Features"]

    BEHAVIOR["Behavioral Features"]

    PROFILE --> PENC["Profile Encoder"]

    TRANS --> TENC["Transaction Encoder"]

    BEHAVIOR --> BENC["Behavior Encoder"]

    PENC --> MERGE["Feature Fusion"]

    TENC --> MERGE

    BENC --> MERGE

    MERGE --> DENSE["Dense Layers"]

    DENSE --> RISK["Risk Score"]

This is an excellent use case for the Functional API.


๐Ÿงช Implementation

import tensorflow as tf


profile = tf.keras.Input(
    shape=(20,),
    name="profile"
)

transactions = tf.keras.Input(
    shape=(50,),
    name="transactions"
)

behavior = tf.keras.Input(
    shape=(30,),
    name="behavior"
)


profile_features = tf.keras.layers.Dense(
    64,
    activation="relu",
    name="profile_encoder"
)(profile)


transaction_features = tf.keras.layers.Dense(
    128,
    activation="relu",
    name="transaction_encoder"
)(transactions)


behavior_features = tf.keras.layers.Dense(
    64,
    activation="relu",
    name="behavior_encoder"
)(behavior)


features = tf.keras.layers.Concatenate(
    name="feature_fusion"
)([
    profile_features,
    transaction_features,
    behavior_features
])


x = tf.keras.layers.Dense(
    128,
    activation="relu",
    name="risk_features"
)(features)


x = tf.keras.layers.Dropout(
    0.2,
    name="regularization"
)(x)


output = tf.keras.layers.Dense(
    1,
    activation="sigmoid",
    name="risk_score"
)(x)


model = tf.keras.Model(
    inputs=[
        profile,
        transactions,
        behavior
    ],
    outputs=output,
    name="enterprise_risk_model"
)

๐Ÿง  Why This Architecture Is Production-Friendly

Each input has a dedicated representation:

Profile
   โ†“
Profile Encoder

Transactions
   โ†“
Transaction Encoder

Behavior
   โ†“
Behavior Encoder

These representations are then combined:

Feature Fusion
      โ†“
Shared Representation
      โ†“
Prediction Head

This structure makes it easier to:

  • Modify individual branches
  • Test components
  • Reuse encoders
  • Monitor inputs
  • Extend the architecture

โš  Common Mistakes

Avoid these mistakes:

  • Using Sequential for a graph-based architecture
  • Using Functional API when Sequential would be simpler without a reason
  • Forgetting that Functional API tensors are symbolic during model construction
  • Connecting tensors with incompatible shapes
  • Using Add when tensor shapes are incompatible
  • Confusing Add with Concatenate
  • Accidentally creating separate layers instead of sharing the same layer instance
  • Creating unnecessarily complicated model graphs
  • Not naming important inputs and outputs
  • Ignoring model visualization
  • Ignoring parameter counts
  • Building giant monolithic Functional models
  • Mixing preprocessing logic inconsistently between training and inference
  • Forgetting to validate multi-input data contracts
  • Using different preprocessing logic for different deployment paths
  • Adding branches without a clear modeling reason

๐Ÿง  Interview Questions

Beginner

1. What is the Sequential API?

The Sequential API is a Keras model-building approach for creating a simple linear stack of layers.

2. What is the Functional API?

The Functional API allows developers to construct arbitrary directed computation graphs by explicitly connecting inputs, layers, and outputs.

3. What is the main difference between Sequential and Functional API?

Sequential represents a simple linear stack, while Functional API supports complex graph structures such as branching, merging, multiple inputs, multiple outputs, shared layers, and skip connections.

4. Can Sequential models have multiple inputs?

Not naturally. Models with multiple inputs should generally use the Functional API.

5. Can Functional API build simple models?

Yes. A simple Sequential model can also be represented using the Functional API.


Intermediate

6. What is a symbolic tensor?

A symbolic tensor represents a node or intermediate value in the Keras computation graph rather than an actual batch of numerical data during model construction.

7. What is a skip connection?

A skip connection allows information to bypass one or more layers and later be combined with the transformed representation.

8. Why is the Functional API useful for ResNet?

ResNet uses residual/skip connections, which require graph structures that are not naturally represented by a simple sequential stack.

9. What is layer sharing?

Layer sharing means using the same layer instance, and therefore the same learned weights, on multiple inputs or paths.

10. What is a multi-input model?

A model that receives more than one independent input tensor.

11. What is a multi-output model?

A model that produces multiple outputs, potentially representing different tasks.

12. What is multi-task learning?

Multi-task learning trains a shared representation to solve multiple related tasks, often using separate task-specific output heads.


Advanced

13. Why would you use the Functional API instead of subclassing?

When the architecture can be clearly expressed as a computation graph, the Functional API provides strong graph visibility, model inspection, visualization, and serialization while remaining flexible.

14. Why might model subclassing be necessary?

Highly dynamic computation, custom control flow, or specialized training behavior may be easier to implement using subclassing.

15. What is the difference between Add and Concatenate?

Add performs element-wise addition and requires compatible shapes. Concatenate joins tensors along a specified dimension and generally increases the feature dimension.

16. How does layer sharing work?

The same layer object is applied to multiple inputs:

shared_layer = Dense(64)

x1 = shared_layer(input1)
x2 = shared_layer(input2)

Both paths use the same learned weights.

17. How would you design a multi-input enterprise model?

Separate each input into an appropriate encoder, transform each representation, fuse the representations, and pass the fused representation into shared prediction layers.

18. Why is model visualization important?

It helps verify:

Connections
Shapes
Branches
Skip Paths
Parameter Growth

and can expose architectural mistakes before training becomes expensive.


๐Ÿงช Practical Exercises

Exercise 1 โ€” Sequential Classifier

Build:

Input
 โ†“
Dense 128
 โ†“
ReLU
 โ†“
Dense 64
 โ†“
ReLU
 โ†“
Dense 10
 โ†“
Softmax

using Sequential.


Exercise 2 โ€” Convert Sequential to Functional

Implement the same architecture using the Functional API.

Compare:

Code
Model Summary
Parameter Count
Predictions

Exercise 3 โ€” Branching Network

Create:

Input
 โ”œโ”€โ”€ Dense 128
 โ”‚
 โ””โ”€โ”€ Dense 64
       โ†“
   Concatenate
       โ†“
   Dense
       โ†“
   Output

Exercise 4 โ€” Skip Connection

Implement:

Input
   โ”‚
   โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ†“               โ”‚
Dense              โ”‚
   โ†“               โ”‚
Dense              โ”‚
   โ†“               โ”‚
   โ””โ”€โ”€โ”€โ”€ Add โ—„โ”€โ”€โ”€โ”€โ”€โ”˜
          โ†“
        Output

Exercise 5 โ€” Multi-Input Model

Build a model accepting:

Profile Features
Transaction Features
Behavior Features

Create separate encoders and combine them using Concatenate.


Exercise 6 โ€” Multi-Output Model

Create one shared network with:

Classification Head
+
Regression Head

Train it using different losses for each output.


Exercise 7 โ€” Shared Encoder

Build a Siamese-style architecture:

Input A โ”€โ”€โ”
          โ”œโ”€โ”€ Shared Encoder
Input B โ”€โ”€โ”˜

Produce two embeddings and calculate a similarity score.


๐Ÿ“Œ Key Takeaways

  • Keras provides multiple model-construction approaches.
  • Sequential is ideal for simple linear stacks.
  • Functional API is designed for arbitrary computation graphs.
  • Functional API supports multiple inputs and outputs.
  • Functional API supports branching and merging.
  • Functional API supports shared layers.
  • Skip connections are naturally implemented using Functional API.
  • Residual networks rely heavily on this capability.
  • Functional API uses symbolic tensors during model construction.
  • tf.keras.Model(inputs, outputs) creates a Functional model.
  • Add performs element-wise addition.
  • Concatenate joins tensors along a dimension.
  • Multi-output models can support multi-task learning.
  • Different outputs can use different losses and metrics.
  • Loss weights can balance multiple tasks.
  • Keras models can be composed and reused as layers.
  • Meaningful layer names improve model inspection and maintainability.
  • Model visualization is especially valuable for complex architectures.
  • Sequential should not be forced onto architectures that require graph connectivity.
  • Functional API is often the right abstraction for production-grade complex neural networks.
  • Model subclassing is useful when computation or behavior requires greater customization.
  • Good architecture design should balance flexibility, readability, maintainability, and operational requirements.

๐Ÿ“š Further Reading

Continue with:

The next chapter moves from standard Keras model construction into custom layers, custom models, and custom training loops, giving you much deeper control over the training process.


โžก๏ธ Next Chapter

15. Custom Layers, Models and Training Loops


Enterprise AI Engineering Handbook
Building Production-Grade Enterprise AI Systems โ€” One Chapter at a Time.