Backpropagation Through Time › Practical Pitfalls and Implementation Advice

Michael BrenndoerferMay 10, 20253 min read
Language AI HandbookAll chapters
Part 11 of 67Recurrent Neural Networks
Chapter 75Backpropagation Through Time › Practical Pitfalls and Implementation Advice
Reading section 12 of 15 · Practical Pitfalls and Implementation Advice · 3 minSection 12 of 15 · 3 min

Book contents

Section 12 of 15 · 3 min read

Backpropagation Through Time › Practical Pitfalls and Implementation AdviceOne section · Sections in this chapter12 / 15 ↓
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

Training RNNs with BPTT involves several common pitfalls that can cause silently incorrect or very slow training.

Forgetting to Detach Between Epochs

When iterating over a dataset multiple times, a common mistake is to carry the hidden state from the end of one epoch into the beginning of the next without detaching it. If the hidden state from epoch nn's last batch is passed directly into epoch n+1n+1's first batch without calling .detach(), the computational graph grows across batches. By the time you call .backward() on the epoch n+1n+1 loss, PyTorch will try to backpropagate through the hidden state all the way back to wherever the graph started in epoch nn. This can lead to memory errors (the graph is unexpectedly large) or incorrect gradients (the graph spans multiple batches of unrelated data).

The safe practice is to always initialize the hidden state fresh at the start of each independent sequence in your dataset, or to call .detach() on any hidden state that is being carried across batch boundaries.

Not Resetting Hidden State Between Sequences

When processing a dataset of multiple independent sequences (documents, sentences, examples), you must reset the hidden state between sequences. Each sequence in the dataset is independent; the hidden state from processing document A should not be used as the initial state for processing document B. Failing to reset leads to a subtle bug where the model's predictions at the start of each sequence are influenced by the content of the previous sequence.

This bug is particularly insidious because the training loss may still decrease: the model might learn to use the leaked context from the previous sequence for certain tasks. But the model's behavior at inference time will be wrong if sequences are processed in a different order or in isolation, because the expected hidden state contamination will not be present.

Exploding Gradients and Gradient Clipping

While vanishing gradients are the chronic problem, exploding gradients are the acute one. A single exploding gradient event can corrupt a model's weights catastrophically, requiring restarting training from a checkpoint. Gradient clipping, covered in detail in the Gradient Clipping chapter, is the standard remedy: before applying the optimizer step, you rescale the global gradient norm to a maximum threshold value if it exceeds that threshold.

In PyTorch, gradient clipping is one line:

In[2]:
Code
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)

This should be called after loss.backward() and before optimizer.step(). The typical threshold for RNN training is 1.0 to 5.0, with 1.0 being a conservative and widely-used default.

The Learning Rate's Interaction with Gradient Scale

The learning rate for RNN training typically needs to be smaller than for feedforward networks, because the gradient scale varies significantly with sequence length. When you increase the sequence length (or chunk size), the gradient norms change, and a learning rate that worked well for short sequences may be too large or too small for longer ones. Using adaptive optimizers like Adam helps, because Adam normalizes gradients per-parameter, but even with Adam, careful learning rate tuning is often necessary when changing the sequence length.

A related consideration is learning rate warmup: starting with a very small learning rate and gradually increasing it to the target value over the first few thousand steps. Warmup helps because early in training, the model's hidden states are poorly initialized and the gradient surface is unstable. Large early gradient steps can send weights to poor regions of parameter space from which recovery is slow.