Sequence Models before Transformers
Recurrent neural networks process a sequence by carrying state from one step to the next. LSTMs and GRUs improve gradient flow and gating, but recurrence still constrains parallel training. This history motivates transformers; it does not make recurrent models obsolete for every small, streaming, or stateful workload.
Prerequisites
Complete Word and Sentence Embeddings and know matrix multiplication and tanh.
Learning goals
- trace an input sequence through a recurrent hidden state.
- observe that reversing tokens changes the final representation.
- compare recurrence's streaming state with attention's parallel token interactions.
Worked example
This two-dimensional recurrence uses fixed matrices. Each hidden state depends on the previous one, so time steps cannot all be evaluated independently.
import torch
sequence = torch.tensor([[1.0, 0.0], [0.0, 1.0], [1.0, 1.0]])
input_weight = torch.eye(2)
recurrent_weight = 0.5 * torch.eye(2)
def encode(tokens):
hidden = torch.zeros(2)
states = []
for token in tokens:
hidden = torch.tanh(token @ input_weight + hidden @ recurrent_weight)
states.append(hidden)
return torch.stack(states)
forward = encode(sequence)
reversed_states = encode(sequence.flip(0))
rounded = [[round(value, 3) for value in row] for row in forward.tolist()]
print(f"input={tuple(sequence.shape)} states={tuple(forward.shape)}")
print(f"forward_states={rounded}")
print(f"forward_final={[round(v, 3) for v in forward[-1].tolist()]}")
print(f"reversed_final={[round(v, 3) for v in reversed_states[-1].tolist()]}")
Output:
input=(3, 2) states=(3, 2)
forward_states=[[0.762, 0.0], [0.363, 0.762], [0.828, 0.881]]
forward_final=[0.828, 0.881]
reversed_final=[0.828, 0.414]
The final states differ because order changes the computation. In an RNN, padding is commonly handled with packed sequences or length-aware selection; reading the last padded position as if it were content is a bug.
Common mistakes
- Describing a recurrent hidden state as perfect memory; information can decay or be overwritten.
- Ignoring exploding or vanishing gradients merely because an LSTM or GRU is used.
- Comparing architectures without equal data, parameter, latency, and tuning budgets.
- Claiming transformers dominate low-latency streaming or small-data workloads categorically.
- Leaking future tokens into a task that requires causal, online prediction.
Practice
Set recurrent_weight to zero and explain why the final state then ignores earlier tokens. Next, describe the state and latency constraints of an on-device streaming classifier.
Next steps
Attention Step by Step replaces a single recurrent bottleneck with weighted access to token values.
Primary sources
Checked 2026-10-16: PyTorch RNN API, PyTorch GRU API, LSTM paper, and GRU encoder-decoder paper.