Contents Language Models Code Course home

Chapter 12Recurrent Networks

A window that will not close

Chapter 11 fixed one blindness and left the other in plain sight. The feed-forward language model concatenates n−1n-1 embeddings into a single vector, that vector is its whole memory, and its length is decided before training begins. So the model reads a sentence through a slot of fixed width, and everything outside falls away exactly as it did for the counter.

Consider “the book that the committee rejected was short”, where the verb agrees with book eight words back and a window of three cannot see it. Widening the window is not a fix, because the hidden matrix WhW_h has shape (n−1)d×H(n-1)d \times H and its size grows with every word you admit. And no fixed width is ever wide enough, since sentences have no maximum length.

What we want is a model that reads left to right, keeping a running summary of everything so far and updating it at each word. The summary is a state and the update rule is a recurrence.

The recurrence

A recurrent neural network keeps a hidden state hth_t and rewrites it at every time step:

ht=tanh⁡(Wxt+Uht−1+bh),ŷt=softmax⁡(Vht+by).\begin{align} h_t &= \tanh\!\big(W\,x_t + U\,h_{t-1} + b_h\big), \label{eq:rnn-state}\\ \hat{y}_t &= \operatorname{softmax}\!\big(V\,h_t + b_y\big). \label{eq:rnn-out} \end{align}

Here xtx_t is the embedding of the word read at step tt, while h0h_0 is conventionally the zero vector.

A recurrent network drawn as a loop and as an unrolled chain. The loop is the definition. The chain is what backpropagation sees. Both show the same three matrices used at every step.

Three matrices carry the whole model, where WW maps the current word into the state, UU maps the previous state into the new one, and VV maps the state to a score for every vocabulary word.

The important word is same, because the same WW, UU and VV are used at every step however long the sentence, and that reuse is what recurrent means.

Equation (12.8) therefore does something the feed-forward model could not, since ht−1h_{t-1} was itself computed from ht−2h_{t-2} and so on back to the first word. The state at step tt depends on every word read so far, so there is no window and no cliff.

Unrolling, worked

Take a model small enough to print, with two hidden units, embeddings of size two and a vocabulary of three words.

W=(0.6−0.30.20.5),U=(0.40.1−0.20.7),bh=(0.05,−0.05).W = \begin{pmatrix} 0.6 & -0.3 \\ 0.2 & 0.5 \end{pmatrix}, \quad U = \begin{pmatrix} 0.4 & 0.1 \\ -0.2 & 0.7 \end{pmatrix}, \quad b_h = (0.05,\, -0.05).

Read the sentence the dog ran, starting from h0=(0,0)h_0 = (0, 0).

tt word xtx_t Wxt+Uht−1+bhWx_t + Uh_{t-1} + b_h hth_t
1 the (+0.5000,−0.2000)(+0.5000, -0.2000) (+0.4100,−0.0500)(+0.4100, -0.0500) (+0.3885,−0.0500)(+0.3885, -0.0500)
2 dog (+0.1000,+0.8000)(+0.1000, +0.8000) (+0.0204,+0.2573)(+0.0204, +0.2573) (+0.0204,+0.2518)(+0.0204, +0.2518)
3 ran (−0.6000,+0.3000)(-0.6000, +0.3000) (−0.3667,+0.1522)(-0.3667, +0.1522) (−0.3511,+0.1510)(-0.3511, +0.1510)

Follow the third row, whose input column depends only on ran while its hth_t depends on h2h_2, which depended on h1h_1, which depended on the. So h3h_3 is a summary of the whole prefix, nothing was thrown away, and no parameter was added to make room for it.

Why tanh\tanh

The lectures ask why tanh\tanh rather than the sigmoid, and the answer is worth keeping. tanh\tanh is symmetric about the origin so its output is zero-centred, and states that average to zero train faster than states that average to 0.50.5. Its derivative is also steeper, since the sigmoid’s lies in [0,0.25][0, 0.25] while tanh\tanh’s lies in [0,1][0, 1].

A derivative capped at 0.250.25 shrinks the gradient by at least a factor of four at every step, and Section 12.5 shows why that is fatal, with tanh\tanh merely delaying the problem rather than removing it.

Modern architectures mostly use ReLU and its variants, which pass gradient unchanged on the positive side, though ReLU has its own failure in that a unit whose input is always negative stops passing any gradient at all and never recovers. Leaky ReLU, max⁡(αx,x)\max(\alpha x, x) with small α\alpha, exists to prevent that.

An RNN is a language model

Equation (12.9) already produces one distribution over the vocabulary per time step, which is exactly what a language model needs. Train it by predicting the next word at every position, where the loss at step tt is the cross-entropy of Chapter 11 and the loss for a sentence is their sum:

Et=−∑kyt,klog⁡ŷt,k,ℒ=∑tEt.(12.1)\begin{equation} E_t \;=\; -\sum_k y_{t,k} \log \hat{y}_{t,k}, \qquad \mathcal{L} \;=\; \sum_t E_t . \label{eq:rnn-loss} \quad\text{(12.1)} \end{equation}

Everything from Chapter 10 carries over, since the task, the maximum likelihood objective and the perplexity of Section that section are all untouched. Only the machinery producing ŷt\hat{y}_t has changed again.

The parameter count stops growing

The clearest argument for the recurrence is a table of shapes. Take |V|=10,000|V| = 10{,}000, embeddings of size d=100d = 100 and H=500H = 500 hidden units, which are the lecture’s numbers.

matrix shape parameters
EE 10,000×10010{,}000 \times 100 1,000,000
WW 500×100500 \times 100 50,000
UU 500×500500 \times 500 250,000
VV 500×10,000500 \times 10{,}000 5,000,000
biases 500+10,000500 + 10{,}000 10,500
total 6,310,500

Not one of those shapes mentions the length of the sentence. Compare the feed-forward model, whose WhW_h has shape (n−1)d×H(n-1)d \times H and therefore grows with every word of context admitted.

context words feed-forward WhW_h recurrent UU
3 150,000 250,000
5 250,000 250,000
10 500,000 250,000
50 2,500,000 250,000
500 25,000,000 250,000

At a context of three the feed-forward model is actually smaller, the two cross over at five, and after that the comparison is not close. The left column grows without limit while the right column is a constant, and that one fact is what lets a recurrent network read a paragraph.

Backpropagation through time

Training needs gradients, and the loop has to be flattened before the chain rule can be applied. Unroll the network and a sentence of TT words becomes a feed-forward network TT layers deep, in which every layer shares the same weights.

The unrolled network. Each step contributes its own error, and every step uses the same three matrices. Gradients from a later step must travel back through every intermediate state to reach an earlier one.

This is backpropagation through time, and the algorithm is ordinary backpropagation on the unrolled graph, with the lectures working the three derivatives out in full. The output matrix is the easy one, because with a softmax and cross-entropy the error signal is again prediction minus target:

∂Et∂V=δouttht⊤,δoutt=ŷt−yt.(12.2)\begin{equation} \frac{\partial E_t}{\partial V} \;=\; \delta_{\text{out}}^{t}\, h_t^{\top}, \qquad \delta_{\text{out}}^{t} \;=\; \hat{y}_t - y_t . \label{eq:bptt-v} \quad\text{(12.2)} \end{equation}

The recurrent matrix needs one more link, through the tanh\tanh:

∂Et∂U=[(V⊤(ŷt−yt))⊙(1−ht2)]ht−1⊤,(12.3)\begin{equation} \frac{\partial E_t}{\partial U} \;=\; \Big[\big(V^{\top}(\hat{y}_t - y_t)\big) \odot (1 - h_t^2)\Big] h_{t-1}^{\top}, \label{eq:bptt-u} \quad\text{(12.3)} \end{equation}

where ⊙\odot is elementwise multiplication and 1−ht21 - h_t^2 is the derivative of tanh\tanh. The input matrix is identical in form, with xtx_t in place of ht−1h_{t-1}:

∂Et∂W=[(V⊤(ŷt−yt))⊙(1−ht2)]xt⊤.(12.4)\begin{equation} \frac{\partial E_t}{\partial W} \;=\; \Big[\big(V^{\top}(\hat{y}_t - y_t)\big) \odot (1 - h_t^2)\Big] x_t^{\top}. \label{eq:bptt-w} \quad\text{(12.4)} \end{equation}

Because the weights are shared, the gradient for one matrix is the sum of its contributions from every time step, and that sum is where the trouble starts.

Why the memory fades

Take an error at step τ\tau and ask what it says about the state at step 00, where the chain rule gives one factor per step in between:

∂E[τ]∂h[0]=∂E[τ]∂h[τ]∏t=1τ∂h[t]∂h[t−1].(12.5)\begin{equation} \frac{\partial E^{[\tau]}}{\partial h^{[0]}} \;=\; \frac{\partial E^{[\tau]}}{\partial h^{[\tau]}} \prod_{t=1}^{\tau} \frac{\partial h^{[t]}}{\partial h^{[t-1]}} . \label{eq:bptt-product} \quad\text{(12.5)} \end{equation}

Each factor is a Jacobian, and Equation (12.8) says what one contains:

∂h[t]∂h[t−1]=diag⁡(tanh⁡′(⋅))U.(12.6)\begin{equation} \frac{\partial h^{[t]}}{\partial h^{[t-1]}} \;=\; \operatorname{diag}\!\big(\tanh'(\cdot)\big)\, U . \label{eq:bptt-jacobian} \quad\text{(12.6)} \end{equation}

So the product in Equation (12.5) behaves like UU raised to the power τ\tau, with the tanh\tanh derivative damping it at each step.

The dependency chain the gradient must travel. The error at step five depends on h[5]h^{[5]}, which depends on h[4]h^{[4]}, and so on back to h[0]h^{[0]}. Every arrow contributes one Jacobian factor.

Repeated multiplication by a matrix is governed by its eigenvalues, and the outcome is binary. If every eigenvalue of UU satisfies |λ|<1|\lambda| < 1 then the product shrinks towards zero and gradients vanish, while if any eigenvalue satisfies |λ|>1|\lambda| > 1 then the product grows without bound and gradients explode. Only |λ|=1|\lambda| = 1 survives, and nothing in training holds it there.

How fast, in numbers

Treat the per-step factor as a single number and raise it to the distance travelled.

distance ×0.5\times 0.5 ×0.9\times 0.9 ×1.0\times 1.0 ×1.1\times 1.1 ×1.2\times 1.2
1 5.00×10−15.00 \times 10^{-1} 9.00×10−19.00 \times 10^{-1} 1.00 1.10 1.20
5 3.13×10−23.13 \times 10^{-2} 5.91×10−15.91 \times 10^{-1} 1.00 1.61 2.49
10 9.77×10−49.77 \times 10^{-4} 3.49×10−13.49 \times 10^{-1} 1.00 2.59 6.19
20 9.54×10−79.54 \times 10^{-7} 1.22×10−11.22 \times 10^{-1} 1.00 6.73 3.83×1013.83 \times 10^{1}
47 7.11×10−157.11 \times 10^{-15} 7.07×10−37.07 \times 10^{-3} 1.00 8.82×1018.82 \times 10^{1} 5.27×1035.27 \times 10^{3}
100 7.89×10−317.89 \times 10^{-31} 2.66×10−52.66 \times 10^{-5} 1.00 1.38×1041.38 \times 10^{4} 8.28×1078.28 \times 10^{7}

Notice how mild the failing factor is, because a per-step multiplier of 0.90.9 looks harmless and yet after fifty steps it has removed more than 9999 per cent of the signal.

The forty-seven word example

The lectures make it concrete, so take this passage.

Raj entered CoffeeDay to meet his partner Dru. Raj said “Hi Dru”. In the next few hours they discussed their start-up and devised a plan to develop a product on knowledge management. After a long and fruitful discussion, Raj said goodbye to his .

The answer is partner. The passage runs to 4444 words, so the evidence is 3737 words behind the gap, and once the punctuation is counted as tokens, which is what the model actually steps over, the distance is 4646. Call it some 4747 steps and here is what reaches back that far.

0.0147=1.0×10−94,0.547=7.1×10−15,1.247=5266.46.0.01^{47} = 1.0 \times 10^{-94}, \qquad 0.5^{47} = 7.1 \times 10^{-15}, \qquad 1.2^{47} = 5266.46 .

A gradient of 10−9410^{-94} is no update at all, so the weights that would have captured the link never move and the network cannot learn the dependency however reliably it holds.

One point deserves emphasis because it is easy to misread. The forward pass is fine and information does flow from the first step to the last. It is the backward pass that fails, since the network cannot assign blame across that distance and so never learns to use the information it is already carrying.

Two partial repairs

The two failures are not equally hard, and it is worth being clear about which one has a cheap fix.

Gradient clipping

Exploding gradients have a one line remedy, which is that if the gradient is longer than a threshold you rescale it while keeping its direction:

if ‖g‖>threshold,g←threshold‖g‖g.(12.7)\begin{equation} \text{if } \lVert g \rVert > \text{threshold}, \quad g \;\leftarrow\; \frac{\text{threshold}}{\lVert g \rVert}\, g . \label{eq:clipping} \quad\text{(12.7)} \end{equation}

step ‖g‖\lVert g \rVert before ‖g‖\lVert g \rVert after direction kept
1 0.3735 0.3735 1.0000
2 2.7058 2.7058 1.0000
3 8.4526 5.0000 1.0000
4 77.0045 5.0000 1.0000
5 1418.8820 5.0000 1.0000

The last column is 11 every time, because clipping changes how far the step goes and never which way it points, which is why it costs nothing in quality.

It does nothing whatever for vanishing gradients, where the problem is that the number is already too small and scaling it up would amplify noise along with signal.

Truncated backpropagation

Unrolling a sequence of thousands of steps is expensive in memory and slow, so truncated backpropagation through time splits the sequence into fixed segments and backpropagates within each, turning a sequence of 50005000 samples into 5050 segments of 100100.

The cost is honest and worth stating, because any dependency spanning a segment boundary is invisible to training, and choosing the boundaries at sentence ends reduces the damage without removing it.

Truncation is therefore a concession rather than a cure. It makes long sequences trainable by giving up on long dependencies, which were the reason for the recurrence in the first place.

Reading both ways

One more variant is worth naming, because it answers a limitation that has nothing to do with gradients. An RNN reads left to right, so hth_t summarises the past and knows nothing of the future, and for tagging a word that is often the wrong half of the sentence.

A bidirectional RNN (Schuster and Paliwal 1997) runs two independent recurrences, one forward and one backward, then concatenates their states at each position so that every output sees the whole sentence.

A bidirectional recurrent network. One recurrence runs left to right and another right to left. The state at each position is the concatenation of the two, so it summarises the whole sentence rather than only its prefix.

The restriction is obvious once stated. A bidirectional model cannot generate text, because the backward pass would need words that have not been produced yet, so it is for labelling rather than prediction. That distinction returns in Chapter 18, where BERT reads both ways and GPT reads one way, for exactly this reason.

Try it yourself. code/worked_examples/rnn.py produces every table in this chapter. --unroll prints the three step trace, --params the two parameter tables, --vanish the decay table and the forty-seven word arithmetic, and --clip the clipping demonstration with its unchanged directions.

Run it in ColabNotebookSource

What the RNN did and did not buy

The window is gone. State carries an unbounded prefix, the parameter count no longer depends on sentence length, and the same three matrices serve a sentence of any size.

In exchange we inherited a new failure, and it is specific rather than vague, since gradients decay geometrically with distance. The network therefore cannot be trained to use information from far back, even though it is perfectly capable of carrying it. Clipping handles one half and truncation manages the cost, while neither touches the vanishing case.

The repair has to be architectural. Multiplying by UU at every step is what destroys the gradient, so the state needs a path that does not pass through that multiplication. That path is a gate, and building one is the subject of Chapter 13.

Further reading.

Elman (1990) is the original recurrent network for language and remains the clearest short introduction. Rumelhart et al. (1986) introduced backpropagation, which backpropagation through time simply applies to an unrolled graph. Bengio et al. (1994) first proved that gradient descent cannot learn long-term dependencies in this architecture, and Pascanu et al. (2013) analyse both failures and propose clipping. Karpathy et al. (2015) inspect what a trained recurrent network actually stores in its state, and it is unusually readable. In a framework the recurrence of this chapter is torch.nn.RNN, which runs the same three lines over a whole batch of sequences at once. Graves (2014) shows what these models could already do with sequences.

Unroll by hand. Using the two by two matrices of this chapter, compute h1h_1, h2h_2 and h3h_3 for the sentence the dog ran and confirm the table. Then recompute with UU set to the zero matrix. What has the model become, and which chapter was it?

Count the parameters. For |V|=10,000|V| = 10{,}000, d=100d = 100 and H=500H = 500, confirm the total of 6,310,5006{,}310{,}500. Which matrix dominates, and what would you change first to shrink the model? Now find the context length at which a feed-forward WhW_h overtakes the recurrent UU.

Derive the third gradient. Equations (12.3) and (12.4) differ in exactly one factor. Derive Equation (12.4) from Equation (12.8) and say why that one factor is the only difference.

Measure the decay. For per-step factors of 0.90.9 and 1.11.1, compute the gradient magnitude after 1010, 5050 and 100100 steps. At which distance does the 0.90.9 case fall below single precision, roughly 10−3810^{-38}? State what that means for a model trying to learn a dependency at that range.

Why tanh\tanh and not sigmoid. The sigmoid derivative is bounded by 0.250.25 and the tanh\tanh derivative by 11. Compute the gradient surviving 2020 steps under each bound. Then explain why tanh\tanh delays the vanishing problem without solving it.

Clipping preserves direction. Show from Equation (12.7) that the clipped gradient is a positive multiple of the original, so the cosine between them is exactly 11. Then explain in one sentence why the same trick cannot rescue a vanishing gradient.

The cost of truncation. A corpus is split into segments of 100100 tokens for truncated backpropagation. Describe a linguistic dependency that this makes unlearnable, and propose a segmentation rule that reduces the damage. What does your rule cost?

Hand your weights to the library. Load this chapter’s WW, UU and bhb_h into a torch.nn.RNN with two inputs, two hidden units and tanh, feed it the three word sequence, and confirm that it returns h1=(0.3885,−0.0500)h_1 = (0.3885, -0.0500), h2=(0.0204,0.2518)h_2 = (0.0204, 0.2518) and h3=(−0.3511,0.1510)h_3 = (-0.3511, 0.1510). You will not get them on the first attempt, because the module holds two bias vectors where the equation above holds one. Find the second bias, say what it is for, and explain why leaving it at its initial value changes every state.

Code for this chapter

Each one reproduces the tables above. Open it in Colab, change an input and watch which numbers move.

From Natural Language Processing: From Corpus Statistics to Grounded LLMs, by Ramaseshan Ramachandran.