Recurrent Neural Networks (RNN)

LSTMSectionLong short-term memory networks — the recurrent architecture that made sequence learning practical before…

A recurrent neural network processes a sequence one element at a time while carrying a hidden state forward, so the output at step t can depend on everything seen before it. The same weights are applied at every step, which lets one model handle sequences of any length. RNNs — and especially their gated forms, LSTM and GRU — were the standard architecture for language, speech and time series until Transformers took over most large-scale sequence work around 2018. They remain useful for streaming, low-latency and small-data problems, and the core ideas reappear in modern linear-recurrent and state-space models.

This page is the hub for the recurrent family under Deep Learning (DL).

Vanilla RNN

The simple Elman recurrence h = tanh(Wx + Uh + b), backpropagation through time, and why gradients vanish.

LSTM (Long Short-Term Memory)

A gated cell state with input, forget and output gates that preserves information over long spans.

GRU (Gated Recurrent Unit)

A lighter two-gate alternative to LSTM that merges cell and hidden state; often equally accurate.

Bidirectional LSTM

Forward and backward passes concatenated so every position sees both past and future context.

Stacked LSTM

Multiple recurrent layers on top of each other for hierarchical sequence features.

Transformers

The attention-based successor that processes all positions in parallel.


1. Recurrence and Unrolling

Folded vs Unrolled RNN (same weights at every step) RNN cell h(t-1) x(t) = cell t=1 cell t=2 cell t=3 cell t=T x1 x2 x3 xT y1 y2 y3 yT h(t) = f(x(t), h(t-1); θ) y(t) = g(h(t); θ) one θ shared over all t

The defining equation is ht = f(xt, ht−1). The hidden state h is a fixed-size summary of the prefix x1..t. Drawing the loop as a chain of copies (unrolling) shows that an RNN is just a very deep feedforward network whose layers share weights — depth equals sequence length. Weight sharing gives generalization across positions and a parameter count independent of length; the chain structure gives sequential processing with constant memory per step at inference, which is ideal for streaming.

2. Input/Output Configurations

Sequence Task Shapes One-to-manycaptioning, generation Many-to-onesentiment, classification Many-to-manytagging, frame labels Seq2seqtranslation

The same cell supports several task shapes. Many-to-one reads the whole sequence and classifies from the final hidden state (or a pooled state) — sentiment, anomaly detection on a window. Many-to-many aligned emits an output per input step — part-of-speech or named-entity tagging, per-frame speech labels. One-to-many generates a sequence from a seed — image captioning, character-level text generation. Encoder-decoder (seq2seq) uses one RNN (blue) to encode the input into a state and another (yellow) to decode an output of different length; adding attention over encoder states (Bahdanau, 2014) was the step that led to Transformers.

3. Training: Backpropagation Through Time

BPTT: Gradients Multiply by a Jacobian at Every Step h1 h2 h(T-1) hT, loss dL/dh1 = dL/dhT · Π (dh(k)/dh(k-1)), each factor ~ Uᵀ diag(σ'(z)) largest singular value < 1: gradient vanishes exponentially (long-range info lost) largest singular value > 1: gradient explodes (loss spikes, NaN) - clip the norm

Training unrolls the network over the sequence, computes the loss, and runs ordinary backpropagation through the unrolled graph — Backpropagation Through Time (BPTT). Because the same recurrent matrix appears at every step, the gradient from step T to step 1 contains a product of T−1 Jacobians. That product tends to shrink or grow exponentially, which is the vanishing / exploding gradient problem analyzed in detail on the Vanilla RNN page. Exploding gradients are handled by gradient-norm clipping; vanishing gradients motivated the gated architectures. For very long sequences, truncated BPTT backpropagates through a fixed window of steps while still carrying the hidden state forward.

4. The RNN Family

VariantIdeaTypical use
Vanilla (Elman) RNNSingle tanh layer recurrenceTeaching, very short dependencies
LSTMAdditive cell state with forget/input/output gatesLong dependencies, time series, speech
GRUUpdate and reset gates, no separate cellSame as LSTM with fewer parameters
BidirectionalForward + backward pass, concatenatedOffline tagging, encoders
Stacked (deep)Several recurrent layersHarder tasks, larger data

5. PyTorch Example

A many-to-one sequence classifier. Switching between RNN, LSTM and GRU is a one-line change; packing handles variable-length batches so padding does not leak into the final state.

import torch
import torch.nn as nn
from torch.nn.utils.rnn import pack_padded_sequence

class SeqClassifier(nn.Module):
    def __init__(self, vocab, d_emb=64, d_hid=128, n_cls=2, cell="lstm"):
        super().__init__()
        self.emb = nn.Embedding(vocab, d_emb, padding_idx=0)
        rnn_cls = {"rnn": nn.RNN, "lstm": nn.LSTM, "gru": nn.GRU}[cell]
        self.rnn = rnn_cls(d_emb, d_hid, batch_first=True)
        self.out = nn.Linear(d_hid, n_cls)
        self.cell = cell

    def forward(self, tokens, lengths):
        x = self.emb(tokens)                               # (B, T, d_emb)
        packed = pack_padded_sequence(x, lengths.cpu(), batch_first=True,
                                      enforce_sorted=False)
        _, h = self.rnn(packed)
        if self.cell == "lstm":
            h = h[0]                                       # (h_n, c_n) -> h_n
        return self.out(h[-1])                             # last layer's final state

model = SeqClassifier(vocab=10_000, cell="gru")
tokens = torch.randint(1, 10_000, (8, 50))
lengths = torch.randint(5, 51, (8,))
for i, L in enumerate(lengths):
    tokens[i, L:] = 0                                      # pad
logits = model(tokens, lengths)
loss = nn.functional.cross_entropy(logits, torch.randint(0, 2, (8,)))
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)   # essential for RNNs
print(logits.shape)                                        # torch.Size([8, 2])

6. RNNs vs Transformers

7. Common Pitfalls

8. A Forward Pass by Hand

A scalar RNN with ht = tanh(w·xt + u·ht−1), w = 1, u = 0.5, h0 = 0, fed the input sequence [1, 0, 0]:

The trace of the first input roughly halves each step, because u = 0.5. The gradient tells the same story: ∂h3/∂h1 = u(1 − h32) · u(1 − h22) ≈ 0.484 × 0.434 ≈ 0.21. Each step multiplies by u times a tanh derivative that is at most 1, so after 20 steps the signal from step 1 is around 10−6 of its size. Making u larger than 1 does not fix this: it makes gradients explode wherever the tanh is unsaturated and still vanish where it saturates. Gated cells sidestep the dilemma with an additive path whose multiplier the network controls — see the GRU and LSTM pages for the same calculation with gates.

9. Recurrence Returns: Linear RNNs and State-Space Models

Linear Recurrence: Two Ways to Compute the Same Thing h(t) = A h(t-1) + B x(t) y(t) = C h(t) Training parallel scan / convolution Inference one step at a time Fast on GPUs O(log T) depth Constant memory no growing KV cache Because h is linear in h(t-1), the recurrence can be unrolled into a scan; a tanh between steps would forbid this.

The classical RNN's weakness for training is the nonlinearity between steps: ht cannot be computed until ht−1 is known. If the state update is linear in the previous state, the whole sequence of states can be computed with a parallel prefix scan (or, when the parameters do not depend on the input, as one long convolution), so training parallelizes across time like a Transformer. Inference can still run as a recurrence with a fixed-size state.

This is the idea behind structured state-space models such as S4, which parameterize A so that long-range memory is stable, and Mamba, which makes the parameters depend on the current input (a "selective" state space) so the model can choose what to store and what to ignore — recovering the content-based gating that LSTMs and GRUs had. RWKV and linear-attention variants reach a similar place from the Transformer side. Hybrids that interleave a few attention layers with many recurrent or state-space layers are a common design, because pure fixed-size states still struggle with exact recall of arbitrary earlier tokens, which is what attention does well.

10. Choosing a Sequence Model

SituationReasonable first choice
Short sensor or tabular time series, little dataGradient boosting on lag features, or a small GRU/LSTM; compare against a seasonal-naive baseline (see Time-Series Forecasting).
Streaming on a device with tight memoryGRU or LSTM, quantized; a state-space model if sequences are long.
Offline tagging of complete sequencesBidirectional recurrent encoder or a fine-tuned encoder-only Transformer.
Large-scale language modelingDecoder-only Transformer, possibly hybridized with state-space layers for long context.
Very long sequences (audio, genomics, logs)State-space or linear-recurrent models, or Transformers with chunked or sparse attention.

Whatever the architecture, evaluate with chronological splits for temporal data, report latency and memory at the sequence lengths you will serve, and check that the model actually uses long context (for example by truncating history and seeing whether accuracy drops) before paying for it.

11. Further Reading