Keras
TimeDistributed
multiple inputs
neural networks
deep learning

Keras TimeDistributed with multiple Inputs in different shapes

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

Introduction

TimeDistributed is useful when you want to apply the same layer to each timestep of a sequence. The pattern becomes more interesting with multiple inputs that have different feature shapes, such as frame tensors plus tabular sensor vectors. The key is aligning sequence length and building separate per-input encoders before temporal fusion.

What TimeDistributed Does

TimeDistributed(layer) wraps a layer so it runs independently on each timestep. If input shape is (batch, time, features), wrapped layers process each time slice while sharing weights.

Common use cases:

  • per-frame image feature extraction
  • per-step tabular embedding
  • per-token dense transformations before recurrent modeling

The wrapper does not fuse modalities by itself. You still design fusion explicitly.

Multi-Input Sequence Setup

Assume two inputs:

  • video-like sequence shape (batch, time, height, width, channels)
  • sensor sequence shape (batch, time, sensor_features)

Build one branch per input, then merge.

python
1import tensorflow as tf
2from tensorflow.keras import layers, Model, Input
3
4# branch 1: frames
5frames_in = Input(shape=(10, 32, 32, 3), name="frames")
6x1 = layers.TimeDistributed(layers.Conv2D(16, 3, activation="relu"))(frames_in)
7x1 = layers.TimeDistributed(layers.MaxPooling2D())(x1)
8x1 = layers.TimeDistributed(layers.Flatten())(x1)
9x1 = layers.TimeDistributed(layers.Dense(64, activation="relu"))(x1)
10
11# branch 2: sensors
12sensor_in = Input(shape=(10, 8), name="sensors")
13x2 = layers.TimeDistributed(layers.Dense(32, activation="relu"))(sensor_in)
14
15# fuse per timestep
16fused = layers.Concatenate(axis=-1)([x1, x2])
17
18# temporal modeling
19h = layers.Bidirectional(layers.LSTM(64))(fused)
20out = layers.Dense(1, activation="sigmoid")(h)
21
22model = Model(inputs=[frames_in, sensor_in], outputs=out)
23model.compile(optimizer="adam", loss="binary_crossentropy", metrics=["accuracy"])
24model.summary()

This architecture keeps branch-specific feature extraction clear.

Different Feature Shapes Are Fine, Time Must Align

Branches can have different feature dimensions, but sequence length must be compatible at fusion time if you concatenate across feature axis.

If one branch has 10 timesteps and another has 12, direct concatenate fails. You need resampling, padding, or model design where fusion happens after independent temporal aggregation.

Handling Variable Length Sequences

Use masking or ragged-compatible flow when sequence lengths vary.

python
1seq_in = Input(shape=(None, 8))
2x = layers.Masking(mask_value=0.0)(seq_in)
3x = layers.TimeDistributed(layers.Dense(16, activation="relu"))(x)
4x = layers.LSTM(32)(x)

For multi-input models, apply consistent padding strategy so masks represent the same logical timesteps.

Training Input Format

Feed data as list or dictionary keyed by input names.

python
1import numpy as np
2
3x_frames = np.random.rand(64, 10, 32, 32, 3).astype("float32")
4x_sensors = np.random.rand(64, 10, 8).astype("float32")
5y = np.random.randint(0, 2, size=(64, 1)).astype("float32")
6
7model.fit(
8    {"frames": x_frames, "sensors": x_sensors},
9    y,
10    batch_size=8,
11    epochs=2
12)

Name-based feeding helps avoid branch-order mistakes.

Debugging Shape Errors

When shape errors occur, inspect each branch output before fusion.

Useful tactics:

  • print model.summary()
  • create sub-models for branch outputs
  • verify batch and time axes in actual arrays

Most TimeDistributed errors come from swapped axis order or mismatch between expected and actual timestep count.

Performance Considerations

TimeDistributed can increase compute significantly with high-resolution sequences. Consider:

  • reducing per-frame resolution
  • using lightweight convolutions
  • precomputing frame embeddings offline

For long sequences, temporal models may become bottlenecks. Pooling strategies or transformer variants can help depending on workload.

Common Pitfalls

  • Assuming TimeDistributed automatically aligns branches with different timestep counts.
  • Mixing axis order and feeding arrays as (time, batch, features) by mistake.
  • Concatenating branches before ensuring compatible per-step dimensions.
  • Ignoring masking and padding consistency for variable-length sequences.
  • Building heavy per-timestep encoders that exceed memory limits.

Summary

  • 'TimeDistributed applies the same layer independently across timesteps.'
  • Multiple input branches can have different feature shapes but must align for fusion.
  • Build branch-specific encoders first, then merge and model temporal context.
  • Validate axis ordering and timestep lengths early to avoid shape errors.
  • Use masking, padding, and lightweight encoders to keep training stable and efficient.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

All Rights Reserved.