The same idea with one gate fewer
- : the update gate, how much of the state to replace.
- : the reset gate, how much of the state the candidate is allowed to see.
A GRU has two gates rather than three, and no separate cell state. The hidden state is the memory.
The last line is the design. Keeping and writing are tied together by one gate, so whatever fraction of the state is replaced is exactly the fraction that was forgotten.
Why that coupling is the whole difference
In an LSTM, and are independent.
An LSTM can forget without writing and write without forgetting, which gives it a strictly larger set of behaviours.
A GRU cannot. The constraint removes a gate's worth of parameters and, in practice, almost none of the capability.
That is 25 percent fewer parameters and 25 percent less arithmetic per step, which is the practical reason to choose one.
One timestep, in order
- Compute the update and reset gates from the previous state and the input.
- Mask the previous state with the reset gate.
- Compute the candidate state from that masked state and the input.
- Blend the old state and the candidate, weighted by the update gate.
- Emit the result, which is both the output and the memory.
There is no output gate, so the full state is exposed at every step. A GRU cannot hold something privately the way an LSTM's cell state can.
Which to use
The honest answer from a decade of comparisons is that they perform about the same, and the difference is smaller than the difference between two random seeds.
GRUs train faster and need less data, which makes them the better default on small datasets and on anything latency-bound.
LSTMs hold an edge on tasks requiring precise long-term counting or copying, where the separate cell state and the output gate genuinely buy something.
Both share the limitation that ended the era. The state at each step depends on the previous one, so neither can be parallelised across time.
Where the two are still chosen over a transformer, it is for the same reason: a fixed-size state means constant memory per token, however long the sequence runs.