Vanishing Gradients › Visualizing Gradient Flow Through Time
Language AI HandbookAll chapters Vanishing Gradients › Visualizing Gradient Flow Through TimeOne section · Sections in this chapter8 / 18 ↓
Reading tools Definitions & inline links
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
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.