Part of Language AI Handbook
Explains why gradient magnitudes collapse exponentially in RNNs, making long-range dependencies impossible to learn without architectural changes like LSTMs.
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
Vanishing Gradients
In the previous chapter, we derived Backpropagation Through Time (BPTT) and saw how gradients flow backward through an unrolled RNN. We noted that the same weight matrix multiplies the gradient at every timestep. That single observation, almost a footnote at the end of the BPTT derivation, turns out to be the central obstacle of training recurrent networks on long sequences. This chapter explores what happens to those gradients as they travel backward across dozens or hundreds of timesteps, why they almost always collapse to zero, and why this collapse makes RNNs unreliable for learning long-range dependencies. Understanding this failure mode thoroughly is the essential motivation for the LSTM architecture we will study next.
Before examining the mathematics, consider the task that any sequence model must accomplish. Language is deeply hierarchical. A sentence can contain a relative clause that interrupts the main clause. A paragraph can open with a claim that its final sentence resolves. A document can introduce a character whose actions hundreds of words later depend on a motivation established at the beginning. Any model that hopes to understand language must be able to connect information across these distances. For an RNN, this means the gradient signal that tells the network "your prediction at time was wrong because of the information at time " must survive a journey through steps. As we will see, for realistic sequence lengths, that journey is almost impossible.
The Gradient Product Problem
To train an RNN, we need to compute how the loss at time depends on the hidden state at some earlier time . This is exactly the gradient , where is the loss at the final timestep and is the hidden state at time .
Recall from the BPTT derivation that the hidden state evolves as:
where:
- is the hidden state vector at time
- is the recurrent weight matrix
- maps input to the hidden layer
- is the bias
Applying the chain rule, the gradient of with respect to involves all intermediate steps:
where each factor in the product is the Jacobian matrix:
where:
- is the pre-activation at step
- is the element-wise derivative of tanh evaluated at
- is the diagonal Jacobian of the tanh nonlinearity
So the full gradient becomes:
This is a product of matrices. When is large, this product determines whether learning succeeds or fails.
To understand why this product is dangerous, think about what it represents physically. Each factor is a local linear approximation to how perturbations to propagate to . Multiplying these local approximations together gives the global sensitivity: how much does a small change to affect ? The answer, for long sequences, is almost always: very little. The product of many factors smaller than one collapses to zero.
The Chain Rule Imposes a Product Structure
It is worth pausing to understand why the chain rule produces this particular structure and why there is no way around it within the RNN formulation. The chain rule is not a choice or an approximation: it is the exact rule for computing how composed functions depend on their inputs. The RNN is a composed function. depends on , which depends on , all the way back to . The gradient of this chain of compositions is exactly the product of the local derivatives.
This means the vanishing gradient problem is not a bug in the BPTT algorithm or a consequence of a bad implementation. It is built into the structure of the RNN itself. The recurrence relation that makes RNNs powerful at processing sequences is also the source of the gradient problem. You cannot have sequential composition without a multiplicative gradient chain.
The only escape is to change the recurrence relation itself, which is precisely what LSTM does. Rather than composing a function at every timestep (multiplying), the LSTM adds to a memory cell. Addition distributes gradient flow uniformly backward, as we will see in the LSTM chapter.
Eigenvalue Analysis: Why Products of Matrices Shrink or Explode
The behavior of a matrix product depends critically on the spectral properties of each factor. Let's analyze what happens when you repeatedly multiply similar matrices together.
Consider a simplified version where is a scalar weight (or, more precisely, where we track the behavior along the dominant eigenvalue direction). Each factor in the gradient product contributes a scaling of approximately , where denotes the largest singular value and is the diagonal scaling matrix from the nonlinearity.
Since for all (the tanh derivative is at most 1, achieved at ), and in practice the hidden states often saturate the tanh function, contributes factors strictly less than 1.
The gradient product norm satisfies:
where is the maximum gain from the nonlinearity across all timesteps.
This inequality has two regimes:
- If , the product shrinks exponentially in . This is the vanishing gradient regime.
- If , the product grows exponentially. This is the exploding gradient regime.
The boundary between the two is the unit circle in eigenvalue space. Practical networks almost always fall into the vanishing regime because the tanh nonlinearity enforces , and weight matrices are initialized to have spectral radius near or below 1 for stability.
The spectral radius of a matrix is the largest absolute eigenvalue: . For a product of matrices to avoid exponential decay, the spectral radius of each factor must be at or above 1. With tanh nonlinearities constraining , this is almost impossible to achieve in practice across a long sequence.
What the Eigenvalue Decomposition Reveals
To understand the behavior of the gradient product more deeply, it helps to think about the eigenvalue decomposition of . Suppose is diagonalizable with eigenvalues , sorted by absolute value so that .
When you multiply the matrix by itself many times, the eigenvectors associated with large eigenvalues dominate the product. For a matrix power , the contribution from the -th eigendirection scales as . For directions where , the product collapses exponentially. For directions where , the product grows exponentially.
In the gradient product, the situation is analogous but more complex because each factor also includes the diagonal scaling from the nonlinearity. Nevertheless, the dominant behavior is determined by the spectrum of . The gradient signal propagates reliably only through eigendirections with eigenvalue magnitude near or above 1. All other directions lose information exponentially fast.
This has a subtle but important consequence for what RNNs can remember. The information that survives long backward passes is not arbitrary: it is precisely the information aligned with the dominant eigenvectors of . The recurrent weight matrix effectively selects which aspects of the hidden state are memorable and which are forgettable based purely on its spectral structure, not based on what the task requires. This spectral selectivity is one reason vanilla RNNs are difficult to reason about and hard to train for specific long-range tasks.
The Sigmoid Nonlinearity Makes Things Worse
While the analysis above uses tanh, the original LSTM paper and many early RNNs used sigmoid activations in some layers. Sigmoid is even more problematic for gradient flow.
The sigmoid function has a maximum derivative of , occurring at :
where:
- is the sigmoid output, always in
- is the complement, also in
- Their product is maximized when , giving
This means each sigmoid gate contributes at most a factor of to the gradient. A chain of 10 sigmoid layers multiplies the gradient by at most . Over 100 timesteps, the gradient magnitude is bounded by , which is effectively zero in floating-point arithmetic.
Tanh has a maximum derivative of 1 (at ), so it is somewhat better, but the maximum is only achieved exactly at zero. In practice, hidden states drift away from zero as the network processes sequences, and the effective gain drops well below 1.
A saturating nonlinearity is one where the derivative approaches zero as the input magnitude grows. Both tanh and sigmoid saturate: tanh approaches plus or minus 1 with near-zero slope for large , and sigmoid approaches 0 or 1 with near-zero slope. When hidden states are pushed into saturated regions, they contribute near-zero factors to the gradient product, accelerating vanishing.
To appreciate the interplay between saturation and vanishing gradients, consider what happens during the forward pass of a long sequence. At each step, the network computes a new hidden state and passes it through tanh. If the inputs are informative and the network is responding strongly, the pre-activation values will have large magnitudes, and tanh will saturate them to values near . This saturation is desirable from a representational standpoint: it allows the network to make confident decisions about whether information is present. But the same saturation that makes the forward representation less brittle makes the backward gradient fragile. A hidden state near has a derivative near zero, contributing nearly zero gain to the gradient product at that step. The very steps where the network is most confident about its representation are the steps that contribute most to gradient decay.
This creates a fundamental tension. Informative hidden states tend to saturate, which kills gradient flow. Unsaturated hidden states near zero have maximum gradient flow, but they also represent uncertain, low-magnitude activations. The network cannot simultaneously have rich, confident representations and efficient gradient flow when using saturating nonlinearities.
Exploding Gradients and Norm Explosion
The complementary problem is exploding gradients. When the spectral radius of exceeds , the gradient product grows without bound. In practice, this manifests as the gradient norm increasing by orders of magnitude within a few backward steps.
Exploding gradients are easier to detect than vanishing gradients: the loss suddenly spikes to NaN or infinity, and you can observe the gradient norm growing exponentially in training logs. The standard fix is gradient clipping, covered separately in the Neural Network Foundations chapter.
The asymmetry between vanishing and exploding gradients is important:
- Exploding gradients are detectable and fixable through gradient clipping. The norm becomes absurdly large, giving you a clear signal.
- Vanishing gradients are silent. The loss may appear to decrease, but the network learns only from the most recent few timesteps. There is no error or warning: the weight updates for long-range connections are just approximately zero.
This asymmetry makes vanishing gradients the more dangerous and practically important failure mode.
How Exploding Gradients Manifest in Practice
When gradients explode, the effect is dramatic and unmistakable. A weight update of magnitude to is applied to parameters that were initialized with magnitudes near 0.01. The parameters jump to wildly incorrect values, the forward pass produces garbage activations or NaN values, and the loss spikes to infinity. Training collapses.
The fix, gradient clipping, is simple in concept: if the global gradient norm exceeds a threshold (typically 1.0 or 5.0), scale down all gradients proportionally so the global norm equals the threshold. This prevents catastrophic parameter updates while preserving the gradient direction. Gradient clipping does not fix the underlying gradient explosion problem but makes it manageable.
It is worth asking why the same spectral radius condition that causes vanishing gradients for small spectral radii causes explosion for large ones. The answer is that both are consequences of the same product structure. When each factor has spectral radius less than 1, the product converges to zero. When each factor has spectral radius greater than 1, the product diverges. The only difference is the direction of the instability.
In practice, training with exploding gradients is more tractable than training with vanishing gradients, because clipping provides a consistent workaround. Vanishing gradients have no analogous fix: there is no operation you can apply after the fact to recover gradient information that was lost during backpropagation.
Effective Gradient Distance
The vanishing gradient problem means that the signal from distant timesteps is effectively zero. We can formalize this with the concept of effective gradient distance: the maximum number of timesteps over which a gradient signal remains large enough to update the weights.
If each timestep contributes a multiplicative factor of to the gradient magnitude, then after steps, the gradient has magnitude proportional to . The effective gradient distance is roughly the at which falls below the numerical noise floor, often taken as relative to the initial magnitude:
For , this gives steps, which seems large. But typical RNN training uses values closer to 0.7 to 0.8, giving between 25 and 50 steps. Sequences of hundreds of tokens far exceed this budget, and dependencies across paragraphs or documents are completely invisible to the gradient signal.
The Memory Horizon
The effective gradient distance defines what we can call the memory horizon of the network: the temporal window within which the network can learn. Beyond this horizon, weight updates are effectively zero and the network behaves as if those earlier tokens never existed.
The memory horizon is not a sharp cutoff. Gradients decay continuously, and the rate of decay varies across different directions in the hidden state space. Some information can survive longer than average if it happens to align with the dominant eigenvectors of . But the average behavior is exponential decay, and for most practical tasks, the memory horizon falls well short of what is needed.
Consider the implications for language modeling. Subject-verb agreement in English typically requires connecting a noun to a verb separated by a short clause. For a sentence like "The man who lives in the old house is friendly," the subject "man" and the verb "is" are separated by six tokens. A network with a memory horizon of 20 steps could in principle learn this dependency. But for a sentence like "The man who lives in the old house near the park with the beautiful fountain and the ancient oak trees that the children always play around in summer is friendly," the subject and verb are separated by more than 30 tokens. Such examples reveal how even moderate clause embeddings can push dependencies beyond the vanilla RNN's effective range.
In practice, the memory horizon for a vanilla RNN trained on natural language is often estimated at roughly 10 to 15 tokens. This is enough for local syntactic patterns, short-distance agreement, and simple phrase-level semantics. It is not enough for clause-level dependencies, paragraph-level coherence, or document-level structure.
Empirical Evidence of Vanishing in Practice
Beyond the theoretical analysis, empirical experiments confirm that vanilla RNNs consistently fail to learn long-range dependencies. Several classic experiments demonstrate this.
Copying task: The network receives a sequence of symbols, then must reproduce the symbols after a delay. Vanilla RNNs succeed easily for delays of 5 to 10 steps but fail almost completely for delays of 50 to 100 steps. The required information simply cannot survive the gradient decay. This task is entirely artificial, with no linguistic complexity, yet the RNN cannot solve it for long delays. This eliminates confounding factors: it is not that the task is linguistically hard. It is that the gradient cannot travel far enough to train the relevant weights.
Counting task: The network must count occurrences of a pattern across a long sequence. The count must be maintained in the hidden state, but gradients that would reinforce count-preserving behavior vanish before reaching the early timesteps. The network either fails to maintain the count at all or maintains it correctly for short sequences and fails for long ones.
Language modeling: When trained on natural language, vanilla RNNs learn local word patterns (bigrams, trigrams, short syntactic structures) reasonably well. They fail to capture subject-verb agreement across long clauses, pronoun coreference, or topic coherence across paragraphs. The perplexity of vanilla RNNs on held-out text plateaus at values that reflect this shallow modeling: the network has learned a sophisticated local grammar but cannot connect information across the document.
Sentiment analysis across long reviews: A review that opens with extensive praise and ends with a qualification can fool a vanilla RNN. The positive signal at the beginning is washed out by the long gradient chain before it influences the final prediction. LSTMs handle such long-range sentiment signals much more reliably.
These empirical failures match the theoretical prediction: the effective receptive field of a vanilla RNN is bounded by the effective gradient distance, not the full sequence length. No amount of additional training data or hyperparameter tuning can extend the effective gradient distance. The problem is structural, not empirical.
Why Long-Range Dependencies Require Stable Gradient Flow
To understand intuitively why long-range dependencies break down, consider training a network to predict that "the cat, which sat on the mat for a very long time, was hungry." The word "hungry" must agree with "cat," which appeared 10 tokens earlier. The gradient of the prediction error on "hungry" must travel backward through "was," "time," "very," "a," "for," "mat," "the," "on," "sat," "which" before reaching "cat." Each of those 10 backward steps multiplies the gradient by a factor less than 1, shrinking it toward zero.
The weight update that would strengthen the "cat to hungry" connection is proportional to this vanished gradient. With a gradient of , the weight update is effectively zero regardless of the learning rate. The network cannot learn this dependency, no matter how many epochs you train for.
This is not a problem of insufficient training time or learning rate tuning. It is a fundamental limitation of the architecture: the gradient signal cannot reliably propagate information over long distances because multiplication by many small factors inevitably destroys the signal.
The Credit Assignment Problem
The vanishing gradient problem is a specific manifestation of the broader credit assignment problem in machine learning. Credit assignment asks: given that the network made an error at step , which earlier decisions were responsible, and by how much? For a vanilla RNN, the credit assignment problem is particularly severe because the gradient is the only mechanism for tracing responsibility backward through time.
In a feedforward network, credit is assigned through the depth of the network (layers correspond to steps of transformation). In a recurrent network, credit must be assigned through the time dimension of the sequence. The longer the sequence, the harder the credit assignment problem. And unlike feedforward networks, where depth can be controlled by architecture design, sequence length is a property of the data and cannot be arbitrarily reduced without discarding information.
The vanishing gradient problem means that for distant timesteps, the credit assigned by gradient descent is essentially zero. The network assigns full credit to recent steps and no credit to distant ones, producing a systematic bias toward local patterns regardless of which patterns matter for the task. This is not a soft or gradual bias: for sequences longer than the memory horizon, the credit assignment for distant tokens rounds to exactly zero in floating-point arithmetic.
Visualizing Gradient Flow Through Time
The easiest way to see vanishing gradients empirically is to train an RNN and track the gradient norm at each timestep. In a healthy training scenario, gradients from all timesteps should have comparable magnitudes. In practice, you see exponential decay.
import numpy as np
np.random.seed(42)
def tanh_deriv(z):
return 1.0 - np.tanh(z) ** 2
def sigmoid_deriv(z):
s = 1.0 / (1.0 + np.exp(-z))
return s * (1.0 - s)
def compute_gradient_norms(
T, hidden_size, W_spectral_radius, activation="tanh", seed=0
):
"""
Simulate gradient norms flowing backward through T timesteps.
Returns a list of gradient norms at each timestep index (from T down to 0).
"""
rng = np.random.default_rng(seed)
W = rng.standard_normal((hidden_size, hidden_size))
eigvals = np.linalg.eigvals(W)
current_radius = np.max(np.abs(eigvals))
W = W * (W_spectral_radius / current_radius)
h = rng.standard_normal(hidden_size) * 0.1
z_values = []
for t in range(T):
z = W @ h + rng.standard_normal(hidden_size) * 0.1
z_values.append(z)
if activation == "tanh":
h = np.tanh(z)
else:
h = 1.0 / (1.0 + np.exp(-z))
grad = np.eye(hidden_size)
norms = [np.linalg.norm(grad, "fro")]
for t in reversed(range(T)):
z = z_values[t]
if activation == "tanh":
d = tanh_deriv(z)
else:
d = sigmoid_deriv(z)
J = np.diag(d) @ W
grad = J @ grad
norms.append(np.linalg.norm(grad, "fro"))
return normsT = 60
hidden_size = 32
scenarios = [
("tanh, rho=0.95", "tanh", 0.95, "#1f77b4"),
("tanh, rho=0.80", "tanh", 0.80, "#ff7f0e"),
("sigmoid, rho=0.95", "sigmoid", 0.95, "#2ca02c"),
("sigmoid, rho=0.80", "sigmoid", 0.80, "#d62728"),
]
results = {}
for label, act, rho, color in scenarios:
norms = compute_gradient_norms(T, hidden_size, rho, activation=act, seed=42)
results[label] = (norms, color)
The plot confirms the theory. Even the most favorable configuration, tanh activations with a spectral radius near 1, shows the gradient norm decaying by several orders of magnitude over 60 timesteps. The sigmoid variants collapse far faster. When training on sequences of length 100 or more, the gradients reaching early timesteps are negligibly small: the weights responsible for long-range dependencies receive near-zero updates throughout training.
Notice how the y-axis of this plot spans many orders of magnitude. The gradient norm at step 60 is often a billion or a trillion times smaller. When you print out gradient values during training and see something like , that is not a rounding error. That is the actual gradient, and it is telling you that the network is completely blind to any influence from 60 steps ago.
The Geometry of the Problem: A Worked Example
To make the vanishing gradient concrete, let's trace a specific gradient through a small RNN by hand.
Consider an RNN with hidden size 2 and a single timestep transition matrix:
The spectral radius of this matrix is approximately 0.71 (less than 1). Now imagine a sequence of length 5. The gradient of with respect to involves multiplying through 5 Jacobian factors:
where each has spectral radius at most 0.71.
After 5 steps: . The gradient has shrunk by a factor of about 5.
After 10 steps: . A factor of 30 reduction.
After 20 steps: . A factor of 900 reduction.
After 40 steps: . Numerically negligible.
This is just one specific matrix. Let's compute this numerically to see how it plays out across various sequence lengths:
W_example = np.array([[0.5, 0.3], [0.2, 0.4]])
eigvals_example = np.linalg.eigvals(W_example)
spectral_radius_example = np.max(np.abs(eigvals_example))
sequence_lengths = [5, 10, 20, 40, 60]
np.random.seed(123)
h_ex = np.array([0.5, -0.3])
grad_ex = np.eye(2)
step_norms = [np.linalg.norm(grad_ex, "fro")]
for step in range(max(sequence_lengths)):
z_ex = W_example @ h_ex
d_ex = tanh_deriv(z_ex)
J_ex = np.diag(d_ex) @ W_example
grad_ex = J_ex @ grad_ex
h_ex = np.tanh(z_ex)
step_norms.append(np.linalg.norm(grad_ex, "fro"))
grad_norms_by_length = {
length: step_norms[length] for length in sequence_lengths
}Spectral radius of W_h: 0.7000 Sequence length Gradient norm Relative to step 1 ----------------------------------------------------------------- 5 1.679169e-01 2.322032e-01 10 2.821764e-02 3.902066e-02 20 7.970731e-04 1.102230e-03 40 6.360027e-07 8.794940e-07 60 5.074810e-10 7.017682e-10
The output shows the gradient norm collapsing exponentially. By step 40, it has shrunk by more than 6 orders of magnitude relative to step 5. Any weight update proportional to this gradient is effectively zero regardless of learning rate choice.
The numbers make the problem viscerally clear. A learning rate of 0.01 applied to a gradient of produces a weight update of . With 32-bit floating-point precision, this update rounds to zero. Long-range dependencies are simply unlearnable.
What the Worked Example Reveals About Initialization
The example above used a specific matrix with spectral radius 0.71. But the choice of initialization matters enormously for how quickly gradients vanish. Early practitioners tried to fight vanishing gradients by using larger initialization, hoping to push the spectral radius closer to 1. This helps, but only partially.
The issue is that the tanh nonlinearity still contributes damping factors less than 1 at every step (unless all pre-activations are exactly at zero). Even with a weight matrix whose spectral radius is exactly 1, the effective spectral radius of the full Jacobian will be less than 1 at every step where the hidden state has moved away from zero. And as the network processes information, the hidden states will inevitably move away from zero.
This suggests that careful initialization can delay but not eliminate vanishing gradients. The network might successfully learn dependencies up to 30 or 40 steps with good initialization, compared to 10 or 15 steps with poor initialization. But for sequences of length 100, 500, or 1000, even the best initialization leaves most of the sequence out of the effective gradient range.
The Vanishing vs Exploding Tradeoff
The vanishing and exploding problems define the two sides of a knife-edge. When the spectral radius is far below 1, gradients vanish and long-range learning fails. When it is far above 1, gradients explode and training becomes numerically unstable. The stable regime is a narrow band around spectral radius 1, but even within this band, the tanh nonlinearity prevents perfect gradient preservation across long sequences because for .
This creates a fundamental architectural constraint: vanilla RNNs have no mechanism to maintain gradient magnitude over long distances. The recurrent multiplication is the same every step, driven by the same , and there is no way to independently control gradient flow while also encoding sequence semantics.
spectral_radii = np.linspace(0.1, 1.5, 100)
T_fixed = 50
hidden_size_small = 16
norm_after_T = []
for rho in spectral_radii:
norms = compute_gradient_norms(
T_fixed, hidden_size_small, rho, activation="tanh", seed=7
)
norm_after_T.append(norms[-1])
norm_after_T = np.array(norm_after_T)
The plot makes the tradeoff vivid. Below spectral radius 1, gradient norms collapse toward zero over 50 steps. Above spectral radius 1, they explode. The inflection point near 1 appears to offer a solution, but even there, the tanh nonlinearity prevents perfect gradient preservation across long sequences.
The knife-edge at spectral radius 1 is not a stable operating point. Small perturbations to from weight updates during training will push the spectral radius slightly above or below 1, and the gradient behavior will shift accordingly. Maintaining the spectral radius precisely at 1 would require a constraint on the optimization, and such constraints typically slow convergence and limit the expressiveness of the network.
Code Implementation: Observing Vanishing Gradients Empirically
To see vanishing gradients in a real training scenario, we can train a small RNN on a synthetic long-range dependency task and examine both the gradient norms at each timestep and the model's performance.
Synthetic Long-Range Dependency Task
We create a task where the correct prediction at the final timestep depends on a token that appeared many steps earlier. The network must remember information across an extended blank sequence. This is the classic copying task used in many papers to benchmark recurrent architectures.
import torch
torch.use_deterministic_algorithms(False)
def generate_memory_task(n_samples, delay, vocab_size=8, seed=42):
"""
Generate sequences where the last token must match the first token.
Input: [signal, zeros*delay, query]
Target: predict signal value at the query position.
"""
torch.manual_seed(seed)
signals = torch.randint(0, vocab_size, (n_samples,))
X = torch.zeros(n_samples, delay + 2, dtype=torch.long)
X[:, 0] = signals
X[:, -1] = vocab_size # special query token
y = signals
return X, y
delay_short = 5
delay_long = 40
vocab_size = 8
n_train = 800
n_test = 200
X_short, y_short = generate_memory_task(n_train, delay_short, vocab_size)
X_long, y_long = generate_memory_task(n_train, delay_long, vocab_size)
X_short_test, y_short_test = generate_memory_task(
n_test, delay_short, vocab_size, seed=99
)
X_long_test, y_long_test = generate_memory_task(
n_test, delay_long, vocab_size, seed=99
)The task has a deliberate structure. The first token in each sequence is the signal: one of eight possible symbols. The next delay tokens are all zeros, acting as blank filler. The final token is a special query marker. At the query position, the network must predict the symbol that appeared at the very beginning. For a short delay, this is feasible. For a long delay, the gradient cannot reach the signal position.
import torch.nn as nn
class SimpleRNN(nn.Module):
def __init__(self, vocab_size, embed_dim, hidden_size, n_classes):
super().__init__()
self.embed = nn.Embedding(vocab_size + 1, embed_dim)
self.rnn = nn.RNN(embed_dim, hidden_size, batch_first=True)
self.classifier = nn.Linear(hidden_size, n_classes)
def forward(self, x):
emb = self.embed(x)
out, _ = self.rnn(emb)
return self.classifier(out[:, -1, :])
embed_dim = 16
hidden_size = 32
n_classes = vocab_size
model_short = SimpleRNN(vocab_size, embed_dim, hidden_size, n_classes)
model_long = SimpleRNN(vocab_size, embed_dim, hidden_size, n_classes)import torch
import torch.optim as optim
def train_model(model, X_train, y_train, X_test, y_test, n_epochs=60, lr=0.01):
optimizer = optim.Adam(model.parameters(), lr=lr)
criterion = nn.CrossEntropyLoss()
train_losses = []
test_accs = []
for epoch in range(n_epochs):
model.train()
logits = model(X_train)
loss = criterion(logits, y_train)
optimizer.zero_grad()
loss.backward()
optimizer.step()
train_losses.append(loss.item())
if (epoch + 1) % 5 == 0:
model.eval()
with torch.no_grad():
test_logits = model(X_test)
preds = test_logits.argmax(dim=1)
acc = (preds == y_test).float().mean().item()
test_accs.append(acc)
return train_losses, test_accs
train_loss_short, test_acc_short = train_model(
model_short, X_short, y_short, X_short_test, y_short_test
)
train_loss_long, test_acc_long = train_model(
model_long, X_long, y_long, X_long_test, y_long_test
)Task: predict which token appeared 5 steps ago Final test accuracy: 1.000 Baseline (random): 0.125 Task: predict which token appeared 40 steps ago Final test accuracy: 0.110 Baseline (random): 0.125


The training curves show a clean contrast. The short-delay model reduces its loss consistently and achieves high accuracy. The long-delay model struggles: its loss barely decreases and its accuracy hovers near the random baseline. The only difference between the two tasks is where the relevant information sits in the sequence. The vanilla RNN architecture provides no mechanism to bridge the gap.
This result is not a function of insufficient training time or a poorly chosen learning rate. We could train for 600 epochs instead of 60, and the long-delay model would still fail. The gradient reaching the signal position is so small that no amount of gradient descent can accumulate useful weight updates.
Measuring Per-Timestep Gradient Norms During Training
We can also look directly at the gradient norms assigned to each timestep by extracting gradients from the computation graph after a backward pass.
def measure_gradient_norms_training(model, X, y):
"""
Run one forward-backward pass and collect gradient norms
for the hidden state at each timestep.
"""
model.train()
model.zero_grad()
embed_out = model.embed(X)
h = torch.zeros(1, X.size(0), model.rnn.hidden_size)
hiddens = []
for t in range(X.size(1)):
xt = embed_out[:, t : t + 1, :]
_, h = model.rnn(xt, h)
h.retain_grad()
hiddens.append(h)
logits = model.classifier(hiddens[-1].squeeze(0))
loss = nn.CrossEntropyLoss()(logits, y)
loss.backward()
norms = []
for h_t in hiddens:
if h_t.grad is not None:
norms.append(h_t.grad.norm().item())
else:
norms.append(0.0)
return norms
model_for_grad_short = SimpleRNN(vocab_size, embed_dim, hidden_size, n_classes)
model_for_grad_long = SimpleRNN(vocab_size, embed_dim, hidden_size, n_classes)
grad_norms_short = measure_gradient_norms_training(
model_for_grad_short, X_short[:64], y_short[:64]
)
grad_norms_long = measure_gradient_norms_training(
model_for_grad_long, X_long[:64], y_long[:64]
)

These bar plots provide the direct empirical evidence. In the short-delay case, gradient norms are spread across the sequence and remain measurable at timestep 0 where the signal appeared. In the long-delay case, essentially all gradient signal is concentrated in the last few steps. The gradient at timestep 0, where the signal lives, is negligible. The network has no gradient to guide it toward learning the dependency.
Looking at these plots, you can almost feel the network's blindness. The bar chart for the long-delay task shows a tall column at the last timestep, a few smaller bars at the preceding steps, and then essentially nothing for the first 40 timesteps. The signal is there in the data. The network processes it during the forward pass. But the backward pass cannot reach it. The 40 steps of blank tokens between the signal and the query are not just noise: they are a 40-step gradient barrier.
Attempts to Mitigate Vanishing Gradients Without Architecture Changes
Before the LSTM became the standard solution, several techniques were proposed to mitigate vanishing gradients without fundamentally changing the architecture. Understanding why these partial solutions fail is instructive.
Careful Initialization
One approach was to initialize to be close to the identity matrix or to have spectral radius exactly 1. This is called identity initialization or orthogonal initialization. With an identity-initialized , the gradient product starts at the identity and decays only due to the nonlinearity damping factors. This delays vanishing but does not prevent it.
The practical limitation is that the weight matrix must change during training to learn useful representations. As soon as weight updates begin, the spectral radius moves away from 1, and gradient decay resumes. Constraining the spectral radius during training requires projecting the weight matrix onto the set of orthogonal matrices after every update, which is computationally expensive and disrupts the optimization landscape.
Irene Sutskever and colleagues experimented with careful initialization strategies in the early 2010s and found that they could improve the performance of vanilla RNNs on certain tasks. But the gains were limited: a well-initialized vanilla RNN could match an LSTM on short-sequence tasks but still failed on long sequences where the gradient decay problem was fundamental.
Truncated Backpropagation Through Time
A pragmatic workaround is truncated BPTT: instead of propagating gradients through the full sequence, you stop the backward pass after steps. This makes training faster and avoids the numerical instability of very long gradient chains, but it does not solve the problem. It simply admits defeat by explicitly limiting the temporal horizon. The network cannot learn dependencies longer than steps by construction.
Truncated BPTT is still widely used as an optimization technique even with LSTM networks, because it reduces memory usage and improves training speed. With LSTMs, the cell state provides a separate gradient path that persists even when the hidden state gradient is truncated, so truncated BPTT is less damaging in practice than it is for vanilla RNNs.
The truncation length is a hyperparameter that creates a tradeoff between computational cost and temporal range. Setting to 20 means the network can potentially learn dependencies up to 20 steps, but no further. Setting to 100 allows longer dependencies but requires holding 100 steps of activations in memory and computing 100 backward steps per update, which is expensive. Most practitioners using vanilla RNNs chose small values to keep training tractable, which inadvertently reinforced the impression that RNNs are fundamentally local models.
Gradient Clipping as a Partial Fix
Gradient clipping addresses the exploding gradient problem but has no effect on vanishing gradients. Clipping prevents the gradient from becoming too large, but it cannot inflate a gradient that has already shrunk to near zero. The two problems are not symmetric.
Some practitioners mistakenly apply gradient clipping hoping to stabilize training when they are experiencing vanishing gradients. The loss may still appear to decrease (because local, short-range updates are still occurring), and the absence of NaN errors suggests training is healthy. The silent failure mode of vanishing gradients means these practitioners may train for a long time without realizing that long-range dependencies are never being learned.
Leaky Integration and Echo State Networks
A more radical approach is to abandon gradient-based learning for the recurrent weights entirely. Echo State Networks (ESNs) and Liquid State Machines fix the recurrent weights randomly and only train the output layer via linear regression. This completely sidesteps the vanishing gradient problem by eliminating the recurrent gradient chain. The fixed recurrent weights create a complex dynamical system (the "reservoir") whose state encodes the recent history, and the output layer learns to read out the relevant information.
ESNs can capture some long-range dependencies through the dynamics of the reservoir, but the reliance on random weights limits their flexibility. The reservoir must be large enough and the dynamics complex enough to implicitly represent all the dependencies the task requires, which is not guaranteed. ESNs found niche applications but never became the dominant approach to sequence modeling.
Historical Context: Why This Problem Went Unsolved for So Long
The vanishing gradient problem was not discovered suddenly. It accumulated gradually through the late 1980s and early 1990s as researchers attempted to train recurrent networks on increasingly complex sequence tasks. Sepp Hochreiter identified and formalized the problem in his 1991 diploma thesis. This provides the mathematical analysis we have largely covered in this chapter. The formal analysis in English attracted relatively little attention until the LSTM paper in 1997.
Part of the reason the problem persisted without a reliable solution for several years was that exploding gradients received more attention. Exploding gradients were easy to observe and directly fixable with clipping. Vanishing gradients were difficult to observe and required an architectural solution rather than a training trick.
Another factor was the prevailing belief that training issues with RNNs were due to poor optimization, insufficient data, or bad initialization rather than a fundamental architectural flaw. The culture of the field emphasized that neural networks were universal approximators and that with the right training setup, any network could learn any function. The idea that an architecture could be provably limited in what it could learn via gradient descent, even if theoretically capable of representing the target function with the right weights, was subtle and not immediately accepted.
Hochreiter and Schmidhuber's 1997 LSTM paper was the turning point. It provided the theoretical analysis and a complete architectural solution that was empirically validated on the exact tasks where vanilla RNNs failed. The success of LSTMs on long-range dependency benchmarks demonstrated that the vanishing gradient problem was the bottleneck and that architectural changes could overcome it.
The two-decade gap between the RNN's invention and the LSTM's adoption illustrates a general pattern in deep learning: fundamental limitations are often recognized empirically before they are understood theoretically, and solutions often precede complete theoretical understanding of why they work. The gradient flow analysis we have covered in this chapter was largely worked out by Hochreiter in 1991, but it took the empirical success of LSTMs to convince the broader community that the analysis was pointing to a real and solvable problem.
The Vanishing Gradient Problem in Deep Feedforward Networks
Before leaving this topic, it is worth connecting the RNN vanishing gradient problem to an analogous problem in deep feedforward networks. In a feedforward network with many layers, the gradient must propagate backward through each layer in sequence. If each layer applies a saturating nonlinearity, the gradient is repeatedly multiplied by derivative factors less than 1, producing the same exponential decay we see in RNNs.
This is why very deep feedforward networks were difficult to train before the introduction of batch normalization (2015) and residual connections (2015). A 20-layer network with sigmoid activations would experience the same gradient collapse over 20 backward passes through layers that an RNN experiences over 20 backward steps through time. The problems are mathematically identical.
The solutions are also analogous. Residual connections in feedforward networks create gradient shortcuts that bypass the multiplicative chain, just as the LSTM cell state creates a gradient highway through time. Batch normalization keeps activations away from saturation zones, reducing the damping factor at each step, just as proper initialization can reduce damping in RNNs.
This connection is not a coincidence. Both problems arise from the same root cause: repeated multiplication of Jacobian matrices through a chain of composed functions, where each Jacobian has spectral properties that lead to exponential decay. The solutions in both cases involve creating additive pathways that allow gradients to bypass the multiplicative chain.
The correspondence between depth in feedforward networks and time in recurrent networks is precise. A feedforward network with layers and a recurrent network with sequence length both require gradient signals to traverse or multiplicative factors, respectively. This analogy guided the development of residual connections: researchers who had solved the depth problem in feedforward networks recognized the same structure in the time dimension of RNNs and applied similar architectural ideas.
The connection between RNN vanishing gradients and deep network vanishing gradients also explains why ReLU activations helped feedforward networks train more easily. Unlike sigmoid and tanh, the ReLU function has a derivative of exactly 1 for positive inputs and 0 for negative inputs. For active neurons (positive pre-activation), ReLU contributes no damping to the gradient. This eliminates the saturation problem in feedforward networks. However, ReLU does not directly help with vanishing gradients in the time dimension of RNNs, because the issue there is the recurrent weight matrix, not the nonlinearity alone.
The Path to LSTM: What the Architecture Needs
The vanishing gradient problem has practical consequences. It is the reason vanilla RNNs are rarely used in modern NLP despite their elegant formulation. The failure is architectural: there is no mechanism in the basic RNN to independently control how much information is retained versus discarded across time, or how much gradient signal is allowed to flow backward.
Any solution to the vanishing gradient problem must address this root cause. The architecture needs a way to:
- Preserve information over long distances without the information being transformed (and potentially diluted) at every step.
- Selectively update which information in memory is relevant to the current input.
- Control gradient flow such that gradients can propagate backward without being repeatedly multiplied by the same shrinking factors.
The LSTM architecture, introduced by Hochreiter and Schmidhuber in 1997, addresses all three requirements through a combination of a separate memory cell and learnable gate mechanisms. Rather than passing the hidden state through a nonlinearity at every step, the LSTM maintains a cell state that can change by addition rather than multiplication. Because addition does not shrink gradient magnitudes the way multiplication does, gradients can flow backward through the cell state pathway with minimal attenuation.
Why Addition Solves the Problem
To see intuitively why addition helps, consider the gradient through an additive update. Suppose the cell state evolves as for some input-dependent function . The gradient of with respect to is then:
The gradient is identically 1, regardless of sequence length. Information from any timestep is equally accessible via the cell state pathway. The LSTM's actual update is slightly more complex because the forget gate modulates before the addition, but the key insight remains: an additive update path creates a near-constant gradient that does not decay with distance.
This is the conceptual heart of LSTM and, later, of residual connections in deep networks. The additive pathway is a gradient highway, allowing the loss at the final timestep to reach and update weights at any earlier position. The gates in LSTM control what information enters the highway and what exits it, but the highway itself is always open.
We will explore the LSTM architecture in detail in the next chapter, and then examine the mathematics of LSTM gradient flow in the chapter after that. The key insight to carry forward from this chapter is that the vanishing gradient problem is a consequence of the multiplicative gradient chain, and any solution must break or bypass this chain for long-distance signals.
Limitations and Practical Impact
The vanishing gradient problem identified a fundamental tension in sequence modeling that influenced architecture design for decades. Even with LSTMs and GRUs, the problem is mitigated rather than eliminated. Gradients still decay over very long sequences in gated architectures, just much more slowly than in vanilla RNNs. The cell state in LSTM provides a nearly constant gradient path, but the gate activations (which are computed through sigmoid functions) do introduce some damping. In practice, LSTMs can reliably learn dependencies spanning hundreds of steps, compared to a few dozen for vanilla RNNs.
The residual connections in modern transformers address a related problem in deep feedforward networks. The analogy between LSTM's additive cell state and ResNet's skip connections is not coincidental: both architectures create gradient highways that allow signals to bypass the multiplicative chain. This connection is developed further in the Transformer Blocks chapter.
Gradient clipping is a standard companion technique to gated architectures. Even though LSTMs suppress the vanishing problem, exploding gradients remain possible in practice, especially in deep or stacked RNN configurations. Monitoring gradient norms during training remains good practice. A common heuristic is to log the global gradient norm every N steps and watch for sudden spikes or sustained elevation above the typical range.
The vanishing gradient analysis also explains why depth in RNNs is more limited than depth in feedforward networks. A deep RNN must propagate gradients both through time and through layers, compounding the decay problem. A two-layer RNN experiences vanishing in the time dimension (across steps) and the depth dimension (across layers), with both decay processes running simultaneously. Residual connections and highway connections were proposed specifically to make deep RNNs more trainable, though in practice the transformer architecture has superseded most deep RNN designs.
The practical impact on the NLP research community between 1997 and 2017 was enormous. The introduction of LSTMs made previously intractable tasks feasible: machine translation, speech recognition, sequence-to-sequence generation, and question answering all saw significant progress with LSTM-based architectures. Much of the neural NLP work of the early 2010s was built on LSTMs. The limitations that LSTMs did not fully overcome, particularly for very long sequences and for tasks requiring non-local structural reasoning, ultimately motivated the development of attention mechanisms and the transformer architecture.
Understanding the vanishing gradient problem is essential for reading this history. Every architectural innovation from LSTM through GRU through transformers can be understood as a successive response to the gradient propagation challenges that vanilla RNNs exposed. The problem was more than a technical obstacle; it was a conceptual clarification that forced the field to think carefully about what learning across time requires and what properties an architecture must have to support it.
Summary
The vanishing gradient problem in RNNs arises because BPTT requires computing a product of Jacobian matrices, one per timestep between the current position and the position of the loss. Each Jacobian is bounded in spectral radius by the product of the weight matrix and the activation derivative, which is less than 1 for saturating nonlinearities like tanh and sigmoid. The product of many such factors shrinks exponentially with sequence length.
The key takeaways from this chapter are:
- The gradient product bound: decays exponentially when , which holds for all practical initializations with saturating nonlinearities.
- Sigmoid is worse than tanh: sigmoid's maximum derivative of 0.25 accelerates gradient decay compared to tanh's maximum of 1. In a chain of 10 sigmoid steps, the gradient decays by at least a factor of .
- Exploding vs vanishing: exploding gradients are detectable and fixable with gradient clipping; vanishing gradients are silent and architecturally catastrophic. The asymmetry makes vanishing the harder problem.
- Effective gradient distance: vanilla RNNs can only reliably learn dependencies spanning at most a few dozen timesteps, far too short for many practical NLP tasks.
- The credit assignment failure: vanishing gradients cause the network to assign zero credit to distant timesteps, systematically biasing it toward local patterns even when global dependencies are what matter.
- Partial solutions fall short: careful initialization, truncated BPTT, and echo state networks all address symptoms without solving the underlying architectural problem.
- LSTM motivation: the vanishing gradient analysis defines exactly what the LSTM must provide, which is an additive update path for a separate memory cell that allows gradients to bypass the multiplicative decay. Addition distributes gradient uniformly, unlike multiplication which amplifies or shrinks it.
The next chapter introduces the LSTM architecture and shows how the cell state and gate mechanisms provide the solution to everything identified here.
Quiz
Ready to test your understanding? Take this quick quiz to reinforce what you've learned about vanishing gradients in RNNs.
Vanishing Gradients Quiz
Reference
Citation details
Cite or share this article.
Continue with the full handbook
This chapter is part of Language AI Handbook. Use the handbook page to browse the complete table of contents and continue reading in sequence.
Explore Language AI HandbookStay up to date
Get articles, book updates, and news delivered to your inbox.
No spam, unsubscribe anytime.
Join the community
Sign in to remove popups, track your reading progress, and join the discussion.

Comments
No comments yet. Be the first to share your thoughts!