Deep Learning Slides 📂 Introduction · 11 of 18 36 min read

Recurrent Neural Networks (RNN): Memory for Sequences

Language, music, and time series unfold in order — and RNNs are the networks built to read them one step at a time while remembering what came before. This visual guide covers the hidden state, unrolling through time, the vanishing gradient, LSTM and GRU gates, BPTT, and PyTorch code, with animated diagrams.

Recurrent Neural Networks

Language, music, stock prices, DNA — the world unfolds in sequences, where order and memory matter. RNNs are the networks that read one step at a time and remember what came before. Meet the architecture that gave neural nets a memory.
Hidden State & Memory Unrolled Through Time LSTM & GRU Gates PyTorch

Press Next → or use ← → arrow keys

Section 01

The World Is Full of Sequences

Read the sentence "the bank raised interest rates" and you know bank means a financial institution — because you remember the words around it. A forgetful reader who saw each word in isolation couldn't tell a river bank from a savings bank. An RNN is the reader who carries context forward, holding a running memory of everything seen so far.
🧠
The core insight

An RNN loops its own output back in as input, maintaining a hidden state that acts as memory. Each new step updates that memory with the latest input — so context accumulates across the sequence.

Section 02

Why a Standard Network Can't Do This

A feedforward network treats every input independently and expects a fixed size. Sequences break all three assumptions:

📏
Fixed input size
A dense layer needs a fixed number of inputs. Sentences, songs, and time series come in every length.
🔀
No sense of order
Shuffle the words and a feedforward net can't tell — it has no notion of "before" and "after."
🧩
No shared parameters
It would learn a separate weight for "cat" at position 1 vs position 5 — wasteful, and it can't generalise across positions.
💡
RNNs fix all three at once

One cell, reused at every time step, with a hidden state passed along — handles any length, respects order, and shares one set of weights across the whole sequence.

Section 03

The Recurrent Loop

At its heart, an RNN is a single cell with a loop: its hidden state feeds back into itself at the next step. That loop is the memory.

RNN cellhₜ = tanh(…) xₜ yₜ hₜ memory loops back
🔁
Output becomes input

The hidden state hₜ produced at one step is fed back in at the next — so the cell always sees both the new input and a summary of everything before it.

Section 03

Unrolled Through Time

Unfold that loop and the RNN becomes a chain — one copy of the same cell per time step, passing the hidden state down the line:

h₀ h₁ x₁ y₁ h₂ x₂ y₂ h₃ x₃ y₃ WW
🔑
The same weights W at every step

It's not three different cells — it's one cell reused three times. Sharing the weights across time is what lets an RNN handle any length and generalise a pattern no matter where it appears.

Section 04

The Mathematics

Two equations run at every time step. The first updates the memory; the second reads out a prediction:

hₜ = tanh(Wₓ xₜ + Wₕ hₜ₋₁ + bₕ)
new hidden state — mixes this input with the previous memory
yₜ = softmax(Wᵧ hₜ + bᵧ)
output — read the prediction off the current memory
TermShapeRole
Wₓhidden × inputHow the new input enters the memory
Wₕhidden × hiddenHow the old memory carries forward
tanh→ (−1, 1)Squashes the state, keeping it bounded
Memory + input, every step

The magic is the sum Wₓxₜ + Wₕhₜ₋₁: the present and the past are blended into one new state, then squashed by tanh.

Section 05

The Four Sequence Patterns

By choosing where inputs and outputs attach, one RNN handles four very different jobs:

PatternShapeExample task
One-to-one1 in → 1 outPlain image classification
One-to-many1 in → sequence outImage captioning, music generation
Many-to-onesequence in → 1 outSentiment analysis, time-series forecast
Many-to-manysequence in → sequence outMachine translation, speech recognition
🎛️
Same cell, different wiring

Nothing changes inside the RNN — only which time steps you feed inputs to and read outputs from. That flexibility is why RNNs fit so many domains.

Section 06

The Vanishing Gradient Problem

Whisper a message down a line of 100 people, each passing it on at 70% volume. By the hundredth person it's inaudible. During training, an RNN's gradient is multiplied at every step it travels back — and if those factors are below 1, it shrinks toward zero long before reaching the early steps.
gradient magnitude as it travels back through time ← step tt−25t−50t−75t−100
🧮
The maths is unforgiving

With a factor of 0.9 per step, 0.9¹⁰⁰ ≈ 0.000027 — effectively zero. Above 1 the opposite happens: gradients explode to NaN. Both wreck vanilla-RNN training on long sequences.

Section 07

Backpropagation Through Time

RNNs train with BPTT — unroll the network across time, then backprop through the whole chain:

1
Forward: compute h₁…hₜ and y₁…yₜ across the sequence.
2
Total loss: L = L₁ + L₂ + … + Lₜ — sum the per-step losses.
3
Backward: ∂L/∂W = Σ ∂Lₜ/∂W — the shared weights collect gradient from every step.
4
Truncate: unroll only k steps back (e.g. k = 32) to keep it affordable.
5
Clip: scale gradients down if their norm exceeds a threshold (1.0 or 5.0).
⛓️
Shared weights, summed gradients

Because W is reused at every step, its gradient is the sum of contributions from all steps — which is exactly why long chains cause vanishing or exploding gradients.

Section 08

LSTM — Long Short-Term Memory

An LSTM adds a separate cell state — a notebook that carries information across many steps almost untouched. Three gates decide what to write, erase, and read.
🚪
Forget gate
fₜ = σ(W_f·[hₜ₋₁, xₜ]) — decides what to erase from the cell state.
📝
Input gate
Chooses what new information to write in: Cₜ = fₜ⊙Cₜ₋₁ + iₜ⊙C̃ₜ.
📤
Output gate
hₜ = oₜ ⊙ tanh(Cₜ) — decides what to read out as this step's hidden state.
🛣️
The gradient highway

The cell state flows forward with only pointwise multiplication by the forget gate — no squashing. That near-uninterrupted path is how LSTMs largely solve the vanishing gradient.

Section 09

GRU — Gated Recurrent Unit

The GRU is a streamlined LSTM: two gates instead of three, no separate cell state, roughly 25% fewer parameters — and usually just as good.

rₜ = σ(Wᵣ·[hₜ₋₁, xₜ])
reset gate — how much past to forget
zₜ = σ(W_z·[hₜ₋₁, xₜ])
update gate — how much new to keep
hₜ = (1−zₜ)hₜ₋₁ + zₜh̃ₜ
blend old memory and new candidate
Lighter and faster

Fewer gates means fewer parameters and quicker training. For most tasks a GRU matches an LSTM — which is why it's a great default.

Section 09

Vanilla RNN vs LSTM vs GRU

PropertyVanilla RNNLSTMGRU
MemoryHidden state onlyCell + hidden stateGated hidden state
Gates032
Vanishing gradientSevereLargely solvedLargely solved
ParametersFewestMost~25% less than LSTM
Training speedFastestSlowestFast
Long sequencesFailsExcellentVery good
🏆
Rule of thumb

Never reach for the vanilla RNN beyond ~20 steps. Start with a GRU; move to an LSTM if you need its extra capacity on very long or complex sequences.

Section 10

Code — Vanilla RNN & PyTorch LSTM

The vanilla RNN cell is four lines of NumPy; a real LSTM classifier is a short PyTorch module:

# Vanilla RNN cell (NumPy) — one time step
h[t] = np.tanh(Wx @ x[t] + Wh @ h[t-1] + bh)   # hidden state
y[t] = Wy @ h[t] + by                          # logits
p[t] = np.exp(y[t]) / np.sum(np.exp(y[t]))     # softmax

# LSTM sentiment classifier (PyTorch)
class SentimentLSTM(nn.Module):
    def __init__(self, vocab, embed, hidden, layers):
        super().__init__()
        self.embedding = nn.Embedding(vocab, embed, padding_idx=0)
        self.lstm = nn.LSTM(embed, hidden, num_layers=layers,
                            batch_first=True, dropout=0.3)
        self.fc = nn.Linear(hidden, 1)
    def forward(self, x):
        out, (hn, cn) = self.lstm(self.embedding(x))
        return torch.sigmoid(self.fc(hn[-1])).squeeze(1)
✂️
Always clip the gradients

Before each optimizer step: nn.utils.clip_grad_norm_(model.parameters(), 1.0) — the single most important line for stable RNN training.

Section 11

Where RNNs Shine

💬
NLP
Sentiment, named-entity recognition, POS tagging — reading text in order.
🌐
Translation
Encoder-decoder seq2seq maps one language sequence to another.
📈
Time series
Stock, energy, weather, IoT sensors — forecasting from history.
🎵
Music & audio
Generating MIDI note by note; speech recognition frame by frame.
🧬
Genomics
DNA motifs and splice sites — sequences of base pairs.
↔️
Bidirectional
Read forward and backward for full context — great for tagging.
🎯
Bidirectional for understanding, one-way for generation

Use a bidirectional RNN when the whole sequence is available (classification, tagging). Stick to unidirectional when you must generate or stream — you can't peek at the future.

Section 12

RNN vs Transformer — What Changed

PropertyRNN / LSTM / GRUTransformer
ParallelismSequential — step by stepFully parallel
Long-range linksMust travel every stepDirect attention
Compute costO(T) — linearO(T²) — quadratic
Streaming inferenceNatural, one token at a timeNeeds the full sequence
Small dataOften betterNeeds lots of data
🔮
Not obsolete — specialised

Transformers dominate large-scale language, but RNNs remain the go-to for time-series, edge devices, streaming, and small-data problems. New hybrids like Mamba and RWKV blend both worlds.

Section 13

7 Golden Rules for RNNs

  Practitioner's cheat-sheet
1Always clip gradients before the optimizer step — the fix for exploding gradients.
2Never shuffle time-series data. Split chronologically, or you leak the future into training.
3Default to GRU, not vanilla RNN — plain RNNs fail beyond ~20 steps.
4Scale your inputs with a scaler fitted only on the training set.
5Use batch_first=True in PyTorch — the natural (batch, seq, features) shape.
6Detach hidden states between batches with h.detach() to stop the graph exploding.
7Bidirectional for understanding, unidirectional for generation — don't let a generator peek ahead.
🚀
Memory, one step at a time

A hidden state that carries context, weights shared across time, and gates to protect the gradient — that's the whole RNN family, from a 4-line cell to production LSTMs.