RNN → LSTM → GRU

* interview cheat-sheet — gate intuition, not BPTT math

1 why sequences need RNNs

A plain feedforward net treats every input as fixed-size and independent — it has no idea that word 5 came after word 4.

Sequential data (text, speech, sensor streams, stock prices) has order and context baked into it — meaning depends on what came before.

unrolled through time — same W every step h0 h1 h2 h3 W W W x1 x2 x3
the error has to travel back across every one of these arrows during training ↴

2 vanishing gradient (why vanilla RNN forgets)

Backprop-through-time multiplies the same weight matrix (and the tanh derivative, which is ≤ 1) over and over — once per timestep, going backward.

Multiply a bunch of numbers < 1 together → the product shrinks toward zero exponentially with sequence length.

Interview one-liner: "gradients from far-back timesteps shrink to ~0 before they reach early layers, so the network can't learn long-range dependencies — it effectively forgets anything more than a few steps back."

3 LSTM — gated memory

Big idea: add a cell state C_t — a conveyor belt that runs straight through the sequence, barely touched, so info (and gradient) can survive many steps.

Three gates decide what happens to that belt. Each gate is a sigmoid layer → outputs numbers 0–1, i.e. "how much to let through."

cell state C — the conveyor belt (mostly untouched) C(t-1) C(t) × forget gate f_t = σ(...) "what fraction of old C to keep" + input gate i_t · candidate C~t "what new info to add, and how much" tanh(C_t) × output gate o_t = σ(...) "what part of C to expose as h_t" h_t C_t = f_t * C(t-1) + i_t * C~t h_t = o_t * tanh(C_t)
forget → decide what to erase · input → decide what to write · output → decide what to reveal
remember it as: forget gate = eraser, input gate = pencil, output gate = the window you're allowed to read C through.

4 GRU — LSTM, simplified

GRU asks: do we really need a separate cell state and 3 gates? It merges things down:

Net effect: fewer parameters, fewer matrix multiplies per step → trains faster, and on many tasks performs about as well as LSTM.

Interview one-liner: "GRU = LSTM with the cell state and hidden state fused, and the forget/input gates fused into one update gate — same core idea, leaner."

LSTMGRU
states carriedC_t and h_th_t only
gatesforget, input, outputupdate, reset
paramsmore~25% fewer

5 sequence-to-sequence (encoder–decoder)

Used when input length ≠ output length — e.g. translating a 5-word sentence into a 7-word one.

encoder RNN context decoder RNN encoder's final hidden state = the whole input, squashed into one vector
decoder generates output tokens one at a time, conditioned on that context vector
gotcha to mention: a single fixed-size context vector is a bottleneck for long inputs — that limitation is exactly what motivated attention (lets the decoder look back at all encoder states, not just the last one).

6 where these actually get used

time series forecasting stock prices, weather, sensor / IoT readings — model runs over past values, predicts the next step (or several steps ahead). Order and recency matter a lot here.
text generation char- or word-level language models — feed in tokens so far, predict a distribution over the next token, sample, repeat. Same trick under early chatbots and autocomplete.

also: speech recognition, sentiment/sequence classification, video captioning, anomaly detection in logs — anywhere "what happened before" changes the meaning of "what's happening now."

7 code — LSTM layer

PyTorch
import torch.nn as nn

lstm = nn.LSTM(
    input_size=64,     # features per timestep
    hidden_size=128,   # size of h_t / C_t
    num_layers=2,
    batch_first=True   # x shape: (batch, seq_len, input_size)
)

output, (h_n, c_n) = lstm(x)
# output: h_t for every timestep -> (batch, seq_len, hidden_size)
# h_n, c_n: final hidden & cell state -> (num_layers, batch, hidden_size)
Keras
from tensorflow.keras.layers import LSTM

lstm = LSTM(
    units=128,             # hidden size
    return_sequences=True  # False -> only last h_t
)
output = lstm(x)  # x shape: (batch, seq_len, input_size)
30-second recap: RNN carries a hidden state forward, but repeated multiplication during BPTT kills gradients over long sequences → LSTM fixes this with a cell-state highway plus forget/input/output gates → GRU strips that down to one update gate and one reset gate, no separate cell state → seq2seq chains an encoder and decoder RNN through a context vector for input-length ≠ output-length tasks like translation. — good luck in there.