AI Grimoire
Sheet
statusstale
difficultyintroductory
timeO(n·d²)
memoryO(d)
described1997
revisedtoday

LSTM and GRU

Give the state a path through the network that is addition rather than multiplication, and put learned gates on what enters and leaves it. The gradient stops vanishing; the loop stays sequential.

Standing

StaleLoad-bearing for understanding how the field arrived here, and replaced in practice by something on this list. Worth reading, not worth reaching for.

Twenty years of sequence modelling, ended by attention in about two. Still correct for small on-device models, and the gating idea outlived the architecture entirely.

judged as of 2026-09 · what the labels mean

Theory

The vanishing gradient is a product of Jacobians. The LSTM’s answer is to add a path along which that Jacobian is approximately the identity.

ct=ftct1+itc~tc_t = f_t \odot c_{t-1} + i_t \odot \tilde{c}_t
eq. 1 — the cell state is added to, not transformed

No matrix multiplies ct1c_{t-1}. It is scaled elementwise by a gate and something is added. If ft1f_t \approx 1 the derivative ct/ct1\partial c_t / \partial c_{t-1} is approximately 1, and the gradient traverses a hundred steps undiminished — the constant error carousel, in the original paper’s phrase.

ft=σ(Wf[ht1,xt])forget: keep how much of ct1it=σ(Wi[ht1,xt])input: admit how much of c~tot=σ(Wo[ht1,xt])output: expose how much of ctc~t=tanh(Wc[ht1,xt])candidateht=ottanh(ct)\begin{aligned} f_t &= \sigma(W_f [h_{t-1}, x_t]) &\quad \text{forget: keep how much of } c_{t-1} \\ i_t &= \sigma(W_i [h_{t-1}, x_t]) &\quad \text{input: admit how much of } \tilde{c}_t \\ o_t &= \sigma(W_o [h_{t-1}, x_t]) &\quad \text{output: expose how much of } c_t \\ \tilde{c}_t &= \tanh(W_c [h_{t-1}, x_t]) &\quad \text{candidate} \\ h_t &= o_t \odot \tanh(c_t) \end{aligned}
eq. 2 — the full cell

Two states, not one. cc is the memory and is only ever gated and added to; hh is the exposed view of it and is what the next layer and the next step see. Keeping them separate is what allows the model to hold something without acting on it.

GRU

Cho et al.’s simplification merges the cell and hidden states, and ties the input and forget gates so that what is admitted is exactly what is displaced.

ht=(1zt)ht1+zth~th_t = (1 - z_t) \odot h_{t-1} + z_t \odot \tilde{h}_t
eq. 3 — one state, one convex combination

Three gates instead of four, about 25% fewer parameters, and — across every careful comparison, including Greff et al.’s eight-variant study — no consistent difference in quality. Which variant to use is a matter of what the framework defaults to.

What actually killed it

Not quality. LSTMs were state of the art in translation until 2017 and their long-range behaviour is respectable.

They were killed by eq. 1’s shape. hth_t depends on ht1h_{t-1}, so training cannot parallelise over the sequence — a length-1024 sequence is 1024 dependent kernel launches. Attention computes all positions at once, and on hardware that rewards parallelism above all else, the quadratic cost was worth paying.

Implementation

python · torch
import torch
from torch import Tensor, nn


class LSTMCell(nn.Module):
    def __init__(self, dim: int, hidden: int):
        super().__init__()
        # One matmul for all four gates, then split — four small matmuls
        # would be four kernel launches per step, and there are T of those.
        self.w = nn.Linear(dim + hidden, 4 * hidden)
        self.hidden = hidden

        # Forget-gate bias to 1: start out remembering, learn to forget.
        with torch.no_grad():
            self.w.bias[hidden : 2 * hidden].fill_(1.0)

    def forward(self, x: Tensor, state: tuple[Tensor, Tensor]) -> tuple[Tensor, Tensor]:
        h, c = state
        i, f, g, o = self.w(torch.cat([x, h], dim=-1)).chunk(4, dim=-1)

        c = torch.sigmoid(f) * c + torch.sigmoid(i) * torch.tanh(g)
        h = torch.sigmoid(o) * torch.tanh(c)
        return h, c

The fused gate projection is the standard optimisation and it is why cuDNN’s LSTM is several times faster than a hand-written loop: it fuses across gates, across layers, and across the batch, leaving only the unavoidable dependency along tt.

What survived

The architecture is gone; the gate is everywhere. SwiGLU is a multiplicative gate on a feed-forward block. Mamba’s selection mechanism is an input-dependent forget gate under another name. RWKV writes its decay as exactly that. The 1997 insight — that a network should learn what to keep rather than being forced to transform everything — outlived the recurrence it was invented to rescue.

LSTM gates
4
GRU gates
3
Parallel over t
no
Per step, hidden size d

Related

References

[1]Hochreiter & Schmidhuber — Long Short-Term Memory (1997)Neural Computation 9(8)
[2]Cho et al. — Learning Phrase Representations using RNN Encoder-Decoder (2014)arXiv:1406.1078
[3]Greff et al. — LSTM: A Search Space Odyssey (2015)arXiv:1503.04069