TensorFlow
Keras
Seq2Seq
Bahdanau Attention
Functional API

How to use tfa.seq2seq.BahdanauAttention with tf.keras functional API?

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

tfa.seq2seq.BahdanauAttention belongs to the TensorFlow Addons seq2seq stack, which was designed around decoder cells and wrapper state, not around a simple plug-and-play Functional API layer graph. That is why it often feels awkward inside a pure tf.keras.Model(inputs, outputs) design.

Why It Feels Different from Regular Keras Layers

In the Functional API, most layers behave like pure tensor transforms:

text
output = layer(input)

The seq2seq attention classes in TensorFlow Addons are different. They are part of a decoder workflow involving:

  • encoder memory
  • decoder cell state
  • an attention wrapper
  • sequence lengths and masking

So the practical answer is often one of these:

  1. use a subclassed model with tfa.seq2seq.AttentionWrapper
  2. use a Keras-native layer such as tf.keras.layers.AdditiveAttention

For many modern projects, option two is simpler.

Keras-Native Additive Attention Example

Bahdanau attention is additive attention. If your goal is attention over encoder outputs inside a Functional API model, tf.keras.layers.AdditiveAttention is usually the clean fit.

python
1import tensorflow as tf
2
3encoder_inputs = tf.keras.Input(shape=(None,), dtype="int32", name="encoder_tokens")
4decoder_inputs = tf.keras.Input(shape=(None,), dtype="int32", name="decoder_tokens")
5
6embedding = tf.keras.layers.Embedding(input_dim=5000, output_dim=128)
7
8encoder_emb = embedding(encoder_inputs)
9decoder_emb = embedding(decoder_inputs)
10
11encoder_outputs = tf.keras.layers.Bidirectional(
12    tf.keras.layers.LSTM(64, return_sequences=True)
13)(encoder_emb)
14
15decoder_outputs = tf.keras.layers.LSTM(128, return_sequences=True)(decoder_emb)
16
17context = tf.keras.layers.AdditiveAttention()([decoder_outputs, encoder_outputs])
18combined = tf.keras.layers.Concatenate()([decoder_outputs, context])
19logits = tf.keras.layers.Dense(5000, activation="softmax")(combined)
20
21model = tf.keras.Model([encoder_inputs, decoder_inputs], logits)
22model.summary()

This is a true Functional API model and captures the core Bahdanau-style idea without using the older Addons seq2seq wrapper stack.

If You Must Use tfa.seq2seq.BahdanauAttention

Then you generally step outside the pure Functional API style and build a decoder around an RNN cell:

python
1import tensorflow as tf
2import tensorflow_addons as tfa
3
4memory = tf.random.normal((32, 20, 128))
5memory_lengths = tf.constant([20] * 32)
6
7attention = tfa.seq2seq.BahdanauAttention(
8    units=128,
9    memory=memory,
10    memory_sequence_length=memory_lengths,
11)
12
13cell = tf.keras.layers.LSTMCell(128)
14attn_cell = tfa.seq2seq.AttentionWrapper(cell, attention_layer_size=128,
15                                         attention_mechanism=attention)
16
17decoder_inputs = tf.random.normal((32, 10, 64))
18sampler = tfa.seq2seq.TrainingSampler()
19decoder = tfa.seq2seq.BasicDecoder(attn_cell, sampler)
20
21initial_state = attn_cell.get_initial_state(batch_size=32, dtype=tf.float32)
22outputs, _, _ = decoder(decoder_inputs, initial_state=initial_state,
23                        sequence_length=[10] * 32)

That is valid, but it is not the same ergonomics as building with ordinary Keras layers.

What the Attention Layer Is Really Doing

At each decoder step, additive attention scores the current decoder state against every encoder output, turns those scores into weights, and builds a context vector as a weighted combination of encoder states. That is why encoder sequence output, decoder state shape, and masking all have to line up cleanly.

Important Current Caveat

TensorFlow Addons is deprecated, and newer TensorFlow work increasingly favors Keras-native layers or task-specific libraries. So if you are starting fresh, it is worth asking whether you actually need tfa.seq2seq.BahdanauAttention rather than a modern Keras attention layer.

That is not just style advice. It reduces compatibility headaches and makes the model easier to maintain.

Common Pitfalls

The most common mistake is trying to drop tfa.seq2seq.BahdanauAttention into a Functional API graph as if it were a normal stateless layer. It is tightly coupled to decoder state and wrapped cells.

Another mistake is forgetting shape conventions. Attention expects encoder memory with time steps and feature dimensions, plus matching decoder query dimensions. Silent shape mismatch can lead to confusing runtime errors.

A third issue is using an incompatible TensorFlow and TensorFlow Addons version pair. Addons has always been sensitive to version compatibility, so verify that combination before debugging model code for hours.

Summary

  • 'tfa.seq2seq.BahdanauAttention is part of the Addons seq2seq decoder stack, not a simple standalone Keras layer.'
  • For pure Functional API models, tf.keras.layers.AdditiveAttention is usually the better fit.
  • If you need Addons seq2seq, use AttentionWrapper and a decoder-oriented design.
  • Check TensorFlow and Addons version compatibility early.
  • For new projects, prefer Keras-native attention unless you specifically need the older seq2seq abstractions.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the 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.