Course contents 12 / 37

12 / 37 · Neural Sequence Models

Recurrent neural networks

A network that reads one token at a time and carries a hidden state has unbounded context in principle and a parameter count independent of length; it can classify a sequence from its last state or predict the next token at every step. Gradients through many steps vanish or explode, which limits what the context can actually hold.

On this page 9

The convolutional classifier of the previous note sees a window of kk tokens and forgets position at pooling. The n-gram language model of the earlier note sees n−1n - 1 tokens and cannot generalize across them. Both have a context of fixed, small size. This note introduces a model whose context is, in principle, the entire prefix: it reads tokens in order and keeps a summary in a vector that it updates at every step.

Notation. Input token vectors x1,…,xT∈Rdx_1, \dots, x_T \in \mathbb R^{d}; hidden states ht∈RHh_t \in \mathbb R^{H}; outputs yty_t. Weight matrices Wx∈RH×dW_x \in \mathbb R^{H \times d}, Wh∈RH×HW_h \in \mathbb R^{H \times H}, Wy∈RK×HW_y \in \mathbb R^{K \times H} and biases b,byb, b_y. ⊙\odot is the element-wise product.

The recurrence#

ht=tanh⁡(Wxxt+Whht−1+b),h0=0,h_t = \tanh\big(W_x x_t + W_h h_{t-1} + b\big), \qquad h_0 = 0, yt=Wyht+by.y_t = W_y h_t + b_y .

WxW_x reads the current token, WhW_h carries the previous state forward, and tanh⁡\tanh keeps every coordinate of hth_t in (−1,1)(-1, 1). The same Wx,Wh,bW_x, W_h, b are used at every step, so the parameter count is Hd+H2+HH d + H^2 + H whatever TT is: a sequence of 5 tokens and one of 500 run the same cell 5 or 500 times. Unrolled over time, the computation is a chain of identical cells with the hidden state as the only connection between them.

Shapes for a batch: input [B,T,d][B, T, d], hidden states [B,T,H][B, T, H], final state [B,H][B, H]. The PyTorch module nn.RNN(d, H, batch_first=True) returns both the full sequence of states and the last one.

Worked example#

Scalar case, d=H=1d = H = 1, Wx=1W_x = 1, Wh=0.5W_h = 0.5, b=0b = 0, inputs x=(1,0,0)x = (1, 0, 0):

ttxtx_tWxxt+Whht−1W_x x_t + W_h h_{t-1}ht=tanh⁡(⋅)h_t = \tanh(\cdot)
111+0=11 + 0 = 10.7620.762
200+0.381=0.3810 + 0.381 = 0.3810.3630.363
300+0.182=0.1820 + 0.182 = 0.1820.1800.180

The input at step 1 is still visible in h3h_3, attenuated by 0.50.5 per step and by the tanh⁡\tanh. With Wh=2W_h = 2 instead, the same input would grow: h2=tanh⁡(1.52)=0.909h_2 = \tanh(1.52) = 0.909, h3=tanh⁡(1.82)=0.949h_3 = \tanh(1.82) = 0.949, saturating at the edge of tanh⁡\tanh‘s range. The recurrent weight decides whether the past fades or floods, and it is the same weight at every step, so whatever it does, it does TT times.

Step by step#

Figure

One step of a recurrent network: what goes where

An RNN step and a three-step exampleLeft, one step: hₜ₋₁ through W_h and xₜ through W_x meet in a sum with b, then tanh gives hₜ, which goes up through W_y and a softmax to the output and right to the next step. Right, three steps with numbers: inputs 1, 0, 0, states 0.762, 0.363, 0.180.(a) one step: what goes wherextWx1ht−1Wh2+b3tanhht4Wysoftmaxŷt5t + 16(b) the worked example: three stepsWx = 1, Wh = 0.5, b = 00h0×0.51x1×1+1tanh0.762h1×0.50x2×1+0.381tanh0.363h2×0.50x3×1+0.182tanh0.180h3inputstateweights, gatesoutputAn RNN step and a three-step exampleLeft, one step: hₜ₋₁ through W_h and xₜ through W_x meet in a sum with b, then tanh gives hₜ, which goes up through W_y and a softmax to the output and right to the next step. Right, three steps with numbers: inputs 1, 0, 0, states 0.762, 0.363, 0.180.(a) one step: what goes wherextWx1ht−1Wh2+b3tanhht4Wysoftmaxŷt5t + 16(b) the worked example: three stepsWx = 1, Wh = 0.5, b = 00h0×0.51x1×1+1tanh0.762h1×0.50x2×1+0.381tanh0.363h2×0.50x3×1+0.182tanh0.180h3inputstateweights, gatesoutput
(a) One step for H = 3 and d = 4. The input xₜ is multiplied by W_x (1), the previous state hₜ₋₁ by W_h (2), the two are added with b (3), and tanh gives the new state hₜ (4). If an output is needed, hₜ is multiplied by W_y and passed through a softmax (5); hₜ itself goes on to the next step (6). (b) The same operations with the numbers of the worked example: the same weights ×1 and ×0.5 at every step; the 1 fed in at step 1 fades from 0.762 to 0.180.

The numbers in the figure follow one application of the cell:

  1. Read the input. WxxtW_x x_t is what the current token contributes, a vector of HH numbers.
  2. Read the memory. Whht−1W_h h_{t-1} is what the previous state contributes; at the first step h0=0h_0 = 0 and the term vanishes.
  3. Add. The two vectors and the bias bb are summed coordinate by coordinate.
  4. Squash. tanh⁡\tanh maps every coordinate into (−1,1)(-1, 1); the result is the new state hth_t.
  5. Predict, if needed. Wyht+byW_y h_t + b_y gives scores over the classes or over the vocabulary, and a softmax turns them into probabilities.
  6. Pass the state on. hth_t becomes the ht−1h_{t-1} of the next step, which repeats steps 1–5 with the same WxW_x, WhW_h and bb.

The models of the following notes keep this skeleton, a state carried from step to step and updated from the current input, and each changes one piece of it. The LSTM changes how the state is updated, seq2seq hands the final state from one network to another, attention lets the second network look back at every state of the first, and self-attention drops the chain altogether. Their figures use the same colours: green for inputs, blue for states, amber for weights and gates, red for outputs, and an amber outline for what a model adds to the previous one.

Two ways to use the states#

Figure

One recurrence, two ways to use its states

An RNN as a classifier and as a language model(a) Four RNN cells read oil, price, soar, record; the last state goes to linear + softmax, which outputs Business. (b) Four cells read the, cat, sat, on; each produces a softmax, and the targets are cat, sat, on, the.(a) classifier: the last stateh₀RNNoilRNNpriceRNNsoarRNNrecordlinear + softmax → Business(b) language model: every stateh₀RNNthesoftmaxcatRNNcatsoftmaxsatRNNsatsoftmaxonRNNonsoftmaxtheAn RNN as a classifier and as a language model(a) Four RNN cells read oil, price, soar, record; the last state goes to linear + softmax, which outputs Business. (b) Four cells read the, cat, sat, on; each produces a softmax, and the targets are cat, sat, on, the.(a) classifier: the last stateh₀RNNoilRNNpriceRNNsoarRNNrecordlinear + softmax → Business(b) language model: every stateh₀RNNthesoftmaxcatRNNcatsoftmaxsatRNNsatsoftmaxonRNNonsoftmaxthe
Both models are the same cell applied to each token in turn; the horizontal arrows pass the hidden state hₜ on, starting from h₀ = 0. (a) A classifier reads the whole document and gives only the last state, which depends on every token in order, to a linear layer. (b) A language model produces a distribution over the vocabulary at every position and is trained to predict the next token: the inputs are the text without its last token, the targets the same text shifted by one (teacher forcing). The loss is the sum of −log P(target) over positions, and the exponential of its average is the perplexity.

RNN as a classifier#

Read the whole document and use the last state as its representation: y^=softmax(WyhT+by)\hat y = \mathrm{softmax}(W_y h_T + b_y). Unlike max-pooling, hTh_T is computed in order, so it can in principle distinguish not good from good not. In the AG News setting of the previous note, a one-layer GRU (a gated cell from the next note) with H=128H = 128 reads the same 100-dimensional Word2Vec vectors and feeds its last state to a linear layer. It has 3(100⋅128+128⋅128+2⋅128)+128⋅4+4=88,8363(100 \cdot 128 + 128 \cdot 128 + 2 \cdot 128) + 128 \cdot 4 + 4 = 88{,}836 parameters, comparable to the convolutional models.

One practical point. The last state of a padded sequence is the state after the padding tokens, not after the last real one. Either pack the sequences so the recurrence stops at the true length, or feed the padding at the start, or read the state at index length - 1 per example.

RNN as a language model#

Produce an output at every step and make it a distribution over the vocabulary:

P(wt+1∣w≤t)=softmax(Wyht+by),Wy∈R∣V∣×H.P(w_{t+1} \mid w_{\le t}) = \mathrm{softmax}(W_y h_t + b_y), \qquad W_y \in \mathbb R^{|V| \times H}.

This is the chain-rule factorization of the n-gram note with the Markov assumption removed: the conditional depends on hth_t, which depends on the whole prefix. Training uses teacher forcing: the input sequence is the text shifted by one, x=(w1,…,wT−1)x = (w_1, \dots, w_{T-1}), the targets are y=(w2,…,wT)y = (w_2, \dots, w_T), and the loss is the cross-entropy summed over positions, which is exactly the per-token negative log-likelihood whose exponential is the perplexity. Padding positions are excluded from the loss with ignore_index. At generation time the model’s own sample is fed back as the next input, as in every autoregressive model.

Shapes: ids [B,T][B, T] → embeddings [B,T,E][B, T, E] → states [B,T,H][B, T, H] → logits [B,T,∣V∣][B, T, |V|]. The cross-entropy is computed on the flattened [B⋅T,∣V∣][B \cdot T, |V|] logits against [B⋅T][B \cdot T] targets.

Take a model over a BPE vocabulary of ∣V∣=37,580|V| = 37{,}580 tokens with E=256E = 256 and H=512H = 512: it has 9.6 M parameters in the input embedding, 0.39 M in the recurrent cell, and 19.3 M in the output layer, 29.3 M in total. Two thirds of the model is the output softmax, the same cost structure that drove negative sampling in Word2Vec; for a language model the full softmax is kept because the distribution itself is the output. The GPT note returns to neural language-model perplexities with a Transformer.

Gradients through time#

Training unrolls the recurrence and backpropagates through every step. The gradient of the loss at step TT with respect to the state at step tt passes through T−tT - t applications of the cell:

∂hT∂ht=∏s=t+1T∂hs∂hs−1=∏s=t+1Tdiag(1−hs2) Wh.\frac{\partial h_T}{\partial h_t} = \prod_{s=t+1}^{T} \frac{\partial h_s}{\partial h_{s-1}} = \prod_{s=t+1}^{T} \mathrm{diag}\big(1 - h_s^2\big)\, W_h .

Each factor is the same WhW_h scaled by the derivative of tanh⁡\tanh (at most 1). If the largest singular value of WhW_h is below 1 the product shrinks geometrically and the gradient vanishes: the loss at the end of a sentence cannot adjust what the cell did at its start. If it is above 1 the product can grow, and the gradient explodes (Pascanu, Mikolov and Bengio, 2013 show that this condition is necessary, not sufficient). With a scalar recurrent weight w=2w = 2 and states in the linear part of tanh⁡\tanh, where its derivative is close to 1, fifty steps multiply the gradient by 250≈10152^{50} \approx 10^{15}. The figure below, shared with the Generative Models note where the computation is written out, shows the mechanism.

Figure

Unrolled RNN with recurrent weight w = 2

Unrolled recurrent network with exploding factorsThree time steps stacked vertically. In each, an input box feeds a recurrent cell drawn with a ReLU icon; the first two cells feed dashed output boxes and the third a solid output box. Curved red arrows carry the hidden state from each cell to the next, each labelled times 2; a dashed red squiggle continues below the third cell, also labelled times 2 and so on, exploding. A note explains that after tau steps the factor is w to the power tau.inputw = 2outputx1x2x3ReLU cell×2×2×2 …explodinghidden state hpasses throughthe same wafter τ steps:×wτUnrolled recurrent network with exploding factorsThree time steps stacked vertically. In each, an input box feeds a recurrent cell drawn with a ReLU icon; the first two cells feed dashed output boxes and the third a solid output box. Curved red arrows carry the hidden state from each cell to the next, each labelled times 2; a dashed red squiggle continues below the third cell, also labelled times 2 and so on, exploding. A note explains that after tau steps the factor is w to the power tau.inputw = 2outputx1x2x3ReLU cell×2×2×2 …explodingRed arrows: the hidden state h passesthrough the same w at every step.After τ steps the factor is wτ.
The same weight w = 2 is applied at every time step, so every red recurrent arrow multiplies by 2. The sensitivity of a later hidden state to an earlier one, and with it the backpropagated gradient, is therefore 2 after one step, 4 after two, 8 after three, and w raised to the power τ after τ steps. Intermediate outputs are dashed because only the last one is used here.

Two consequences for practice. Gradient clipping rescales the gradient vector when its norm exceeds a threshold, such as 1.0, which stops explosions from destroying the weights but does nothing for vanishing. And vanishing is the binding constraint: a plain RNN learns dependencies over a handful of tokens and loses the rest, which is why “context is the whole prefix” is true of the forward pass and not of what the model learns (Bengio, Simard and Frasconi, 1994). The next note changes the cell so that the state has an additive path with a gradient factor near 1.

What changed#

CNN classifierN-gram LMRNN
Contextkk tokens per filtern−1n - 1 tokensWhole prefix (in principle)
Parameters vs lengthIndependentTable grows with dataIndependent
PositionLost at poolingLast n−1n-1 onlyEncoded in state order
Generalizes across tokensYes (embeddings)NoYes
GenerationNoYesYes, with ∣V∣\lvert V \rvert-way output
Parallel over TTYes—No: hth_t needs ht−1h_{t-1}

Limitations#

  • Short effective memory. Gradients vanish over tens of steps; the state cannot be trained to keep information for long.
  • Sequential computation. Step tt waits for step t−1t - 1, so an RNN cannot use a GPU’s parallelism along the sequence. Training time grows linearly with TT per example, and the Transformer note will make this the central argument against recurrence.
  • One state for everything. A single vector hTh_T must summarize the whole input. For classification that is adequate; for translating a 30-word sentence it is the bottleneck that the seq2seq note runs into.
  • Padding interacts with the last state. Variable lengths need packing or masking that the convolutional model did not.

What you should remember#

  • ht=tanh⁡(Wxxt+Whht−1+b)h_t = \tanh(W_x x_t + W_h h_{t-1} + b): the same cell at every step, parameters independent of length, state carries the prefix.
  • Classifier: last state → linear. Language model: every state → softmax over ∣V∣|V|, trained by teacher forcing on the shifted sequence with padding ignored.
  • The gradient through T−tT - t steps is a product of T−tT - t copies of diag(1−h2)Wh\mathrm{diag}(1 - h^2)W_h; it vanishes or explodes geometrically. Clipping handles explosion only.
  • Recurrence is sequential: no parallelism along the sequence.

Notebook#

Two notebooks. Cell numbers count every cell, markdown included, from 1. Quoted numbers come from the run saved with the course; cells changed since then for current library versions have no saved output.

week02_NN_text_classification.ipynb, cells 40–43: SimpleRNN wraps nn.GRU(100, 128, batch_first=True) and a linear layer on the last hidden state, trained with the same loop as the convolutional models for 5 epochs. The final printout is absent from the saved outputs, so the course has no accuracy to compare this GRU with the convolutional classifiers and the TF-IDF baseline. The classifier takes the final state after the padding tokens rather than after the last real token, the simplification discussed under RNN as a classifier.

week03_lm.ipynb:

  • Cells 46–49 BPE-tokenize the joke corpus and build a 37,580-entry vocabulary with fixed ids for <pad> (0) and <unk> (1).
  • Cell 51 builds batches: each joke is cut to 128 tokens, inputs are s[:-1], targets s[1:], both padded with 0.
  • Cell 52 defines SimpleRNNLM: nn.Embedding(V, 256, padding_idx=0), nn.RNN(256, 512, batch_first=True), nn.Linear(512, V), the 29.3 M-parameter model counted in RNN as a language model.
  • Cell 53 runs one epoch: cross-entropy with ignore_index=0, gradient-norm clipping at 1.0, and perplexity as exp⁡\exp of the token-weighted mean loss (set to ∞\infty above a loss of 20 to avoid overflow). Cell 55 trains for three epochs with AdamW at 3×10−33 \times 10^{-3} and batches of 64; the run saved with the course was interrupted after a few batches, so there are no saved perplexities. The softmax over 37,580 tokens makes this cell practical only on a CUDA GPU, as the markdown cell before it says.

Next#

The recurrence gives the model a memory and the gradient takes it away. The next note keeps the recurrence and redesigns the cell: a separate memory line that is updated additively and gates that decide what to write, keep and read, so that the gradient along the memory line is a product of factors close to 1 rather than of WhW_h.

References#