Keras
CTC `Loss`
Machine Learning
Deep Learning
TensorFlow

Keras CTC `Loss` input

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

CTC loss is used when a model predicts a sequence over time but the exact alignment between time steps and target labels is unknown. The idea is conceptually elegant, but most implementation bugs come from shape bookkeeping rather than from the mathematics of CTC itself. In Keras, the essential task is to pass predictions, target labels, input lengths, and label lengths in the exact form the loss expects.

The Four Inputs You Must Get Right

A typical Keras or TensorFlow CTC setup needs four pieces of data for each batch:

  • 'y_pred: model outputs across time steps'
  • 'y_true: padded target label ids'
  • 'input_length: number of valid model time steps per example'
  • 'label_length: number of valid target labels per example'

The batch dimension must align across all four tensors. If one of them is off by even one axis, training fails or silently learns the wrong thing.

Common Shape Convention in Keras

With tf.keras.backend.ctc_batch_cost, the usual shape contract is:

  • 'y_pred: (batch_size, time_steps, num_classes)'
  • 'y_true: (batch_size, max_label_length)'
  • 'input_length: (batch_size, 1)'
  • 'label_length: (batch_size, 1)'

A minimal working example looks like this:

python
1import tensorflow as tf
2from tensorflow.keras import backend as K
3
4batch_size = 2
5time_steps = 6
6num_classes = 5   # includes the CTC blank class
7max_label_length = 3
8
9y_pred = tf.random.uniform((batch_size, time_steps, num_classes))
10y_pred = tf.nn.softmax(y_pred, axis=-1)
11
12y_true = tf.constant([
13    [1, 2, 0],
14    [2, 3, 4],
15], dtype=tf.int32)
16
17input_length = tf.constant([[6], [6]], dtype=tf.int32)
18label_length = tf.constant([[2], [3]], dtype=tf.int32)
19
20loss = K.ctc_batch_cost(y_true, y_pred, input_length, label_length)
21print(loss)

The label tensor can be padded, but label_length tells CTC which positions are real and which are just filler.

The Blank Class Is Required

CTC needs one extra output class for the blank symbol. If your vocabulary contains N actual labels, the network output must usually contain N + 1 classes.

For example, if you have digits 0 through 9, the output layer needs 11 units when using a CTC blank:

python
1inputs = tf.keras.Input(shape=(None, 16))
2x = tf.keras.layers.Bidirectional(
3    tf.keras.layers.LSTM(32, return_sequences=True)
4)(inputs)
5outputs = tf.keras.layers.Dense(11, activation="softmax")(x)
6model = tf.keras.Model(inputs, outputs)

If the blank class is missing, the loss and the decoder no longer match the CTC formulation.

input_length Is About Model Time Steps

A very common mistake is setting input_length equal to the target length. That is wrong. input_length describes how many valid time steps the model produced for each item after all convolution, pooling, or subsampling layers.

If a CNN or pooling stage shrinks the time dimension before the recurrent stack, you must compute the reduced length correctly. Otherwise, CTC tries to align labels against a time axis that does not actually exist.

Padding Is Allowed, but Lengths Must Be Honest

Target labels are usually padded to a uniform width so they fit in a batch tensor. That is normal. The important part is that label_length gives the true number of labels before padding.

For example, if two samples are [1, 2] and [2, 3, 4], the padded batch might be:

text
[[1, 2, 0],
 [2, 3, 4]]

but the true label lengths are [2, 3]. If you lie about those lengths, the loss interprets padding as real labels and the training signal becomes invalid.

Debug One Batch Before Training

CTC is much easier to debug on one batch than during a long training run. Print the shapes and lengths early.

python
1print("y_pred:", y_pred.shape)
2print("y_true:", y_true.shape)
3print("input_length:", input_length.numpy().tolist())
4print("label_length:", label_length.numpy().tolist())

That small check often catches the real problem immediately: swapped axes, wrong output width, missing blank class, or an incorrect length calculation after downsampling.

Common Pitfalls

The most common mistake is confusing input_length with label_length. They measure completely different things.

Another mistake is forgetting the extra blank class in the output layer. That makes the entire CTC setup inconsistent.

Developers also pad labels but fail to supply the correct label_length, so the loss interprets padded tokens as part of the target sequence.

Summary

  • Keras CTC loss needs predictions, labels, input lengths, and label lengths.
  • 'y_pred is typically shaped (batch_size, time_steps, num_classes).'
  • The output classes must include the CTC blank token.
  • 'input_length describes model time steps, not target length.'
  • Most CTC failures are shape or length bookkeeping errors, not problems with the loss formula itself.

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.