Bahdanau Attention › Computational Considerations
Language AI HandbookAll chapters Bahdanau Attention › Computational ConsiderationsOne section · Sections in this chapter7 / 11 ↓
Reading tools Definitions & inline links
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
Bahdanau attention introduces additional computation compared to a standard encoder-decoder. Let's analyze the complexity by examining each operation required at every decoder step.
For each decoder step, we must perform the following operations:
- Project the decoder state: Computing requires operations (matrix-vector multiplication)
- Project all encoder states: Computing for all requires operations
- Compute scores: The addition, tanh, and dot product with require operations
- Apply softmax: Normalizing scores requires operations
- Compute weighted sum: Computing requires operations
where:
- : source sequence length
- : target sequence length
- : decoder hidden dimension
- : encoder hidden dimension
- : alignment dimension
The dominant term is the encoder projection, which is per decoder step. Over an entire output sequence of length , the total complexity becomes . This quadratic dependence on sequence lengths () becomes significant for long sequences.

For typical machine translation tasks with sequences of 20-50 tokens, this overhead is manageable. However, for very long sequences (documents, conversations), the quadratic scaling becomes problematic. This motivated later work on efficient attention variants, which we'll explore in subsequent chapters.
A practical optimization addresses the redundant computation in step 2. Notice that the encoder projections depend only on the encoder outputs, which remain constant throughout decoding. By precomputing these projections once after encoding and caching them, we avoid repeating this computation at every decoder step:
class EfficientBahdanauAttention(nn.Module):
"""Bahdanau attention with precomputed encoder projections."""
def __init__(self, encoder_dim, decoder_dim, attention_dim):
super().__init__()
self.W_a = nn.Linear(decoder_dim, attention_dim, bias=False)
self.U_a = nn.Linear(encoder_dim, attention_dim, bias=False)
self.v_a = nn.Linear(attention_dim, 1, bias=False)
# Cache for precomputed encoder projections
self.encoder_proj_cache = None
def precompute_encoder_proj(self, encoder_outputs):
"""Precompute encoder projections once per sequence."""
self.encoder_proj_cache = self.U_a(encoder_outputs)
def forward(self, decoder_state, encoder_outputs):
"""Compute attention using cached encoder projections."""
decoder_proj = self.W_a(decoder_state)
# Use cached projection if available
if self.encoder_proj_cache is not None:
encoder_proj = self.encoder_proj_cache
else:
encoder_proj = self.U_a(encoder_outputs)
combined = torch.tanh(decoder_proj.unsqueeze(1) + encoder_proj)
scores = self.v_a(combined).squeeze(-1)
attention_weights = F.softmax(scores, dim=-1)
context = torch.bmm(
attention_weights.unsqueeze(1), encoder_outputs
).squeeze(1)
return context, attention_weights