10.2 RNN, LSTM, and BPTT: State Can Carry Context, But Also Leak
When a decision at the tenth second of a task depends on the preceding nine seconds, isolated-row features may be insufficient. An RNN reuses the same update rule over time and retains a finite-dimensional state.
The strength and risk of RNNs both stem from the same source: the hidden state. It can summarize the past, but it can also carry forward outdated information (like the identity of a previous user, information from the reverse direction, or padding artifacts) into contexts where they shouldn’t belong.
Learning Objectives
- Write the vanilla RNN recurrence and corresponding tensor shapes;
- Understand BPTT and gradient vanishing/explosion from an unrolled computation graph;
- Derive the LSTM cell dynamics, hidden state, and gate computations;
- Properly handle padding, sequence length, bidirectional processing, and streaming state;
- Distinguish between sequence classification, token prediction, and text generation.
1. RNN Shares Parameters Across Time
For input
The same weights
h_t represents a compressed state and does not guarantee preservation of all historical information. The retained content depends on hidden size, training objective, and gradient flow.
2. Shape Contract
Common at batch_first=True:
input: [B, T, input_size]
output: [B, T, directions * hidden_size]
h_n: [layers * directions, B, hidden_size]batch_first does not change the layout of the hidden state. In multi-layer/bidirectional setups, directly accessing h_n[-1] might only retrieve the last layer in a specific direction; you must reshape according to [layers, directions, B, H] and explicitly select or concatenate.
Embedding inputs are typically long token IDs [B,T], outputs [B,T,E]. Padding ID must match the vocabulary/embedding configuration.
3. BPTT is Backprop on an Unrolled Graph
After unrolling the recurrence
Here gradients are column vectors,
This product repeatedly multiplies the weight matrix
The longer the sequence length, the higher the activation memory and the backward computation cost. Truncated BPTT detaches the hidden state every few steps, performing backpropagation only within a finite window. This reduces computational cost and explicitly cuts off credit assignment across windows.
4. LSTM Provides a Controlled Addition Path to the Cell State
A common LSTM formulation:
The forget/input/output gates respectively regulate the retention of previous cell state, the candidate write-in, and the exposure of the output. The addition operation in the cell state allows certain gradients to flow more persistently through time, but gates can still saturate, meaning LSTM does not guarantee infinite memory or immunity to gradient vanishing problems.
GRUs merge portions of state and gates, reducing parameter count. The choice between them should be based on validation performance, latency, and data volume, not on a fixed notion that "LSTM is more advanced than RNN."
5. Padding Is Not Empty Information
Variable-length sequences are commonly padded to match the maximum batch length. If an RNN is run directly without special handling:
- Padding steps continue to update the hidden state;
output[:, -1]might correspond to padding tokens, not the final valid token;- If token loss is not masked, padding tokens are treated as training targets;
- Batch statistics or metrics may be skewed due to inclusion of padding.
The effective output can be gathered using lengths, or pack_padded_sequence can be used to skip over padding entirely. In PyTorch, when passing a lengths tensor to pack, it is typically moved to the CPU.
This runnable Python 3 / PyTorch 2.14 CPU example uses a unidirectional LSTM without projection, right-padded long token IDs, and positive integer lengths no larger than the padded width:
import torch
from torch import nn
from torch.nn.utils.rnn import pack_padded_sequence
class LSTMClassifier(nn.Module):
def __init__(self, vocab_size, embedding_dim, hidden_dim, classes, pad_id=0):
super().__init__()
self.embedding = nn.Embedding(
vocab_size,
embedding_dim,
padding_idx=pad_id,
)
self.lstm = nn.LSTM(
embedding_dim,
hidden_dim,
batch_first=True,
)
self.head = nn.Linear(hidden_dim, classes)
def forward(self, token_id, length):
embedded = self.embedding(token_id)
packed = pack_padded_sequence(
embedded,
length.cpu(),
batch_first=True,
enforce_sorted=False,
)
_, (h_n, _) = self.lstm(packed)
last_layer_hidden = h_n[-1]
return self.head(last_layer_hidden)
model = LSTMClassifier(vocab_size=20, embedding_dim=8, hidden_dim=12, classes=2)
model.eval()
tokens = torch.tensor([[1, 2, 3, 0], [4, 5, 0, 0]])
lengths = torch.tensor([3, 2])
changed_padding = tokens.clone()
changed_padding[0, 3] = 9
changed_padding[1, 2:] = 9
with torch.inference_mode():
logits = model(tokens, lengths)
assert logits.shape == (2, 2)
assert torch.allclose(logits, model(changed_padding, lengths))The assertions verify that changing values after each valid length does not change predictions. A multilayer unidirectional LSTM still uses h_n[-1] for its final layer; bidirectional output needs both final directions. With LSTM projection, hidden and cell dimensions differ and require a revised shape contract.
6. Bidirectional RNN Sees Into the Future
A bidirectional encoder processes the entire sequence both left-to-right and right-to-left, making it well-suited for offline tagging or classification tasks. However, it is unsuitable for causal online prediction or autoregressive generation, because the backward pass relies on future tokens.
Enabling bidirectional training during feature extraction inadvertently introduces temporal leakage. Before deploying, always verify whether the full sequence is available at the time of inference.
7. Many-to-one, Many-to-many, and Causal LM
- Sequence classification: the entire sequence outputs a single label;
- Token labeling: each valid position outputs a label;
- Forecasting: predicts future values based on past inputs;
- Autoregressive language models: predicts the probability distribution of the next token;
- Seq2seq: decoder state or outputs are conditioned on encoder states or outputs.
Loss placement differs. Supervision is applied only at the final step, resulting in longer information paths earlier in the sequence; step-wise supervision provides denser gradients but requires alignment with task labels.
Teacher forcing feeds the true previous token during decoder training, while during inference it feeds the model’s own output, creating an exposure mismatch. Techniques like scheduled sampling alter the objective, but no automatic solution exists.
8. The Boundary of Stateful Streaming
Stateful streaming RNNs can retain state across chunks, reducing redundant computations. However, it is essential to define when state should be reset:
- New users or new sessions;
- Device restarts or long idle periods;
- Changes in the streaming order within a batch;
- Model version updates;
- Backfill or out-of-order events.
When training truncated chunks, the state must be detached with detach() to prevent the computation graph from growing indefinitely. Detaching preserves state values and only cuts the gradient history; zeroing or reindexing state is a separate action required between independent sequences. The state cache itself is a stateful service component and requires TTL (time-to-live), consistency, privacy, and fault recovery mechanisms.
9. RNN's Sequential Cost and Alternatives
Each
Alternative approaches:
- 1D or dilated causal convolution: fully parallelizable with a fixed receptive field;
- Transformers: enable global content-based interactions, though dense attention memory may grow quadratically;
- State-space models: offer alternative long-sequence computation and state design paradigms;
- Feature aggregation combined with tabular models: may provide greater stability for small-data tasks.
RNNs still hold value in low-latency streaming scenarios, with limited state requirements, and for shorter sequence lengths.
10. Training and Evaluation
Records:
- Length distribution and length-bucket metrics;
- Padding fraction/pack throughput;
- Hidden/cell norm, gradient norm, and clip rate;
- State reset correctness;
- Causal cutoff and label maturity;
- Teacher-forced loss and free-running generation/forecast error.
Random row splitting may inadvertently place adjacent time windows from the same session into train and test sets. Splitting by entity, time, or horizon helps avoid highly overlapping window leakage.
Common Misconceptions
- RNNs remember the entire history: The hidden state is a finite compression of past inputs; training does not guarantee that target information is preserved.
- LSTMs solve gradient vanishing: They improve gradient flow, but do not provide a guarantee of infinite memory.
output[:, -1]is the final effective state: This is often not true when padding is present.- Bidirectional models are just stronger: In causal sequential tasks, they look into the future, which violates the causality assumption.
- Preserving state always improves continuity: Incorrect reuse of state across entities can lead to severe information leakage.
Exercise
- Expand the 4-step RNN computation graph and label the shared parameters.
- Verify the output shape of
batch_first=Trueagainsth_n. - Compare the effects of padding on
output[:,-1], length-based gather, and packed LSTM. - Implement truncated BPTT and observe the graph and memory differences before and after detach.
- Define state keys, reset logic, and TTL rules for a streaming multi-user service.
Summary
RNNs push historical information into a hidden state through shared recurrence, and BPTT computes gradients along the unrolled time diagram. LSTM gates and cells improve long-range dependencies, but whether the model truly respects deployment causality depends on factors like padding, bidirectionality, truncation, and state lifecycle.
The next lesson removes the bottleneck of single recurrence: attention allows each query to directly read information from a set of keys and values, while introducing new challenges such as masking, quadratic matrix complexity, and interpretability boundaries.