Architectures/Recurrent Networks

Lesson 6.32,061 words

Recurrent Networks

A recurrent network folds a sequence into a fixed-size hidden state, reusing one set of weights at every time step, the architectural prior that the same rule applies wherever it lands in time. Unrolling the recurrence exposes a deep feed-forward graph; backpropagation through it sums gradient contributions across all steps and chains a product of Jacobians, and that product is why long-range gradients vanish or explode.

╌╌╌╌

A convolutional network bakes in the prior that the same feature detector should slide across space. A recurrent network bakes in the dual prior for time: the same transition rule should apply at every position in a sequence. It processes one element at a time, carrying a fixed-size hidden state that summarizes everything seen so far, a learned, lossy memory of the past.1

The recurrence

A vanilla RNN is one nonlinear map applied repeatedly. At step it folds the new input into the running state and emits an output :

with , , the activation usually , and the state seeded at . Three weight matrices carry the whole model:

matrixshaperole
maps the current input into the state
propagates the previous state forward
reads an output off the state

The defining structural fact is in the subscripts: carry no time index. The same three matrices act at and at .

To make the sizing concrete, chase the shapes through one step. Take a concrete input, an state, and a output. Then is , is , is , and is . The two matrix-vector products both land in , add elementwise, pass through componentwise, and read out through :

The parameter count is , and it stays whether the sequence is ten steps or ten thousand. The figure below tracks those shapes through one step; the same block is what the unrolled graph copies.

One recurrence step as a shape flow ( input, state, output). The two matrix-vector products land in , sum with the bias, pass through , and read out through to .

This is the temporal analogue of a convolution's shared filter: it lets one network ingest sequences of any length, and it is a strong regularizer: the count of free parameters does not grow with .

Unrolling through time

The self-loop is a compact fiction. To compute anything we unroll the recurrence: copy the cell once per time step and feed each copy's state into the next. The result is an ordinary feed-forward graph, layers deep, in which every layer shares weights.2

Left: the recurrent cell with its self-loop. Right: the same cell unrolled across time, one copy per step with hidden state threaded along and weights shared.

The forward pass is a left-to-right sweep over the unrolled graph: each step reads the input, updates the state, and emits an output.

Algorithm:RnnForward(x1:T,Whh,Wxh,Why,b)\textsc{RnnForward}(x_{1:T}, W_{hh}, W_{xh}, W_{hy}, b) — one left-to-right sweep
  1. 1
    h00h_0 \gets 0
    zero-initialize the memory
  2. 2
    for t1t \gets 1 to TT do
  3. 3
    atWhhht1+Wxhxt+ba_t \gets W_{hh}\,h_{t-1} + W_{xh}\,x_t + b
    pre-activation
  4. 4
    htg(at)h_t \gets g(a_t)
    new hidden state
  5. 5
    ytWhyhty_t \gets W_{hy}\,h_t
    output at step tt
  6. 6
    return h1:T, y1:Th_{1:T},\ y_{1:T}

Unrolling reframes the model exactly: a recurrent network is a very deep feed-forward network whose depth equals the sequence length and whose every layer is tied to the same weights. Everything about training follows from that one observation.

Backpropagation through time

Because the unrolled graph is feed-forward, it is trained by ordinary backpropagation; applied to the unrolled graph the method is called backpropagation through time (BPTT).3 The total loss sums per-step losses, , and we want . The subtlety is that is the same matrix at every step, so it influences the loss through every hidden state; its gradient is a sum of contributions across all time steps:

The middle factor is where time enters. To get from step back to an earlier step , the gradient must pass through every intermediate state, so is itself a product of one-step Jacobians:

Each link is the recurrent weight scaled by the activation derivative . The gradient flows backward through the unrolled chain, accumulating one such Jacobian per step.

BPTT. The loss propagates backward (red) through the chain; each hop is one one-step Jacobian, and their product spans distant steps.

The same backward sweep accumulates , , , and by adding each step's contribution into a shared accumulator.

Algorithm:Bptt(x1:T,y1:T)\textsc{Bptt}(x_{1:T}, y_{1:T}^{\star}) — accumulate shared-weight gradients
  1. 1
    run RnnForward\textsc{RnnForward}, caching at,ht,yta_t, h_t, y_t
  2. 2
    initialize dWhh,dWxh,dWhy,db0\,\mathrm{d}W_{hh}, \mathrm{d}W_{xh}, \mathrm{d}W_{hy}, \mathrm{d}b \gets 0
  3. 3
    δT+10\delta_{T+1} \gets 0
    incoming state gradient
  4. 4
    for tTt \gets T down to 11 do
  5. 5
    dWhy+=(Lt/yt)htT\mathrm{d}W_{hy} \mathrel{+}= (\partial L_t / \partial y_t)\,h_t^{T}
    output weights
  6. 6
    δtWhyT(Lt/yt)+WhhTδt+1\delta_t \gets W_{hy}^{T}(\partial L_t / \partial y_t) + W_{hh}^{T}\delta_{t+1}
    sum output + future
  7. 7
    δtδtg(at)\delta_t \gets \delta_t \odot g'(a_t)
    through the activation
  8. 8
    dWhh+=δtht1T\mathrm{d}W_{hh} \mathrel{+}= \delta_t\,h_{t-1}^{T}
    accumulate, shared weights
  9. 9
    dWxh+=δtxtT\mathrm{d}W_{xh} \mathrel{+}= \delta_t\,x_t^{T}
  10. 10
    db+=δt\mathrm{d}b \mathrel{+}= \delta_t
  11. 11
    return dWhh,dWxh,dWhy,db\mathrm{d}W_{hh}, \mathrm{d}W_{xh}, \mathrm{d}W_{hy}, \mathrm{d}b

The cost is memory: BPTT must cache every for the backward pass, so memory grows with . For a sequence of tokens the graph is layers deep, and the whole stack of activations has to live in memory until the backward sweep reaches step . Truncated BPTT caps this by chopping the sequence into windows of steps: the forward pass still runs the full length, carrying across window boundaries, but the backward pass only flows within each window and is cut at the seam. Gradients travel at most steps back, which bounds memory and compute at the price of any dependency longer than .

Truncated BPTT with window . The forward state (blue) runs the full length, but each backward pass (red) is confined to its window and stopped at the boundary, so no gradient crosses a seam.

Vanishing and exploding gradients

The product of Jacobians is also the network's deepest weakness.4 Hold the activation roughly linear () so the one-step Jacobian is essentially ; then the state-to-state gradient across steps is a matrix power:

Diagonalize with eigenvalues . A matrix power raises each eigenvalue to the same power, so the gradient's growth is governed entirely by the spectral radius :

The number either collapses to or blows up to as the gap grows, depending on a single threshold:

Vanishing gradient through time. With the gradient decays geometrically as the gap grows, so distant steps receive almost no learning signal.

For example, suppose the largest eigenvalue is . Over a -step gap the gradient is scaled by , and over steps by : the signal from a hundred steps back arrives four to five orders of magnitude weaker than a signal from one step back, drowned out by nearer terms and by numerical noise. Flip the eigenvalue to and the same steps multiply the gradient by , and the loss overflows to NaN in a few updates. The stable band around is vanishingly thin, and nothing in ordinary gradient descent pins to it.

The spectral radius sets the fate of the gradient. decays geometrically (blue), blows up (red), and only the knife-edge (dashed) is flat — and unstable under training.

The two failures need different fixes, and only one is easy.

pathologyconditionsymptomfix
explodinggradient norm spikes, loss diverges (NaN)gradient clipping
vanishingdistant steps get no signal, no long-range memorygating (LSTM / GRU)

Gradient clipping rescales the gradient whenever its norm exceeds a threshold , capping the step size without changing its direction:

Clipping controls explosion but does nothing for vanishing — a signal that has already decayed to numerical zero cannot be amplified. The real fix is to change the recurrence so the gradient has a near-identity path to travel along, which is precisely what gated cells provide.

Sequence-task shapes

The same recurrent cell serves very different tasks depending on which inputs and outputs are present. Counting elements on each side gives a small taxonomy.

shapeinputs outputsreadsemitsexample
one-to-onesinglesingleplain classification (degenerate RNN)
one-to-manysinglesequenceimage captioning
many-to-onesequencesinglesentiment classification
many-to-many (aligned)sequencesequence, step-alignedpart-of-speech tagging
many-to-many (seq2seq)sequencesequence, different lengthmachine translation
Four sequence-task shapes (black inputs, blue outputs), spanning aligned many-to-many and the encoder-decoder seq2seq case that reads the input fully first.

The distinction is purely in the wiring, not the cell. One-to-many feeds a single input at the first step and then generates by looping its own output back in as the next input — an image-captioning model conditions on the image once, then emits words until a stop token. Many-to-one ignores every intermediate output and reads a single prediction off the final state , the only vector that has seen the whole sequence; a sentiment classifier does exactly this. Aligned many-to-many emits one output per input in lockstep, the natural shape for per-token labeling like part-of-speech tagging.

The seq2seq case is special: an encoder RNN reads the whole input into a final state, which seeds a decoder RNN that generates the output. The input and output lengths need not match — a five-word English sentence can become a seven-word French one — because the decoder runs its own clock, generating until it emits a stop token. The bottleneck of forcing all meaning through one fixed vector is the strain attention was invented to relieve.

Bidirectional RNNs

A plain RNN at step has seen only . For aligned tasks where the whole sequence is available up front — tagging a word given its full sentence — the future is just as informative as the past. A bidirectional RNN runs two independent recurrences, one forward and one backward, and concatenates their states.5

A bidirectional RNN. A forward chain (blue) and backward chain (red) are concatenated at each step, so the output depends on the entire sequence.

The output stacks both directions, , so every prediction is conditioned on the full context. Bidirectionality fits tagging and classification, where the entire input is known in advance; it is unavailable for streaming or generation, where the future has not happened yet.

Around and after the plain RNN

Three practical mechanisms sit around the plain recurrence in Goodfellow's Chapter 10 but are easy to miss on a first read, and one later development is the bridge to everything after RNNs.

  • Truncated BPTT. The unrolled graph is layers deep, and full BPTT stores every hidden state for the backward pass — memory, impossible for a book- length sequence. Truncated BPTT processes the stream in windows of length (say tokens): it carries the hidden state forward across window boundaries but only propagates the gradient back steps before detaching. Concretely, a -token document with needs the memory of a -step graph, not a -step one — a saving — at the price that no dependency longer than steps gets a gradient. This makes the vanishing-gradient limit a design parameter rather than an accident.
  • Teacher forcing and exposure bias. During training a generative RNN is fed the true previous token rather than its own prediction, which decouples the steps and lets the whole sequence train in parallel (teacher forcing). At inference it must consume its own outputs, so a single early mistake shifts the model into states it never saw in training — exposure bias. Scheduled sampling (Bengio et al., NeurIPS 2015) anneals from true tokens toward sampled ones to close the gap.
  • Echo-state / reservoir networks. One way to sidestep the exploding-gradient problem entirely is not to train the recurrence at all: fix with spectral radius tuned just below , let the random reservoir project the input into a rich dynamical state, and train only the linear readout. Echo-state networks (Jaeger, 2001) trade capacity for a convex, gradient-free fit.

The development that mattered most postdates all of this: sequence-to-sequence with attention (Bahdanau et al., ICLR 2015; Sutskever et al., NeurIPS 2014) let a decoder read every encoder state directly instead of squeezing the source through one fixed-size hidden vector. That attention step removed the recurrence's memory bottleneck and led, within two years, to the Transformer dropping recurrence altogether.

Takeaways

  • A recurrent network is the recurrence , , with one weight set shared across all time steps and the hidden state as a fixed-size, lossy memory of the prefix.
  • Unrolling turns the cell into a feed-forward graph layers deep with tied weights; the forward pass is one left-to-right sweep.
  • BPTT sums the gradient of the shared over every step, and the state-to-state term is a product of one-step Jacobians.
  • That product scales like : vanishes, explodes. Clip the gradient to control explosion; gate the recurrence (LSTM/GRU) to address vanishing.
  • Task shapes range over one-to-one, one-to-many, many-to-one, and many-to-many (aligned and seq2seq); bidirectional RNNs add a backward pass when the whole sequence is known up front.

Footnotes

  1. Goodfellow, Deep Learning, §10.2 — Recurrent Neural Networks: the shared-weight recurrence and the hidden state as a lossy summary of the prefix.
  2. Goodfellow, Deep Learning, §10.1 — Unfolding Computational Graphs: rewriting the self-loop as a depth- feed-forward graph with tied weights.
  3. Goodfellow, Deep Learning, §10.2.2 — Computing the Gradient in a Recurrent Network: back-propagation through time as ordinary backprop on the unrolled graph, summing the shared-weight gradient across steps.
  4. Goodfellow, Deep Learning, §10.7 — The Challenge of Long-Term Dependencies: the product of Jacobians scaling as , hence vanishing or exploding gradients.
  5. Goodfellow, Deep Learning, §10.3 — Bidirectional RNNs: running a forward and a backward recurrence so each output conditions on the entire sequence.

╌╌ END ╌╌