Recurrent Models for NLP
Imagine predicting the next word in "The clouds are gathering, so I should bring my ___." A good model needs to remember "clouds" and "gathering" many words back. It must then connect that memory to "umbrella." A Bag-of-Words Models or fixed-window classifier discards this ordering information. It truncates it arbitrarily instead. Language is fundamentally sequential and of variable length. Any architecture that treats a sentence as an unordered set throws away meaning. The same is true for architectures that force sentences into a fixed-size window. Recurrent neural networks (RNNs) were the first widely successful architecture designed explicitly for this problem. They consume sequences of arbitrary length. They also maintain a compressed memory of everything seen so far. Building on Word Embeddings and Dense Vectors, which give each token a meaningful vector, RNNs add something more. They add a mechanism for combining those vectors over time into a running hidden state. This state acts as the network's working memory. At each step it is updated using the current input. It also uses the memory carried from the previous step. So information can in principle flow across an entire sentence, paragraph, or document. Plain RNNs, however, suffer from a well-documented failure mode. Gradients must travel back through many time steps. These gradients tend to vanish or explode. This makes it hard to learn dependencies spanning more than a few words. Long Short-Term Memory (LSTM) networks and Gated Recurrent Units (GRUs) were designed specifically to fix this. They introduce gating mechanisms. These let the network learn when to remember, forget, and expose information. These gated architectures dominated NLP from roughly 2014 to 2018. They powered machine translation, speech recognition, and language modeling. Attention-based architectures took over after that. This article builds the recurrent family from first principles. It covers how a vanilla RNN processes a sequence. It explains why its gradients misbehave. It also shows how LSTMs and GRUs repair this with gates. You will see full pseudocode for forward and backward passes. There is also a worked numerical trace. And there is an honest accounting of complexity and failure modes. The ideas here — hidden state, gating, backpropagation through time — matter beyond this article. They directly foreshadow Sequence-to-Sequence Models. They also foreshadow the contextual representations used in modern Contextual Embeddings.
- RNNs process token sequences one step at a time, maintaining a hidden state that summarizes everything seen so far.
- Vanilla RNNs suffer from vanishing/exploding gradients, which limits how far back they can learn dependencies.
- LSTMs add input, forget, and output gates plus a separate cell state, giving gradients a near-linear path across time.
- GRUs simplify LSTMs into two gates (update, reset) with comparable performance and fewer parameters.
- Training uses backpropagation through time (BPTT), which unrolls the recurrence into a deep feedforward computation graph.
- Recurrent models were the backbone of NLP before Transformers, and still matter for streaming, low-resource, and small-footprint settings.
A recurrent model compresses an arbitrarily long sequence into a fixed-size hidden state that is updated one token at a time, and gating mechanisms (LSTM/GRU) control what that state remembers, forgets, and reveals so gradients survive long dependencies.
The idea
A recurrent neural network processes a sequence of tokens by maintaining a hidden state that is updated at every time step according to a shared transition function:
The same parameters are reused at every step — this parameter sharing across time is what lets an RNN handle sequences of any length with a fixed-size model. Each is typically a word embedding (see Word Embeddings and Dense Vectors), and is meant to summarize the entire prefix . Because depends on which depends on , and so on, information from arbitrarily far back can in principle influence the current prediction — this is the defining advantage over fixed-window models. The core difficulty is long-range dependency learning. Training an RNN uses backpropagation through time (BPTT): the recurrence is "unrolled" into a deep computational graph with one layer per time step, and gradients are propagated backward through this unrolled graph. Because the same weight matrix is applied repeatedly, the gradient of the loss at step with respect to the hidden state at step involves a product of Jacobians. If the dominant eigenvalues of these Jacobians are consistently below 1, the product shrinks toward zero (vanishing gradients); if consistently above 1, it blows up (exploding gradients). Either failure prevents the network from learning dependencies that span many steps.
Gated recurrent architectures — the LSTM and the GRU — solve this by introducing a separate, more slowly changing memory pathway and learned gates: small neural networks (typically sigmoid layers) that output values in controlling how much information flows through, is overwritten, or is forgotten at each step. The key structural trick is an additive update to memory, rather than a purely multiplicative one, which creates a path along which gradients can flow with a multiplier close to 1 across many time steps.
How it works
Vanilla RNN cell. At each step , given input and previous hidden state :
For language modeling, is a distribution over the vocabulary predicting , and the loss is the sum of per-step cross-entropies.
LSTM cell. The LSTM maintains two vectors per time step: a hidden state (used for output) and a cell state (the long-term memory highway). Three gates and one candidate value are computed from and :
Here denotes concatenation, is the sigmoid function, and is elementwise multiplication. The forget gate decides how much old memory to keep; the input gate decides how much new candidate content to add; the output gate decides how much of the cell state to expose as the hidden state.
GRU cell. The GRU merges the cell and hidden state into one vector and uses two gates:
The update gate directly interpolates between the old state and the new candidate — structurally similar to the LSTM's forget/input pairing but with one gate instead of two, and no separate cell state. Below is language-agnostic pseudocode for a full forward pass of a single-layer LSTM language model over a batch of sequences, followed by the BPTT gradient computation sketch. ```
# Forward pass: LSTM language model over a sequence of length T
# x[1..T] : input token embeddings, each in R^d_in
# h0, c0 : initial hidden and cell state, each in R^d_hidden (usually zero)
# Parameters : W_f, W_i, W_o, W_c (each R^(d_hidden x (d_hidden+d_in)))
# b_f, b_i, b_o, b_c (each R^d_hidden)
# W_hy, b_y (output projection to vocabulary) function LSTM_FORWARD(x[1..T], h0, c0): h[0] = h0 c[0] = c0 for t = 1 to T: z = concat(h[t-1], x[t]) # (d_hidden + d_in)-dim vector f_t = sigmoid(W_f @ z + b_f) # forget gate i_t = sigmoid(W_i @ z + b_i) # input gate o_t = sigmoid(W_o @ z + b_o) # output gate c_tilde = tanh(W_c @ z + b_c) # candidate cell content c[t] = f_t c[t-1] + i_t c_tilde # elementwise; update cell memory h[t] = o_t * tanh(c[t]) # elementwise; expose hidden state logits[t] = W_hy @ h[t] + b_y yhat[t] = softmax(logits[t]) # predicted distribution over vocab return h[1..T], c[1..T], yhat[1..T]
function LSTM_LOSS(yhat[1..T], targets[1..T]): loss = 0 for t = 1 to T: loss += cross_entropy(yhat[t], targets[t]) # -log P(target_t) return loss / T
# Training step: unroll T steps, compute loss, backpropagate through time function TRAIN_STEP(x[1..T], targets[1..T], params, h0, c0): h[], c[], yhat[] = LSTM_FORWARD(x, h0, c0) loss = LSTM_LOSS(yhat, targets) grads = BACKPROP(loss, params) # reverse-mode autodiff through the
# unrolled T-step computation graph;
# gradients w.r.t. shared params are
# SUMMED across all T time steps CLIP_GRADIENT_NORM(grads, max_norm) # guard against exploding gradients params = OPTIMIZER_UPDATE(params, grads) return params, loss ``` The essential correctness property to notice: because (and the vanilla RNN's ) are shared across all time steps, backpropagation must accumulate their gradients by summing the contribution from every step — this is what "backpropagation through time" specifically means, as opposed to ordinary backpropagation in a feedforward net where each layer has distinct parameters.
Complexity & correctness
Time complexity. For a sequence of length , hidden size , and input embedding size , each vanilla RNN step costs for the matrix-vector products, so a full forward pass costs . An LSTM computes four such affine transforms per step (three gates plus the candidate), so its cost is up to a constant factor; a GRU uses three transforms, landing between the two. Backpropagation through time has the same asymptotic cost as the forward pass, since each step's backward computation mirrors its forward computation (standard for reverse-mode autodiff).
Space complexity. Naively, BPTT must store every intermediate activation (, gate values, candidate values) for all steps to compute gradients on the backward pass, costing memory. This is the key sequential bottleneck: unlike a Transformer's attention layer, which computes all positions in parallel, an RNN's step depends on the output of step , so steps cannot be parallelized across time — only across the batch dimension. Truncated BPTT (backpropagating only steps at a time, carrying forward without gradient) trades correctness of the gradient (it becomes a biased estimate of the true gradient) for a memory bound of and enables training on arbitrarily long streams.
Why gradients vanish or explode (proof sketch). Consider a simplified vanilla RNN recurrence . By the chain rule,
Each factor's operator norm is bounded by (since ). If the largest singular value of is consistently less than 1, this product shrinks geometrically in , so — the gradient signal from a distant time step vanishes before it can update earlier weights, explaining why vanilla RNNs struggle to learn dependencies beyond roughly 5-10 tokens in practice. If the largest singular value exceeds 1, the product can grow geometrically instead, causing exploding gradients (visible as sudden loss spikes or NaNs during training), which is why gradient clipping is standard practice.
Why LSTMs fix this. The cell-state recurrence gives
directly (with no matrix multiplication and no squashing nonlinearity in this particular path). If the network learns to keep for the time steps spanning a long-range dependency, this Jacobian is close to the identity, so gradients can flow across many steps with no multiplicative decay — this is the celebrated "constant error carousel" argument that motivated the LSTM's design. The gates themselves are learned, so the network adaptively decides which information deserves this near-lossless path, rather than every path having this property indiscriminately (which would make forgetting impossible).
Correctness of BPTT itself. BPTT is not an approximation of backpropagation; it is exact reverse-mode automatic differentiation applied to the unrolled computational graph, which is a finite, well-defined directed acyclic graph once weight-sharing is accounted for (the same weight tensor appears as a node reused at positions, and its gradient is the sum of the partials from each occurrence — a direct consequence of the multivariate chain rule for a variable used in multiple places). This is why RNNs train with the exact same guarantees as any feedforward network with tied weights.
Honest tradeoffs. LSTMs/GRUs mitigate but do not eliminate the vanishing gradient problem — extremely long sequences (thousands of tokens) still degrade performance. Their fundamentally sequential computation makes them harder to parallelize on GPUs than Transformers, which is a major reason attention-based architectures displaced them for large-scale training, even though RNNs remain attractive for streaming/online inference where output must be produced token-by-token with per-step cost rather than attending over a growing context.
Worked example
Consider language modeling on the tiny sentence "the cat sat" with a toy vocabulary and a hidden size of 2, to make the LSTM cell-state update mechanics fully concrete. Suppose (after training) the forget gate for the third word ("sat") has learned to be close to 1 in the dimension that tracks "subject established," while other gates modulate the rest.
| Step | Token | (forget) | (input) | (candidate) | ||
|---|---|---|---|---|---|---|
| 1 | the | |||||
| 2 | cat | |||||
| 3 | sat |
Reading the second hidden dimension across steps: the forget gate stays high () once "cat" establishes the subject, so the candidate contributions from "sat" () get blended with, rather than overwrite, the memory of "cat" (), giving . This is exactly the mechanism that lets the model retain "the subject is a cat" long enough to correctly predict a subject-agreeing verb form, even if several more words intervened before "sat." Contrast with a vanilla RNN on the same input: its single hidden state at each step is entirely recomputed as , with no gated, additive channel — so the influence of "the" at step 1 on is attenuated by two full multiplications through and two squashings, illustrating concretely why the vanilla model's memory decays faster than the LSTM's gated cell state.
Common pitfalls & traps
| Pitfall | Why it happens | Fix |
|---|---|---|
| Exploding gradients (loss → NaN) | Repeated multiplication by with spectral norm across many steps | Gradient norm clipping (e.g., clip to max norm 1-5) |
| Vanishing gradients on long sequences | and repeated matrix multiplication shrink the gradient geometrically | Use LSTM/GRU gating; shorten effective dependency length via truncated BPTT |
| Forgetting to detach hidden state between unrelated sequences | Carrying from a previous, unrelated document leaks information and grows the backward graph unboundedly | Reset (zero) or `.detach()` hidden state at document/batch boundaries |
| Treating padding tokens as real inputs | Batches of variable-length sequences padded to a common length still update hidden state on pad tokens | Mask the loss on padding positions; use `pack_padded_sequence`-style length tracking |
| Confusing GRU's single update gate with LSTM's separate forget/input gates | GRU couples "forget old" and "add new" into one gate ; easy to mis-derive as independent | Re-derive explicitly; note it sums to a convex combination |
| Using the last hidden state alone for long-document classification | The final may have "forgotten" salient content from early in a long document | Consider attention pooling over all , or bidirectional RNNs to capture both directions |
Pioneers & history
Origins · 1 minJürgen Schmidhuber and how this was discovered
Recurrent architectures for sequence modeling emerged from a lineage connecting theoretical neuroscience-inspired networks to a rigorous fix for a gradient pathology.
Origins · 1 minhow this was discovered
Where this matters
Gated recurrent networks powered the first generation of production neural NLP systems: Google's early Neural Machine Translation system (GNMT, 2016) used stacked LSTM encoder-decoders, and LSTMs were standard in speech recognition acoustic and language models, autocomplete systems, and named-entity recognition pipelines throughout the mid-2010s. Bidirectional LSTMs (processing a sequence forward and backward and concatenating both hidden states) became a default building block for sequence-labeling tasks like part-of-speech tagging and named-entity recognition, and formed the backbone of early Contextual Embeddings such as ELMo, which stacked bidirectional LSTMs to produce context-sensitive word representations — a direct conceptual predecessor to Transformer-based contextual models. RNN encoder-decoder pairs also established the Sequence-to-Sequence Models paradigm (encode a source sequence into a fixed vector or set of hidden states, then decode a target sequence), which persists conceptually even as the encoder and decoder internals shifted to attention and self-attention. Today, pure RNNs are less common at the frontier of large language models, but their gating principles persist in modern efficient-attention alternatives (state-space models, linear attention variants), and LSTMs/GRUs remain practical choices for low-latency streaming applications, resource-constrained on-device inference, and time-series or sensor-fusion tasks adjacent to NLP where strict sequential processing and small model footprints are valued over maximal parallelism.
Ask the tutor: Can you walk through, step by step, how the gradient of the loss at time step 5 flows back to the input at time step 1 in an LSTM versus a vanilla RNN?
Why can't RNN computation be parallelized across time the way Transformer attention is?
Each hidden state is defined as a function of , so computing requires that already be known. This creates a strict sequential dependency chain across all steps of a sequence. Transformer self-attention, by contrast, computes all positions' representations simultaneously from the same input layer, since attention scores between any pair of positions can be computed independently and combined via matrix multiplication. This is the single biggest practical reason RNNs lost favor for large-scale training on modern parallel hardware: even though RNN per-step cost is comparable or cheaper, the inability to parallelize across the sequence length makes wall-clock training time scale much worse with GPUs designed for massive parallelism.
Do LSTMs completely solve the vanishing gradient problem?
No — they substantially mitigate it but do not eliminate it. The cell-state path gives a near-identity Jacobian when , letting gradients survive much longer than in a vanilla RNN. But the hidden state still passes through a squashing nonlinearity, and forget gates are learned values that are rarely exactly 1 for every dimension across arbitrarily long spans. In practice LSTMs reliably learn dependencies spanning tens to a few hundred tokens, a large improvement over vanilla RNNs, but they still degrade on sequences of many thousands of tokens, which is part of why attention mechanisms and Transformers were developed.
What is the difference between using an RNN's final hidden state versus all hidden states for a downstream task?
The final hidden state is a fixed-size summary that compresses the entire sequence, useful for tasks like sentence classification where a single vector suffices. But this creates an information bottleneck: everything about a long document must be squeezed through one vector, and details from early tokens may be diluted by the time is reached. Using all hidden states (for example with an attention layer over them, or by feeding them all into a downstream sequence labeling layer) avoids this bottleneck and is standard for tasks like named-entity recognition or machine translation, where each output token may need to reference a different part of the input.
Why do practitioners use bidirectional RNNs, and what is the tradeoff?
A standard (unidirectional) RNN's hidden state at position only reflects tokens — it has no information about what comes after. Many NLP tasks (part-of-speech tagging, named entity recognition) benefit from context on both sides of a word. A bidirectional RNN runs two separate RNNs, one left-to-right and one right-to-left, and concatenates their hidden states at each position, giving each token's representation access to the full sentence. The tradeoff is that bidirectional models cannot be used for strictly online/streaming generation (predicting the next token as it arrives), since the backward pass requires the entire sequence to already be available.
How does gradient clipping actually prevent exploding gradients, and is it a real fix?
Gradient clipping rescales the gradient vector (or per-parameter-group gradient) so its norm never exceeds a threshold, typically by computing before the optimizer step. It does not change the underlying cause (large singular values in causing genuinely huge gradients over long unrolled chains); it simply prevents a single anomalously large gradient from destabilizing the weights in one update. It is a practical safeguard, not a structural fix like gating — LSTMs/GRUs address the vanishing side of the problem structurally through additive memory, while clipping remains standard practice for the exploding side in both vanilla RNNs and gated variants.
Are RNNs still used anywhere in modern NLP systems, given the dominance of Transformers?
Yes, though less centrally for large-scale language modeling. RNNs (especially LSTMs/GRUs) remain attractive where strict sequential, constant-memory-per-step processing matters: on-device speech recognition, real-time streaming transcription, low-latency keyboard prediction, and resource-constrained embedded NLP. Their gating principles also directly inspired newer efficient sequence architectures (state-space models like S4/Mamba, linear attention variants) that try to recover RNN-like linear-time, constant-memory inference while being more parallelizable during training. Understanding RNNs and their gates remains foundational for understanding both the history of NLP and a family of modern efficient-sequence-modeling research.
What is the relationship between RNN language models and n-gram language models?
Both are trained to predict the next token given previous context, but an n-gram model only conditions on a fixed window of the last tokens, using count-based probability estimates with smoothing. An RNN language model instead conditions on a learned, compressed summary of the entire preceding sequence via its hidden state, and computes probabilities through a neural network rather than counting. This lets RNNs in principle capture arbitrarily long dependencies and generalize better to unseen contexts (via distributed word embeddings), at the cost of requiring gradient-based training and being harder to interpret than transparent n-gram counts.