Gated Recurrent Units (GRU)
Gated Recurrent Units (GRUs) are a streamlined recurrent neural network architecture introduced by Cho et al. in 2014 that use learnable gate mechanisms to selectively retain or discard information across time steps, solving the vanishing gradient problem that crippled vanilla RNNs. They matter because they deliver performance comparable to LSTMs on sequence modeling tasks — speech, text, time series — at significantly reduced computational cost and with fewer parameters. The counterintuitive fact: GRUs have no separate memory cell and no output gate, yet on many benchmarks they match or outperform the more complex LSTM, suggesting that architectural simplicity can be a feature rather than a limitation.
The fundamental problem with vanilla RNNs is that gradients either explode or vanish as they propagate backward through hundreds of time steps, making it impossible to learn long-range dependencies. GRUs solve this by introducing two learned scalar gates — a reset gate and an update gate — that create a direct, nearly unobstructed path for gradients to flow backward through time. The update gate acts like a soft interpolation switch: at each step it decides "how much of the old hidden state should I keep, and how much of the new candidate should I blend in?" This blending operation is the GRU's "aha moment" — it transforms the recurrent computation from a hard overwrite into a smooth, differentiable memory update that the network can tune end-to-end.
How It Works
At each time step , a GRU takes the current input and the previous hidden state and produces an updated hidden state . The computation proceeds through three sub-steps: compute the reset gate, compute the update gate, compute the candidate hidden state, then blend old and new.
Reset Gate:
Explanation: The reset gate produces a vector of values between 0 and 1 that will be element-wise multiplied with the previous hidden state, controlling how much past context enters the candidate computation. Here and are learned weight matrices for the input and recurrent connections respectively, is a bias, and is the sigmoid function mapping each element to .
Update Gate:
Explanation: The update gate decides, for each hidden unit independently, what fraction of the old hidden state to carry forward versus how much of the new candidate state to accept. are analogously defined learned parameters. When , unit preserves its old value; when , it adopts the candidate.
Candidate Hidden State:
Explanation: The candidate is what the hidden state would become if the update gate fully accepted it — a tanh-squashed combination of the current input and the reset-gated previous hidden state. The symbol denotes the Hadamard (element-wise) product. When , the previous hidden state is zeroed out and the candidate is computed almost entirely from the current input, allowing the network to "reset."
Final Hidden State Update:
Explanation: The final hidden state is a convex combination of the old hidden state and the candidate, smoothly interpolated by the update gate — this is the operation that creates a direct gradient highway through time. This linear interpolation is the architectural key to GRU's training stability. The gradient of with respect to includes a direct additive term , which prevents the gradient from being multiplied by a small weight matrix at every step.
Parameter Count: For a GRU with input dimension and hidden dimension , the total parameter count is:
Explanation: Three gate/candidate computations each require an input weight matrix, a recurrent weight matrix, and a bias — giving GRU roughly 25% fewer parameters than a comparable LSTM, which uses four such sets. Compare to LSTM's : for this means GRU uses ~786K parameters versus LSTM's ~1.05M, a meaningful difference when stacking layers or deploying at scale.
PyTorch Implementation
```python import torch import torch.nn as nn
# Built-in GRU — idiomatic PyTorch gru = nn.GRU( input_size=256, hidden_size=512, num_layers=2, batch_first=True, # input shape: (batch, seq_len, input_size) dropout=0.3, # applied between layers (not on final layer) bidirectional=False )
# Forward pass x = torch.randn(32, 100, 256) # (batch=32, seq_len=100, features=256) h0 = torch.zeros(2, 32, 512) # (num_layers, batch, hidden_size) output, h_n = gru(x, h0)
# output: (32, 100, 512) — all hidden states
# h_n: (2, 32, 512) — final hidden state per layer
# Manual GRU cell — shows the math directly class GRUCell(nn.Module): def __init__(self, input_size: int, hidden_size: int): super().__init__() self.hidden_size = hidden_size
# Pack all three gates into one matrix multiply for efficiency self.W_xr = nn.Linear(input_size, hidden_size) self.W_hr = nn.Linear(hidden_size, hidden_size, bias=False) self.W_xz = nn.Linear(input_size, hidden_size) self.W_hz = nn.Linear(hidden_size, hidden_size, bias=False) self.W_xh = nn.Linear(input_size, hidden_size) self.W_hh = nn.Linear(hidden_size, hidden_size, bias=False) def forward(self, x: torch.Tensor, h_prev: torch.Tensor) -> torch.Tensor: r = torch.sigmoid(self.W_xr(x) + self.W_hr(h_prev)) # reset gate [1] z = torch.sigmoid(self.W_xz(x) + self.W_hz(h_prev)) # update gate [2] h_cand = torch.tanh(self.W_xh(x) + self.W_hh(r h_prev)) # candidate [3] h_next = (1 - z) h_prev + z * h_cand # blend [4] return h_next
# Usage cell = GRUCell(input_size=256, hidden_size=512) h = torch.zeros(32, 512) for t in range(100): x_t = torch.randn(32, 256) h = cell(x_t, h) ```
Kyunghyun Cho completed his PhD at Aalto University and later joined NYU as a faculty member, working at the intersection of deep learning and natural language processing. He introduced the GRU architecture in the landmark 2014 paper "Learning Phrase Representations using RNN Encoder–Decoder for Statistical Machine Translation," co-authored with Bahdanau, Bougares, Schwenk, and Bengio — the same paper that introduced the encoder-decoder framework for seq2seq learning. Remarkably, GRUs were not the paper's headline contribution; they were presented as an implementation detail of the encoder-decoder model, and their significance as a standalone architecture only became clear through subsequent community adoption.
Intuition
Imagine you are taking notes during a very long lecture. At each moment you face two decisions: how much of what was said five minutes ago still matters for understanding the current sentence? and how much should the current sentence update your summary notes? The reset gate answers the first question. When you hear a phrase like "Now, completely changing topics —", your reset gate fires high, telling your brain to largely ignore the accumulated context when forming your current interpretation. When the topic is continuous, the reset gate stays low, letting the full context inform your understanding. The update gate answers the second question. When the lecturer makes a throwaway aside, your update gate stays low — you keep your notes almost unchanged. When they say "The most important point of this entire lecture is —", your update gate opens high, and that new information largely replaces or heavily updates your summary. The magic is that the final hidden state is simply a weighted average of your old notes and what you'd write fresh — a smooth, differentiable interpolation that lets gradients flow back through the entire lecture unimpeded, as if the past were always just one step away.