Gated Recurrent Unit
A recurrent cell that keeps old memory unless new input earns a rewrite. Two small gates decide what to forget when building a candidate and how much of it to keep.
Why Does This Exist?
A vanilla RNN rewrites its whole state every step, so old clues wash out fast. Long short-term memory fixes that with a separate cell plus three gates, at the cost of four weight matrices and two states to carry.
Many tasks do not need that full machinery. A gated recurrent unit keeps the gating idea with one state and two gates: a reset gate that decides how much past to use for a fresh candidate, and an update gate that blends that candidate against the old state. Three matrices instead of four, one line of memory instead of two, and training that runs faster.
This page covers the two gates and their tradeoff. The three gate parent lives in long short-term memory. The ungated starting point lives in vanilla RNN. Reading both directions lives in bidirectional RNN.
Think of It Like This
One notebook with two sliders
You keep trip notes in one notebook. Each new stop brings two sliders. The first slider sets how much of the old notes you reread before drafting a replacement paragraph. Slide it to zero at a new city and you draft from the fresh stop alone. Slide it to full mid city and you draft with full history beside you.
The second slider sets how much of the draft replaces the notebook. Slide it to zero and the notebook stays exactly as it was. Slide it to full and the draft overwrites it. Middle values blend.
Old notes are , the first slider is the reset gate , the draft is the candidate, the second slider is the update gate , and the notebook after is .
Where it stops: you can keep everything and still paste something new on the next page. The unit cannot, because keep and overwrite shares sum to one by construction.
How It Actually Works
Call the current input and the old state. Call the sigmoid that squashes any number into to . Call elementwise multiply. Call , , the three weight matrices with biases folded in.
Reset gate first, since the candidate needs it:
Candidate built with reset applied to the past:
Update gate, then the blend that is the whole trick:
When nears the candidate ignores the past, which helps at true boundaries such as a new sentence. When nears the state freezes. When nears the candidate overwrites. Keeping and overwriting trade off because and sum to one, unlike the independent forget and input gates in long short-term memory.
Worked example: 0.8 carried to 0.40 in one step
Take scalar state . Suppose the reset input sums to , so . Suppose the candidate input sums to , so . Suppose the update input sums to , so .
Blend:
That to move is the diagram headline: one horizontal memory line, a reset box feeding the candidate box from below, and a blend circle where keeps half the past.
What the coupling costs and buys
An LSTM can forget nothing and write a lot in the same step because its gates move independently. A GRU cannot pick that corner: more keep means less write by definition. On most mid length tasks the gap is small. What returns is real: one state to store, three matrices to learn, fewer multiplies per step, and less overfitting on small data where the fourth matrix mostly fit noise.
Code
Rebuild the worked example with plain Python and confirm .
import math
def sigmoid(a: float) -> float: return 1.0 / (1.0 + math.exp(-a))
def gru_scalar(x: float, h_prev: float, pre_r: float, pre_z: float, pre_h: float) -> float: r: float = sigmoid(pre_r) z: float = sigmoid(pre_z) cand: float = math.tanh(pre_h + 0.0 * x + 0.0 * (r * h_prev)) h_new: float = (1.0 - z) * h_prev + z * cand return h_new
h_prev: float = 0.8h_new: float = gru_scalar(1.0, h_prev, 0.0, 0.0, 0.0)
print(f"{sigmoid(0.0):.1f} {math.tanh(0.0):.1f}") # -> 0.5 0.0print(f"{h_new:.2f}") # -> 0.40The printout matches the hand arithmetic. Set the reset pre value very negative so nears and watch the candidate drop its term entirely.
Watch Out For
Reading smaller as strictly better than LSTM
Fewer matrices train faster, so teams default to GRU everywhere. On long dense sequences where keep and write must fire together, the coupled gate loses real accuracy.
Symptom: GRU trails LSTM by a stable margin on the longest inputs while matching elsewhere. Fix: benchmark both on full length data from long short-term memory before locking the cell.
Applying reset after the candidate is built
The reset product must enter the tanh. Placed after, it scales the finished candidate and the boundary behavior vanishes.
Symptom: boundaries bleed and shortening the reset path changes nothing. Fix: follow the order in the code above and test with at : the candidate must ignore , as in vanilla RNN debugging.
The Quick Version
- A GRU carries one state line instead of the two lines in an LSTM.
- Reset 0.5 gates old state 0.8 before the candidate is built in our example.
- Candidate 0.0 blends against old state 0.8 at update 0.5 to give new state 0.40.
- Update and its complement sum to one, so keeping more always means writing less.
- Three weight matrices replace four, which speeds training and shrinks the cell.
- Reset must act before the candidate tanh, or boundary resets stop working.