Skip to main content
LSTM and GRU Architectures

LSTMs & GRUs: Gated Recurrent Networks

The Memory Problem Revisited

Vanilla RNNs suffer from a fundamental flaw: they can’t remember things for long. The vanishing gradient problem means information from early time steps gets “washed out” as it passes through many layers of tanh activations. Real-world consequence: An RNN reading a book can’t remember what happened in Chapter 1 when it reaches Chapter 10.
The Solution: Instead of trying to force information through a single path, create multiple pathways for information flow, some of which can pass information unchanged. This is the key insight behind LSTMs and GRUs.

Long Short-Term Memory (LSTM)

The Big Idea: A Memory Cell with Gates

An LSTM maintains two types of state:
  1. Cell State (CtC_t): The “long-term memory” — a conveyor belt for information that flows through with minimal modification
  2. Hidden State (hth_t): The “working memory” — what the network is currently thinking about
Three gates control information flow:
  1. Forget Gate: What to erase from long-term memory (“the subject changed, forget the old topic”)
  2. Input Gate: What new information to write to long-term memory (“this is a new character, store their name”)
  3. Output Gate: What to surface from long-term memory for the current decision (“for predicting the next word, I need the subject, not the setting”)
The analogy: imagine a student taking notes during a lecture. The cell state is their notebook. The forget gate is crossing out old notes that are no longer relevant. The input gate is writing new notes. The output gate is deciding which notes to glance at to answer a question. The notebook persists across the entire lecture — that is the key difference from a vanilla RNN, which is like a student trying to remember everything in their head without writing anything down.
LSTM Cell Diagram

LSTM Equations

ft=σ(Wf[ht1,xt]+bf)(Forget gate)it=σ(Wi[ht1,xt]+bi)(Input gate)C~t=tanh(WC[ht1,xt]+bC)(Candidate values)Ct=ftCt1+itC~t(Cell state update)ot=σ(Wo[ht1,xt]+bo)(Output gate)ht=ottanh(Ct)(Hidden state)\begin{aligned} f_t &= \sigma(W_f \cdot [h_{t-1}, x_t] + b_f) & \text{(Forget gate)} \\ i_t &= \sigma(W_i \cdot [h_{t-1}, x_t] + b_i) & \text{(Input gate)} \\ \tilde{C}_t &= \tanh(W_C \cdot [h_{t-1}, x_t] + b_C) & \text{(Candidate values)} \\ C_t &= f_t \odot C_{t-1} + i_t \odot \tilde{C}_t & \text{(Cell state update)} \\ o_t &= \sigma(W_o \cdot [h_{t-1}, x_t] + b_o) & \text{(Output gate)} \\ h_t &= o_t \odot \tanh(C_t) & \text{(Hidden state)} \end{aligned} Where:
  • σ\sigma is the sigmoid function (outputs 0-1 for gating)
  • \odot is element-wise multiplication
  • [ht1,xt][h_{t-1}, x_t] is concatenation of previous hidden and current input

Complete LSTM Layer


Understanding the Gates

The Forget Gate: Learning What to Ignore

The Input Gate: Learning What to Remember

The Output Gate: Learning What to Reveal

LSTM Gates Information Flow

Why LSTM Solves Vanishing Gradients

The Gradient Highway


Gated Recurrent Unit (GRU)

A Simpler Alternative

GRU simplifies LSTM by:
  1. Combining forget and input gates into an “update gate”
  2. Merging cell state and hidden state
The key insight is that forgetting and remembering are two sides of the same coin. In an LSTM, the forget gate and input gate are independent — you could forget everything and write nothing new (losing information), or forget nothing and write a lot (accumulating unboundedly). GRU enforces a conservation law: the update gate ztz_t controls a smooth interpolation between the old state and the new candidate. When zt=1z_t = 1, you fully adopt the new candidate. When zt=0z_t = 0, you keep the old state unchanged. There is no way to simultaneously forget and fail to replace — which makes the GRU more constrained but also more stable and easier to train. Think of it like a thermostat dial. LSTM gives you two separate dials (heating and cooling), which is more flexible but also lets you accidentally run both at once. GRU gives you a single dial that smoothly blends between “keep the old temperature” and “adopt the new temperature.” Fewer controls, but harder to misconfigure.
GRU Cell Diagram

GRU Equations

zt=σ(Wz[ht1,xt])(Update gate)rt=σ(Wr[ht1,xt])(Reset gate)h~t=tanh(Wh[rtht1,xt])(Candidate)ht=(1zt)ht1+zth~t(Final state)\begin{aligned} z_t &= \sigma(W_z \cdot [h_{t-1}, x_t]) & \text{(Update gate)} \\ r_t &= \sigma(W_r \cdot [h_{t-1}, x_t]) & \text{(Reset gate)} \\ \tilde{h}_t &= \tanh(W_h \cdot [r_t \odot h_{t-1}, x_t]) & \text{(Candidate)} \\ h_t &= (1 - z_t) \odot h_{t-1} + z_t \odot \tilde{h}_t & \text{(Final state)} \end{aligned}

LSTM vs GRU Comparison


Practical Applications

Sentiment Analysis with LSTM

Language Modeling with LSTM

Sequence-to-Sequence Translation


Advanced LSTM Variants

Peephole Connections

Layer Normalization in LSTM


Best Practices and Tips

Training LSTM/GRU Models:
  1. Gradient clipping: Always use torch.nn.utils.clip_grad_norm_ with max_norm=1.0-5.0
  2. Learning rate: Start with 0.001 for Adam, 0.1-1.0 for SGD
  3. Dropout: Use 0.2-0.5 between layers, not within cells
  4. Initialization: PyTorch defaults are usually fine
  5. Bidirectional: Use for tasks where you have full sequence (classification, tagging)
Common Mistakes:
  1. Not detaching hidden state: For stateful training, detach hidden state between batches:
  2. Ignoring sequence lengths: Use pack_padded_sequence for variable-length sequences
  3. Wrong hidden state indexing: For bidirectional, hidden[-2:] contains last layer
  4. Too many layers: 2-3 layers usually sufficient; more can hurt
Advanced Debugging Hints for LSTM/GRU Training:Forget gate bias initialization: A well-known trick from the original LSTM paper: initialize the forget gate bias to 1.0 (or even 2.0). This biases the gate toward “remembering” at initialization, which helps gradient flow in early training. In PyTorch, after creating the LSTM: for name, param in lstm.named_parameters(): if 'bias' in name: n = param.size(0); param.data[n//4:n//2].fill_(1.0). The [n//4:n//2] slice targets the forget gate bias specifically because PyTorch packs gates in order (input, forget, cell, output).Cell state explosion: If your loss becomes NaN but gradients look normal, check the cell state magnitude. Unlike the hidden state (bounded by tanh), the cell state CtC_t is unbounded — it can grow arbitrarily large if the forget gate stays near 1 and the input gate keeps adding. Monitor cell_state.abs().max() during training. If it exceeds 100, you likely need gradient clipping or a lower learning rate.Teacher forcing ratio scheduling: For seq2seq models, starting with 100% teacher forcing and abruptly switching to 0% at inference causes a train/test mismatch. Schedule the ratio from 1.0 down to 0.0 over the course of training (linear or exponential decay). This is called “scheduled sampling” and significantly improves generation quality.LSTM vs GRU selection heuristic: Use LSTM as your default. Switch to GRU if: (a) you need faster training and your sequences are under 200 tokens, (b) you are memory-constrained (GRU uses 25% fewer parameters), or (c) your ablation shows no accuracy difference. For most NLP tasks with sequences over 100 tokens, LSTM has a slight edge; for shorter sequences, they are typically indistinguishable.

Exercises

Implement a complete LSTM without using nn.LSTM:
  1. Implement LSTMCell with all gates
  2. Stack cells into LSTM layer
  3. Add bidirectional support
  4. Verify outputs match nn.LSTM
Test on a simple sequence classification task.
Create a visualization of gradient flow through LSTM:
  1. Process sequences of length 10, 50, 100, 200
  2. Track gradient magnitude at each time step
  3. Compare with vanilla RNN
  4. Plot the results
What do you observe about the forget gate values in trained models?
Build an NER tagger using BiLSTM:
  1. Load CoNLL-2003 dataset
  2. Implement BiLSTM-CRF model
  3. Train with proper evaluation (F1 score)
  4. Analyze errors by entity type
Compare BiLSTM with BiGRU.
Train an LSTM to generate music:
  1. Download MIDI files and convert to sequences
  2. Train character-level LSTM on ABC notation
  3. Generate new melodies
  4. Convert back to MIDI and listen
Experiment with different temperatures.
Build a multivariate time series forecaster:
  1. Use a dataset like air quality or stock prices
  2. Implement encoder-decoder with LSTM
  3. Add attention (preview of next chapter!)
  4. Compare with simple baselines
Evaluate with proper time series cross-validation.

Key Takeaways


What’s Next

Module 10: Attention Mechanism

Go beyond sequential processing — learn how attention allows models to focus on relevant parts of the input, enabling breakthrough performance on translation, summarization, and more.

Interview Deep-Dive

Strong Answer:
  • Forget gate: ft=σ(Wf[ht1,xt]+bf)f_t = \sigma(W_f [h_{t-1}, x_t] + b_f). Sigmoid outputs in (0,1) act as a dimmer switch on each cell state element. Values near 1 keep information, near 0 erase it. Sigmoid is chosen because we need a smooth differentiable gate in [0,1].
  • Input gate + candidate: it=σ(Wi[ht1,xt]+bi)i_t = \sigma(W_i [h_{t-1}, x_t] + b_i) controls how much new information to write. Candidate values C~t=tanh(WC[ht1,xt]+bC)\tilde{C}_t = \tanh(W_C [h_{t-1}, x_t] + b_C) use tanh because its [-1, 1] range allows both additive and subtractive modifications to the cell state.
  • Cell state update: Ct=ftCt1+itC~tC_t = f_t \odot C_{t-1} + i_t \odot \tilde{C}_t. This additive update is the key — Ct/Ct1=ft\partial C_t / \partial C_{t-1} = f_t can stay near 1, preserving gradients across hundreds of steps. Compare to vanilla RNNs where gradients decay exponentially.
  • Output gate: ot=σ(Wo[ht1,xt]+bo)o_t = \sigma(W_o [h_{t-1}, x_t] + b_o), ht=ottanh(Ct)h_t = o_t \odot \tanh(C_t). The cell state may store information not needed for the current prediction. The output gate selectively exposes relevant parts.
  • The architectural insight: cell state is the memory bus, gates are learned read/write controllers. This separation of storage from computation enables long-term memory.
Follow-up: The forget gate bias is typically initialized to 1.0 instead of 0.0. Why?With bias=0, sigmoid outputs 0.5, so the LSTM forgets half the cell state every step. Over 100 steps, only (0.5)1001030(0.5)^{100} \approx 10^{-30} survives. Initializing to 1 gives sigmoid(1)=0.73, keeping the gradient highway open by default. The network can then learn which dimensions to close rather than having to first learn to keep them open. This trick (Jozefowicz et al., 2015) is considered essential practice for tasks with long dependencies.
Strong Answer:
  • GRU merges forget and input gates into a single update gate and combines cell state with hidden state, reducing parameters by roughly 25% and improving training speed by 15-20%.
  • LSTM has a separate cell state providing a cleaner gradient highway, and separate forget/input gates give more fine-grained memory control.
  • Choose GRU: small datasets (fewer parameters reduce overfitting), speed-critical applications, moderate-length dependencies (under 200 steps). GRU performs comparably to LSTM on most benchmarks with shorter sequences.
  • Choose LSTM: very long dependencies (500+ steps), ample data to support extra parameters, or when you need the explicit cell state for inspection/interpretability.
  • In practice, the performance gap is usually 1-2%, and both have been largely superseded by transformers. The choice between them matters less than the choice between recurrence and attention.
Follow-up: GRU’s reset gate has no direct equivalent in LSTM. What does it do?The reset gate rt=σ(Wr[ht1,xt])r_t = \sigma(W_r [h_{t-1}, x_t]) controls how much of the previous hidden state to expose when computing the candidate update. When rt0r_t \approx 0, the candidate ignores previous state, allowing the model to write completely fresh information. LSTM achieves a similar effect through a low forget gate combined with a high input gate, but GRU’s mechanism is more direct and parameter-efficient.
Strong Answer:
  • The cell state update Ct=ftCt1+itC~tC_t = f_t \odot C_{t-1} + i_t \odot \tilde{C}_t is additive: new state = weighted old state + weighted new candidate. Vanilla RNNs use ht=tanh(Whhht1+...)h_t = \tanh(W_{hh} h_{t-1} + ...), a nonlinear (multiplicative) transformation.
  • Gradient consequence: CT/C1=t=2Tft\partial C_T / \partial C_1 = \prod_{t=2}^{T} f_t. With forget gates near 1, this product stays close to 1 across hundreds of steps. Vanilla RNNs have WhhTdiag(tanh)\prod W_{hh}^T \cdot \text{diag}(\tanh'), which decays exponentially.
  • This is the exact same principle as ResNet: y=F(x)+xy = F(x) + x gives gradient y/x=F/x+1\partial y / \partial x = \partial F / \partial x + 1. The additive identity provides a gradient highway. LSTM’s forget gate modulates this highway (ff instead of fixed 1), but when f1f \approx 1, the effect is identical.
  • Both LSTM (1997) and ResNet (2015) independently discovered that additive shortcuts solve the gradient degradation problem in deep/long computation chains. The underlying math is the same: addition distributes gradients without attenuation.
Follow-up: Can the forget gate learn to be exactly 0 for some dimensions and exactly 1 for others simultaneously?Yes, and this is exactly what happens in practice. Different dimensions of the cell state specialize: some maintain f1f \approx 1 for hundreds of steps (long-term registers storing sentence subjects or global context), while others cycle between 0 and 1 rapidly (short-term buffers for recent token information). Visualizing forget gate values across dimensions and time steps reveals this specialization clearly — it is one of the most interpretable aspects of LSTM internals.