"On my first day they told me I could represent any function, and I believed them. Stack me deep enough across enough timesteps, they said, and no pattern in the past is beyond my reach. So I reached. I reached back forty steps for the signal that explained today, and somewhere around step fifteen my gradient, the only message that ever tells me how to change, faded to a number so small it rounded to nothing. The early layers never heard the correction. I can represent the long dependency; I simply cannot be taught it. The whole rest of this part is a series of increasingly clever apologies for that one cruel arithmetic fact."
A Freshly Initialized Weight Matrix With High Hopes
Chapter Overview
Part II built its models by hand. An analyst chose an ARIMA order, specified the components of an exponential smoother, wrote down the transition and observation matrices of a Kalman filter, and named the regimes of a hidden Markov model. The structure of the model was a human decision, and the data only filled in the coefficients. Those models are precise, interpretable, and remarkably strong on the clean, low-dimensional, near-linear series they were designed for, which is exactly why they earned six chapters. But the moment the dynamics turn nonlinear in ways no one can write down, or the relevant signal hides in a long, tangled history, or many series interact in patterns too rich to specify, the burden of specifying the model by hand becomes the bottleneck. Part III lifts that burden by learning the structure of the sequence model itself from data. This chapter is where that shift begins, and it lays the groundwork every later architecture in the part will stand on.
The groundwork comes in four moves. First, the chapter takes the humblest neural model, a multilayer perceptron, and slides it along a fixed lag window so that it becomes a nonlinear autoregression, a learned generalization of the linear AR model of Chapter 5 that can capture interactions between lags a linear model never could. Second, it steps back to ask what kinds of sequence problems exist at all: a taxonomy of one-to-one, one-to-many, many-to-one, and many-to-many tasks, the variable-length inputs and outputs that demand padding and masking, and the encoder-decoder bottleneck that will later motivate attention. Third, it confronts the central computational fact of every recurrent model, that a recurrence $h_t = f(h_{t-1}, x_t)$ unrolls into a deep feedforward graph with weights shared across time, and that training it means running the chain rule backward across that graph, the algorithm called backpropagation through time. Fourth, it follows that backward pass to its painful conclusion.
That conclusion is the vanishing and exploding gradient problem, and it is the hinge of the whole part. When the gradient of a loss at time $t$ flows back to an input many steps earlier, it accumulates as a product of Jacobians, one per timestep, and a long product of matrices either collapses toward zero or blows up toward infinity at a rate set by the recurrent Jacobian's spectral radius. The consequence is stark: a vanilla recurrence can represent a long-range dependency in principle, yet cannot be taught it in practice, because the gradient carrying the lesson dies on the way back. Almost every architecture in the chapters that follow is, at heart, a different engineered answer to that single problem. The gated cells of Chapter 10 add a protected carry path; the dilated convolutions of Chapter 11 reach far back in few steps; the attention of Chapter 12 connects any two timesteps directly; the structured state-space models of Chapter 13 design their recurrence to keep the spectral radius near one by construction. Reading the rest of Part III is largely a matter of asking, of each new design, how it keeps the gradient alive.
The temporal thread runs straight through this chapter. Section 7.6 closed Part II with a precise claim: the Kalman filter, the HMM forward pass, and the exponential smoother are all special cases of one recurrence that carries a state forward, updates it with each observation, and reads out a prediction. That recurrence was handed to the filter as known matrices. Here the same recurrence becomes trainable. The transition and readout are no longer specified by an analyst; they are parameters, and backpropagation through time is the machinery that learns them. The recurrent network is the Kalman filter with its matrices set free, and this chapter is where we discover both the power of that freedom and the gradient pathology that is its price. Following the Part III convention, the worked examples lean on synthetic long-dependency tasks where the right answer is known exactly, so the gradient's decay can be measured rather than guessed.
Prerequisites
This chapter sits at the seam between classical and neural sequence modeling, so it draws on both sides. From Part II you need two anchors. From Chapter 5: Univariate Forecasting Models you need the linear autoregression and the forecasting baselines, because the lag-window network of Section 9.1 is the nonlinear generalization of AR and is judged against those baselines; a neural forecaster that cannot beat a well-tuned classical model has not earned its complexity. From Chapter 7: State-Space Models and Filtering, and especially its closing recurrence bridge, you need the idea that a filter is a state carried forward and updated, since this chapter makes that exact recurrence trainable. From Chapter 2: Temporal Data Engineering you need windowing and the discipline of the temporal train-validation-test split, because every network here is trained on windows and every leak of future information silently inflates its scores. Beyond the time-series prerequisites you need a working knowledge of neural networks and PyTorch: what a multilayer perceptron and an activation function are, how a loss is minimized by gradient descent, and how automatic differentiation computes gradients. Readers who want a refresher on optimization and deep learning basics or on PyTorch itself will find Appendix C and Appendix D, along with all referenced chapters, indexed in the Table of Contents.
If you keep one idea from this chapter, keep this: every temporal deep architecture in this book is an answer to one question, how do you let information from far in the past reach the present without the gradient vanishing on the way back? A neural network slid along a lag window is a nonlinear autoregression, strictly more expressive than the linear AR of Chapter 5. A recurrence is a deep feedforward graph with weights shared across time, and training it means running the chain rule backward through every unrolled step, which is backpropagation through time. That backward pass multiplies one Jacobian per timestep, and a long product of matrices either vanishes toward zero or explodes toward infinity at a rate set by the recurrent Jacobian's spectral radius, so a vanilla recurrence can represent a long dependency but cannot be taught it. Hold that fact, and the gated cells, dilated convolutions, attention layers, and structured state-space models of the chapters ahead read as what they are: a sequence of escalating, increasingly elegant ways to keep the gradient alive across time.
Chapter Roadmap
- 9.1 Feedforward Networks for Time Series How a multilayer perceptron slid over a fixed lag window becomes a nonlinear autoregression, why its nonlinearity buys the lag interactions a linear AR model cannot capture, what the universal-approximation theorem does and does not promise for sequences, the training essentials that respect the time split, and a from-scratch numpy forecaster paired with its PyTorch equivalent.
- 9.2 Sequence Learning Concepts and Tasks The taxonomy of sequence tasks (one-to-one, one-to-many, many-to-one, and aligned and unaligned many-to-many), variable-length inputs and outputs with padding and masking, the losses and targets of temporal learning, conditioning on static and covariate features, and the encoder-decoder bottleneck that will later motivate attention, with a runnable PyTorch masking pipeline.
- 9.3 Backpropagation Through Time How the recurrence $h_t = f(h_{t-1}, x_t)$ unrolls into a deep feedforward graph with shared weights, how the chain rule carries gradients backward across timesteps as a sum of products of Jacobians, why truncated BPTT trades bias for cost and memory, and a from-scratch numpy implementation matched gradient-for-gradient against PyTorch autograd.
- 9.4 Long-Term Dependencies and Gradient Pathologies Why information from far in the past is hard for a recurrence to use, how the product of Jacobians makes gradients vanish or explode at a rate set by the recurrent Jacobian's spectral radius, how to diagnose the characteristic exponential decay, and the toolkit (clipping, orthogonal initialization, nonsaturating activations, normalization, skip connections, and gated carry paths) that the rest of Part III is built to provide.
Once you have worked through the four sections, the Hands-On Lab below chains them into a single experiment. Each lab step maps to a section, so the lab doubles as a review: by the time you finish you will have built the lag-window forecaster of Section 9.1, framed a long-dependency task in the taxonomy of Section 9.2, run backpropagation through time on a recurrence from Section 9.3, and measured, then fought, the gradient decay that Section 9.4 predicts.
Hands-On Lab: Feel the Gradient Vanish
Objective
Make the central pathology of this chapter visible, measurable, and then partly curable, all on one small problem you can run on a laptop CPU. You will construct a synthetic long-dependency task, a delayed-copy problem in which the target at the end of a sequence is a value injected far back at the beginning, with everything in between pure distraction, so the only way to score well is to carry information across the full gap. First you will train a sliding-window multilayer perceptron forecaster, the nonlinear autoregression of Section 9.1, and watch it succeed when the dependency fits inside its window and fail the instant the relevant value sits one step beyond the window's edge, which makes the case for a model that carries a state. Then you will build a vanilla recurrent network for the same task and train it with backpropagation through time, the algorithm of Section 9.3. The heart of the lab is the measurement: you will record the norm of the gradient of the final loss with respect to the hidden state at each earlier timestep, plot it against the distance back through the sequence, and see the characteristic exponential decay of Section 9.4 with your own eyes, gradients from twenty steps back arriving orders of magnitude smaller than gradients from one step back. Finally you will reach into the Section 9.4 toolkit, applying gradient clipping to tame the occasional explosion and orthogonal initialization of the recurrent weights to push the spectral radius toward one, and confirm that while these tricks help, the vanilla recurrent network still struggles on the longest gaps. That residual difficulty is the point: it is precisely the motivation for the gated cells of Chapter 10, and you will have earned the motivation rather than been told it.
What You'll Practice
- Building a sliding-window multilayer perceptron forecaster in PyTorch and finding the window edge where it can no longer reach the relevant lag, following Section 9.1.
- Framing a delayed-copy task as a many-to-one sequence problem with the right inputs, target, and loss, following Section 9.2.
- Training a vanilla recurrent network with backpropagation through time and inspecting per-timestep gradients, following Section 9.3.
- Measuring gradient-norm decay against sequence length and plotting the exponential signature of the vanishing gradient, following Section 9.4.
- Applying gradient clipping and orthogonal initialization, and confirming the long-dependency task stays hard for the vanilla cell, following Section 9.4.
Setup
You need PyTorch for both models and for the autograd that supplies the per-timestep gradients, NumPy to generate the synthetic delayed-copy sequences, and Matplotlib to plot the gradient-norm decay curve. No dataset download is required; the task is generated in a few lines so the lab runs out of the box, and a real long-dependency benchmark is available from the Monash Forecasting Archive linked in the bibliography when you want to repeat the experiment on field data. Every model is trained and evaluated on disjoint sequences and respects the temporal split discipline of Chapter 2, so a strong score reflects real long-range carry rather than a leak of the answer.
import torch, torch.nn as nn, numpy as np
def delayed_copy(n_seq, length, gap):
"""Target = a value injected 'gap' steps before the end; the rest is noise."""
x = torch.randn(n_seq, length, 1) # distractor signal
signal = torch.randn(n_seq, 1) # the value to carry
x[:, length - gap, 0] = signal.squeeze() # inject it far from the end
y = signal # many-to-one target read at the last step
return x, y # only long-range carry can solve this
Steps
Step 1: Build the sliding-window MLP forecaster and find its window edge
Start with the lag-window multilayer perceptron of Section 9.1, the nonlinear autoregression. Flatten a fixed window of recent timesteps into a vector and feed it to a small MLP that predicts the target. Train it on the delayed-copy task with the injected value sitting inside the window, and confirm it solves the task easily. Then move the value one step beyond the window's edge and watch the score collapse: a fixed-window model cannot see a lag it does not include, which is the structural reason a model needs to carry a state rather than re-read a fixed slice.
class WindowMLP(nn.Module):
def __init__(self, window, hidden=64):
super().__init__()
self.net = nn.Sequential(nn.Linear(window, hidden), nn.ReLU(),
nn.Linear(hidden, 1))
def forward(self, x): # x: (batch, window, 1)
return self.net(x.squeeze(-1)) # flatten the window, predict the target
Step 2: Frame the task and build a vanilla RNN
Cast the delayed-copy problem as the many-to-one sequence task of Section 9.2: a variable-length input sequence in, a single value out, read from the final hidden state. Build a minimal vanilla recurrent network that carries a hidden state across the whole sequence, the trainable form of the filter recurrence from Section 7.6. Unlike the windowed MLP, this model has, in principle, an unbounded reach: its hidden state can hold the injected value for as long as it learns to.
class VanillaRNN(nn.Module):
def __init__(self, hidden=64):
super().__init__()
self.rnn = nn.RNN(input_size=1, hidden_size=hidden, batch_first=True)
self.head = nn.Linear(hidden, 1)
def forward(self, x): # x: (batch, length, 1)
out, h = self.rnn(x) # carry a state across every timestep
return self.head(h[-1]) # read the target from the last hidden state
Step 3: Train with backpropagation through time and read per-timestep gradients
Train the recurrent network with backpropagation through time, the unrolled chain rule of Section 9.3. PyTorch autograd performs the unrolling for you, but you can expose its inner workings: retain the gradient on the hidden state at each timestep and, after one backward pass, read the gradient norm flowing into each step. This is the raw material for the measurement that makes the chapter concrete, the message the early timesteps actually receive.
model = VanillaRNN()
opt = torch.optim.Adam(model.parameters(), lr=1e-3)
x, y = delayed_copy(256, length=40, gap=35)
loss = nn.functional.mse_loss(model(x), y)
loss.backward() # BPTT: chain rule across all 40 steps
# inspect grad norms per timestep via hooks on the unrolled hidden states
Step 4: Measure and plot the gradient-norm decay
Now make the pathology visible, following Section 9.4. Sweep the gap of the delayed-copy task from short to long, and for each gap record the norm of the gradient of the final loss with respect to the hidden state as a function of how many steps back you go. Plot gradient norm against distance on a log scale. You will see a near-straight line sloping down, the exponential decay set by the recurrent Jacobian's spectral radius, and you will read off, concretely, that the gradient from twenty or thirty steps back is orders of magnitude weaker than the gradient from one step back. The early timesteps barely hear the correction, which is exactly why the network fails to learn the long copy.
Step 5: Apply the toolkit and confirm the limit
Reach into the Section 9.4 toolkit and see how far it gets you. Add gradient clipping to cap the rare exploding update, and initialize the recurrent weight matrix orthogonally so its spectral radius starts near one, slowing the vanishing. Retrain and re-measure: the decay curve flattens somewhat and training is steadier, yet on the longest gaps the vanilla cell still cannot reliably carry the value to the end. Record where it breaks. That ceiling is the lab's punchline and the bridge to the next chapter: clipping and orthogonal init buy you some range, but a fundamentally different mechanism, a protected gated carry path, is needed to cross the longest gaps, and that mechanism is the subject of Chapter 10.
nn.init.orthogonal_(model.rnn.weight_hh_l0) # spectral radius near one slows vanishing
# inside the training loop, after loss.backward():
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0) # tame exploding gradients
Expected Output
The lab produces a story in three plots rather than a single score. The windowed MLP of Step 1 nails the task while the injected value sits inside its window and falls to chance the moment it slips one step beyond the edge, a clean demonstration that a fixed receptive field has a hard horizon. The vanilla recurrent network of Steps 2 and 3 has no such fixed horizon, yet the gradient-decay plot of Step 4 shows why that freedom is hollow for long gaps: gradient norm against distance is a near-straight downward line on a log scale, the exponential signature of the vanishing gradient, with the gradient from thirty steps back typically several orders of magnitude below the gradient from one step back. The toolkit of Step 5 visibly flattens that curve and removes the loss spikes from exploding gradients, but the network's accuracy on the longest-gap copy stays poor, so the curative trick is partial by design. The reader finishes with a measured, plotted, hands-on understanding that a vanilla recurrence can represent a long dependency yet cannot be taught it, and with the exact problem statement the gated architectures of Chapter 10 were invented to solve.
The lab needs only the PyTorch standard library. nn.RNN supplies the vanilla recurrent cell with the unrolling and backpropagation through time handled by autograd, so you never write the backward pass by hand once Section 9.3 has shown you what it computes. torch.nn.utils.clip_grad_norm_ implements gradient clipping in one line, and nn.init.orthogonal_ gives the orthogonal initialization that nudges the recurrent spectral radius toward one, the two Section 9.4 remedies with no bespoke code at all. Per-timestep gradient inspection, the one part with no off-the-shelf call, is a few lines of tensor hooks on the retained hidden states. Write the recurrence and its backward pass from scratch in numpy once, as Section 9.3 does, so you know exactly what autograd is doing, then let PyTorch carry every experiment from here to the end of the part.
Stretch Goals
- Replace the delayed-copy target with the adding problem (sum two marked values far apart in a long sequence) and confirm the same gradient decay, a harder long-dependency task in the spirit of Section 9.4.
- Swap the recurrence's $\tanh$ activation for a nonsaturating one and remeasure the decay slope, testing the activation lever of the Section 9.4 toolkit.
- Vary the recurrent weight scale to push the spectral radius above and below one, and watch the gradient cross from exploding to vanishing, the spectral-radius mechanism of Section 9.4 made directly visible.
What's Next?
This chapter ended on a problem it could diagnose but not cure: a vanilla recurrence can represent a long-range dependency, yet backpropagation through time cannot teach it one, because the gradient vanishes across the gap. Chapter 10: Recurrent Neural Networks supplies the first and most influential answer. The Long Short-Term Memory cell and the Gated Recurrent Unit add a protected carry path, a cell state that information can ride across many timesteps with its gradient kept near one by learned gates that decide what to remember, what to forget, and what to expose. They are the gated answer to the exact arithmetic fact this chapter measured, and they turn the recurrence from a fragile idea into the workhorse that dominated sequence modeling for a decade. The hidden state stays central, the carry-update-read shape inherited from the Kalman filter of Chapter 7 stays central; Chapter 10 simply gives the recurrence a way to keep the gradient alive long enough to learn the dependency this chapter showed it could not.
Bibliography & Further Reading
Foundational Papers
Werbos, P. J. "Backpropagation Through Time: What It Does and How to Do It." Proceedings of the IEEE, 78(10), 1990. ieeexplore.ieee.org
The paper that named and systematized backpropagation through time, the unrolled chain rule across a shared-weight recurrence that is the entire subject of Section 9.3.
Bengio, Y., Simard, P., Frasconi, P. "Learning Long-Term Dependencies with Gradient Descent is Difficult." IEEE Transactions on Neural Networks, 5(2), 1994. ieeexplore.ieee.org
The result that proved the vanishing-gradient problem is intrinsic to training recurrences over long horizons, the formal core of Section 9.4 and the reason the rest of Part III exists.
Hochreiter, S. "Untersuchungen zu dynamischen neuronalen Netzen." Diploma thesis, Technische Universität München, 1991. people.idsia.ch
The original analysis of the vanishing and exploding gradient, the diploma thesis that first identified the pathology Section 9.4 measures, predating the architectures built to defeat it.
Pascanu, R., Mikolov, T., Bengio, Y. "On the Difficulty of Training Recurrent Neural Networks." ICML, 2013. arXiv:1211.5063. arxiv.org/abs/1211.5063
The modern treatment linking the vanishing and exploding gradient to the recurrent Jacobian's spectral radius and prescribing gradient clipping, the analysis and remedy at the heart of Section 9.4 and the lab.
Le, Q. V., Jaitly, N., Hinton, G. E. "A Simple Way to Initialize Recurrent Networks of Rectified Linear Units." 2015. arXiv:1504.00941. arxiv.org/abs/1504.00941
The IRNN paper showing that identity and orthogonal initialization plus ReLU lets a vanilla recurrence carry signal far further, the initialization lever the lab's Step 5 exercises.
Key Books
Goodfellow, I., Bengio, Y., Courville, A. "Deep Learning," Chapter 10: Sequence Modeling. MIT Press, 2016 (free online). deeplearningbook.org
The standard textbook account of recurrent networks, backpropagation through time, and the long-term-dependency problem, the single best companion to all four sections of this chapter.
Zhang, A., Lipton, Z. C., Li, M., Smola, A. J. "Dive into Deep Learning," sequence and recurrent-network chapters. 2023 (free, runnable). d2l.ai
An interactive, code-first development of sequence models, backpropagation through time, and gradient clipping, a runnable counterpart to the from-scratch builds of Sections 9.1 and 9.3.
Tools & Libraries
PyTorch: tensors, autograd, and neural-network modules. Official documentation. pytorch.org/docs
The framework behind every model in this chapter; its autograd performs the backpropagation through time of Section 9.3 and supplies nn.RNN, gradient clipping, and orthogonal init for the lab.
PyTorch sequence-modeling tutorials (sequence models and the NLP-from-scratch series). pytorch.org/tutorials
Step-by-step recurrent-network tutorials in PyTorch, the gentlest hands-on path from the concepts of Section 9.2 to a working training loop.
Tutorials
Karpathy, A. "The Unreasonable Effectiveness of Recurrent Neural Networks." Blog, 2015. karpathy.github.io
The essay that made recurrent networks vivid for a generation, with a clear intuition for sequence tasks and the unrolled recurrence that grounds Sections 9.2 and 9.3.
Olah, C. "Understanding LSTM Networks" (and its recurrent-network preamble). colah's blog, 2015. colah.github.io
A widely cited visual explanation that opens with the vanilla recurrence and its long-dependency failure, the exact problem of Section 9.4 and the on-ramp to Chapter 10.
Datasets & Benchmarks
Godahewa, R., Bergmeir, C., Webb, G. I., Hyndman, R. J., Montero-Manso, P. "Monash Time Series Forecasting Archive." NeurIPS Datasets and Benchmarks, 2021. forecastingdata.org
A large, curated collection of real forecasting datasets across many domains and frequencies, the field-data benchmark for extending the lag-window forecaster of Section 9.1 beyond synthetic tasks.