AI-340 · Lesson 4 of 12 · 35 min

Sequence models

Language is sequential: the meaning of a word depends on what came before it, sometimes many words before. "The keys to the cabinet in the hallway are missing" requires matching "are" with "keys", seven words back, not with "hallway". Bag-of-words models ignore order, and n-gram models see only a few words of context. This lesson covers neural models designed for sequences: recurrent neural networks (RNNs), their gated variants LSTM and GRU, bidirectional and encoder-decoder architectures, and the decoding algorithms used to generate text. RNNs dominated NLP from about 2014 to 2018; transformers (Lesson 6) have since replaced them for most tasks, but the problems RNNs faced explain why transformers were designed as they were, and recurrent ideas are returning in efficient modern architectures.

Sequence tasks

NLP tasks on sequences fall into a few patterns:

  • Sequence classification: one label for a whole input, such as sentiment, topic or spam detection.
  • Sequence labeling (tagging): one label per token, such as part-of-speech tagging or named entity recognition (NER). NER often uses the BIO scheme: B-PER marks the beginning of a person's name, I-PER a token inside it, and O a token outside any entity, so "Ada Lovelace visited London" is tagged B-PER I-PER O B-LOC.
  • Sequence-to-sequence (seq2seq): an output sequence of different length from the input, such as translation, summarization and speech recognition.
  • Language modeling and generation: predicting the next token repeatedly to produce text.

Neural language models

In 2003, Bengio and colleagues proposed a neural language model that replaced n-gram counts with a neural network: the embeddings of the previous \(n-1\) words are concatenated and passed through a hidden layer and a softmax output over the vocabulary. Because similar words have similar embeddings, the model generalizes from "the cat sat" to "the kitten sat", which n-gram models cannot. Its context is still fixed, though: a 5-word window cannot see the subject of a long sentence.

Worked problem

Size of a fixed-window neural language model

A model uses a 4-word context, 100-dimensional embeddings, a 500-unit hidden layer and a 20,000-word vocabulary. Count its parameters (weights plus biases).

Show solution
  1. Embeddings: \(20{,}000 \times 100 = 2{,}000{,}000\).
  2. Hidden layer: input \(4 \times 100 = 400\); weights \(400 \times 500 = 200{,}000\) plus 500 biases.
  3. Output layer: \(500 \times 20{,}000 = 10{,}000{,}000\) plus 20,000 biases.
  4. Total: \(\approx 12.2\) million.

Answer: About 12.2 million parameters, most in the output layer and embeddings, which scale with vocabulary size. Doubling the context window only adds 200,000 parameters, but the window remains fixed.

Recurrent neural networks

A recurrent neural network processes a sequence one element at a time, maintaining a hidden state \(\mathbf{h}_t\) that summarizes everything seen so far. At each step it combines the previous hidden state with the current input:

\[\mathbf{h}_t = \tanh(W_{hh}\mathbf{h}_{t-1} + W_{xh}\mathbf{x}_t + \mathbf{b}_h), \qquad \hat{\mathbf{y}}_t = \text{softmax}(W_{hy}\mathbf{h}_t + \mathbf{b}_y)\]

The same weights are used at every time step, so an RNN can process sequences of any length with a fixed number of parameters, and what it learns about a pattern at one position applies at every position. As a language model, the RNN reads one token per step and predicts the next from \(\mathbf{h}_t\), which in principle depends on the entire history.

Diagram of a recurrent neural network unrolled over four time steps. Inputs x1 to x4 enter from below into identical cells labeled with shared weights. Each cell passes its hidden state h to the next cell to the right and produces an output y above it. A compact folded version on the left shows a single cell with a loop arrow back to itself.
An RNN reuses the same cell at every step, passing the hidden state forward.

Worked problem

One RNN step by hand

A scalar RNN has \(w_{hh} = 0.5\), \(w_{xh} = 1.0\), \(b = 0\), and \(h_0 = 0\). Compute \(h_1\), \(h_2\) and \(h_3\) for inputs \(x = (1, 0, 0)\).

Show solution
  1. \(h_1 = \tanh(0 + 1) = 0.762\).
  2. \(h_2 = \tanh(0.5 \times 0.762) = \tanh(0.381) = 0.364\).
  3. \(h_3 = \tanh(0.5 \times 0.364) = \tanh(0.182) = 0.180\).

Answer: The memory of the single input fades by roughly half at each step (0.76, 0.36, 0.18), because \(w_{hh} = 0.5\). After 20 steps almost nothing remains, which illustrates why simple RNNs struggle with long-range dependencies.

Training and vanishing gradients

RNNs are trained by backpropagation through time (BPTT): the network is unrolled over the sequence, the loss is summed over all steps, and gradients flow backwards through every step. The gradient reaching step \(t-k\) from step \(t\) is a product of \(k\) Jacobian matrices:

\[\frac{\partial \mathbf{h}_t}{\partial \mathbf{h}_{t-k}} = \prod_{j=t-k+1}^{t} \text{diag}\!\left(1 - \mathbf{h}_j^2\right) W_{hh}\]

If the factors shrink vectors, the product shrinks exponentially and the gradient vanishes, so the network cannot learn long-range dependencies. If they grow vectors, the gradient explodes and training becomes unstable. The spectral radius of \(W_{hh}\) (its largest eigenvalue magnitude) largely decides which, and the tanh derivative, at most 1 and much smaller when units saturate, pushes toward vanishing.

Gradient norms backpropagated through a tanh RNN and a linear RNN with different spectral radii.
import numpy as np

rng = np.random.default_rng(0)
d, T = 64, 60
for rho in (0.8, 1.0, 1.2):
    W = rng.normal(0, 1, (d, d))
    W *= rho / max(abs(np.linalg.eigvals(W)))       # set the spectral radius
    h = np.zeros(d)
    hs = []
    x = rng.normal(0, 1, (T, d)) * 0.5
    for t in range(T):                              # forward pass of a tanh RNN
        h = np.tanh(W @ h + x[t])
        hs.append(h)
    g = np.ones(d) / np.sqrt(d)                     # gradient arriving at the last state
    norms = {}
    for k, t in enumerate(range(T - 1, -1, -1)):    # backpropagate through time
        norms[k] = np.linalg.norm(g)
        g = W.T @ (g * (1 - hs[t] ** 2))
    print(f"spectral radius {rho}: |grad| after 1, 10, 30, 59 steps back:",
          " ".join(f"{norms[k]:.1e}" for k in (1, 10, 30, 59)))
    # the same with a linear RNN (no tanh) shows pure exponential growth or decay
    g = np.ones(d) / np.sqrt(d)
    for _ in range(59):
        g = W.T @ g
    print(f"   linear RNN after 59 steps: {np.linalg.norm(g):.1e}")
Output
spectral radius 0.8: |grad| after 1, 10, 30, 59 steps back: 6.5e-01 6.7e-03 5.9e-07 3.5e-13
   linear RNN after 59 steps: 9.7e-07
spectral radius 1.0: |grad| after 1, 10, 30, 59 steps back: 7.8e-01 6.1e-02 8.5e-05 9.5e-09
   linear RNN after 59 steps: 2.2e-01
spectral radius 1.2: |grad| after 1, 10, 30, 59 steps back: 9.4e-01 2.0e-01 2.2e-03 1.4e-05
   linear RNN after 59 steps: 2.2e+04

With spectral radius 0.8, the gradient falls by about 12 orders of magnitude over 59 steps. Even at 1.0 and 1.2, the saturating tanh makes gradients vanish in the nonlinear network, while the linear network with radius 1.2 explodes to 22,000. In practice, exploding gradients are handled by gradient clipping (rescaling the gradient when its norm exceeds a threshold, AI-310), while vanishing gradients required a change of architecture.

Log-scale plot of gradient norm against number of steps backpropagated, from 0 to 60, for a tanh RNN with spectral radius 0.8, 1.0 and 1.2, and a linear RNN with radius 1.2. All three tanh curves decline steadily, the 0.8 curve falling below 10 to the minus 12, while the linear curve rises above 10 to the 4.
Gradients vanish or explode exponentially with distance in simple RNNs.

LSTM and GRU

The long short-term memory (LSTM) network, introduced by Hochreiter and Schmidhuber in 1997, adds a cell state \(\mathbf{c}_t\) that is updated additively and controlled by gates, vectors of values between 0 and 1 computed by sigmoid functions:

\[\begin{aligned} \mathbf{f}_t &= \sigma(W_f[\mathbf{h}_{t-1}, \mathbf{x}_t] + \mathbf{b}_f) \\ \mathbf{i}_t &= \sigma(W_i[\mathbf{h}_{t-1}, \mathbf{x}_t] + \mathbf{b}_i) \\ \mathbf{g}_t &= \tanh(W_g[\mathbf{h}_{t-1}, \mathbf{x}_t] + \mathbf{b}_g) \\ \mathbf{o}_t &= \sigma(W_o[\mathbf{h}_{t-1}, \mathbf{x}_t] + \mathbf{b}_o) \\ \mathbf{c}_t &= \mathbf{f}_t \odot \mathbf{c}_{t-1} + \mathbf{i}_t \odot \mathbf{g}_t \\ \mathbf{h}_t &= \mathbf{o}_t \odot \tanh(\mathbf{c}_t) \end{aligned}\]

The forget gate \(\mathbf{f}_t\) decides how much of the old cell state to keep, the input gate \(\mathbf{i}_t\) how much new information \(\mathbf{g}_t\) to write, and the output gate \(\mathbf{o}_t\) how much of the cell to expose as the hidden state. When the forget gate is near 1 and the input gate near 0, the cell state passes through unchanged, and so does its gradient: the additive path acts as a "gradient highway" across many steps. Initializing the forget-gate bias to a positive value (such as 1 or 2) helps networks remember by default.

Diagram of an LSTM cell. The cell state runs horizontally across the top as a straight line, modified only by a multiplication with the forget gate and an addition of the input gate times the candidate value. Below, the previous hidden state and current input feed four blocks: forget gate, input gate and output gate with sigmoid activations, and a candidate with tanh. The new hidden state is the output gate times tanh of the cell state.
The LSTM cell: gated, additive updates to the cell state preserve information and gradients.
Remembering a single input for 100 steps: a simple RNN versus an LSTM cell with different forget-gate biases.
import numpy as np

sig = lambda z: 1 / (1 + np.exp(-z))
T = 100
x = np.zeros(T); x[0] = 1.0                         # a single signal at the first step, then silence

def simple_rnn(w):
    h = 0.0
    for t in range(T):
        h = np.tanh(w * h + 2.0 * x[t])
    return h

def lstm(forget_bias):
    c = h = 0.0
    for t in range(T):
        f = sig(forget_bias)                        # forget gate (input-independent here)
        i = sig(-4 + 8 * x[t])                      # input gate opens only when x = 1
        g = np.tanh(2.0 * x[t])                     # candidate value
        c = f * c + i * g                           # additive cell-state update
        o = sig(4.0)
        h = o * np.tanh(c)
    return c, h

print("simple RNN, w = 0.9: memory after 100 steps =", f"{simple_rnn(0.9):.2e}")
print("simple RNN, w = 1.5: memory after 100 steps =", f"{simple_rnn(1.5):.3f}  (saturated, cannot forget)")
for b in (2, 5, 10):
    c, h = lstm(b)
    print(f"LSTM forget bias {b:2d}: f = {sig(b):.5f}, cell after 100 steps = {c:.3f}")
Output
simple RNN, w = 0.9: memory after 100 steps = 1.48e-05
simple RNN, w = 1.5: memory after 100 steps = 0.859  (saturated, cannot forget)
LSTM forget bias  2: f = 0.88080, cell after 100 steps = 0.000
LSTM forget bias  5: f = 0.99331, cell after 100 steps = 0.487
LSTM forget bias 10: f = 0.99995, cell after 100 steps = 0.942

The simple RNN either forgets the signal almost completely (\(w = 0.9\)) or saturates and holds a value permanently, unable to forget anything (\(w = 1.5\)). The LSTM's memory is controlled by its forget gate: with \(f = 0.88\) the stored value vanishes, with \(f = 0.993\) half remains after 100 steps (\(0.993^{100} = 0.51\)), and with \(f = 0.99995\) almost all of it does. In a trained LSTM, the gates are computed from the input, so the network learns when to keep, overwrite and output information.

The gated recurrent unit (GRU, 2014) simplifies the LSTM to two gates (update and reset) and no separate cell state, with fewer parameters and similar performance on many tasks.

Worked problem

LSTM parameter count

An LSTM layer has 300-dimensional inputs and 512 hidden units. How many parameters does it have? How many would a GRU of the same size have?

Show solution
  1. Each LSTM gate has weights for \([\mathbf{h}, \mathbf{x}]\): \(512 \times (512 + 300) = 415{,}744\) plus 512 biases \(= 416{,}256\).
  2. Four gate blocks (f, i, g, o): \(4 \times 416{,}256 = 1{,}665{,}024\).
  3. A GRU has three blocks: \(3 \times 416{,}256 = 1{,}248{,}768\).

Answer: About 1.67 million for the LSTM and 1.25 million for the GRU, 25% fewer.

Bidirectional and stacked RNNs

For tagging and classification, the whole input is available at once, so a token's label can depend on words after it as well as before ("Washington" is a person in "Washington said" and a place in "Washington state"). A bidirectional RNN runs one RNN forwards and another backwards and concatenates their hidden states at each position. Stacking several RNN layers, each taking the previous layer's hidden states as input, increases capacity. Bidirectional LSTMs with a conditional random field (CRF) output layer, which scores whole tag sequences so that invalid transitions such as O followed by I-PER are discouraged, were the standard NER architecture before transformers. ELMo (2018) used the hidden states of a deep bidirectional LSTM language model as contextual word embeddings (Lesson 2).

Encoder-decoder models

For seq2seq tasks such as translation, an encoder RNN reads the source sentence into its final hidden state, and a decoder RNN, initialized with that state, generates the target sentence one token at a time, each step conditioned on the previously generated token. During training, the decoder is fed the correct previous token rather than its own prediction, a technique called teacher forcing. This speeds training but creates exposure bias: at inference time the model must continue from its own, possibly wrong, outputs, a situation it never saw in training.

The basic encoder-decoder has a bottleneck: the entire source sentence must be compressed into one fixed-size vector, and translation quality fell sharply for long sentences. In 2014, Bahdanau, Cho and Bengio added attention: at each decoding step, the decoder computes a weighted average of all encoder hidden states, with weights based on how relevant each source position is to the current output. The decoder can then look back at the relevant source words directly. Attention dramatically improved translation and, as Lesson 6 shows, turned out to be powerful enough to replace recurrence entirely.

Two diagrams of encoder-decoder translation. Top: an encoder reads the source sentence le chat est noir into a single context vector, a narrow bottleneck that initializes the decoder, which outputs the cat is black. Bottom: the same model with attention, where each decoder step draws weighted connections to all encoder states, with the strongest weight from black to noir.
The fixed-vector bottleneck of a basic encoder-decoder, and attention over all encoder states.

Decoding: generating text

A trained language model gives a probability distribution over the next token. Turning this into text requires a decoding strategy:

  • Greedy decoding picks the most probable token at each step. It is fast but can miss sequences whose first token is less likely but whose total probability is higher.
  • Beam search keeps the \(k\) most probable partial sequences (the beam) at each step, extends each by every possible token, and keeps the top \(k\) again. It finds higher-probability outputs and is standard for translation and speech recognition, usually with a length penalty because shorter sequences have higher probabilities.
  • Sampling draws the next token from the distribution, producing varied text; it is used for open-ended generation, where the most probable text tends to be bland and repetitive.
Greedy decoding versus beam search on a toy language model.
import math

# a toy next-word model: probabilities depend only on the previous word
P = {"<s>":  {"the": 0.5, "a": 0.4, "</s>": 0.1},
     "the":  {"dog": 0.4, "cat": 0.35, "</s>": 0.25},
     "a":    {"small": 0.9, "</s>": 0.1},
     "small": {"cat": 0.9, "</s>": 0.1},
     "dog":  {"barked": 0.3, "</s>": 0.7},
     "cat":  {"</s>": 0.9, "sat": 0.1},
     "barked": {"</s>": 1.0}, "sat": {"</s>": 1.0}}

def greedy():
    seq, lp = ["<s>"], 0.0
    while seq[-1] != "</s>":
        w, p = max(P[seq[-1]].items(), key=lambda kv: kv[1])
        seq.append(w); lp += math.log(p)
    return seq[1:], lp

def beam(width, max_len=6):
    beams = [(["<s>"], 0.0)]
    done = []
    for _ in range(max_len):
        cand = []
        for seq, lp in beams:
            for w, p in P[seq[-1]].items():
                cand.append((seq + [w], lp + math.log(p)))
        cand.sort(key=lambda c: -c[1])
        beams = []
        for seq, lp in cand[:width]:
            (done if seq[-1] == "</s>" else beams).append((seq, lp))
        if not beams:
            break
    seq, lp = max(done, key=lambda c: c[1])
    return seq[1:], lp

s, lp = greedy()
print("greedy:      ", " ".join(s), f" p = {math.exp(lp):.3f}")
for k in (1, 2, 3):
    s, lp = beam(k)
    print(f"beam width {k}:", " ".join(s), f" p = {math.exp(lp):.3f}")
Output
greedy:       the dog </s>  p = 0.140
beam width 1: the dog </s>  p = 0.140
beam width 2: a small cat </s>  p = 0.292
beam width 3: a small cat </s>  p = 0.292

Greedy decoding chooses "the" (0.5) over "a" (0.4) and ends with "the dog", probability 0.14. Beam search with width 2 keeps "a" alive and finds "a small cat", with probability 0.29, more than twice as likely. Beam width 1 is identical to greedy.

Sampling is controlled by several parameters. Temperature \(T\) divides the logits (the scores before softmax) before sampling:

\[p_i = \frac{\exp(z_i/T)}{\sum_j \exp(z_j/T)}\]

Low temperature sharpens the distribution toward greedy decoding; high temperature flattens it, increasing diversity and the risk of nonsense. Top-k sampling keeps only the \(k\) most probable tokens; nucleus (top-p) sampling keeps the smallest set of tokens whose cumulative probability exceeds \(p\) (such as 0.9), adapting the number of candidates to how confident the model is.

Worked problem

Temperature

Three tokens have logits 2.0, 1.0 and 0.0. Compute the probabilities at \(T = 1\) and \(T = 0.5\).

Show solution
  1. \(T = 1\): \(e^2 = 7.39\), \(e^1 = 2.72\), \(e^0 = 1\); sum 11.11; probabilities 0.665, 0.245, 0.090.
  2. \(T = 0.5\): logits become 4, 2, 0: \(e^4 = 54.6\), \(e^2 = 7.39\), 1; sum 63.0; probabilities 0.867, 0.117, 0.016.

Answer: Halving the temperature raises the top token from 67% to 87% and nearly eliminates the third. As \(T \to 0\), sampling becomes greedy; as \(T \to \infty\), it becomes uniform.

Bar chart of the probabilities of five candidate next tokens at temperatures 0.5, 1 and 2. At temperature 0.5 the most likely token has most of the probability; at temperature 1 probabilities follow the model; at temperature 2 they are much flatter, with the least likely token gaining substantial probability.
Temperature sharpens or flattens the next-token distribution.

Limits of recurrence

RNNs process tokens one after another, so computation cannot be parallelized across a sequence during training: step 100 must wait for step 99. This makes training on long sequences and huge corpora slow on GPUs, which excel at parallel work. Even LSTMs struggle to use information hundreds of tokens back, and information must pass through every intermediate step. The transformer, introduced in 2017, removed recurrence and connected every position directly to every other with attention, solving both problems at the cost of computation that grows with the square of the sequence length. Since 2023, state space models such as Mamba and linear-attention architectures have revived recurrent ideas, offering constant memory per generated token, and some production models combine them with attention layers.

Lesson 5 applies encoder-decoder models and beam search to machine translation, and Lesson 6 shows how attention replaced recurrence.

Common mistakes

  • MistakeTraining RNNs without gradient clipping.

    InsteadClip the gradient norm to prevent exploding gradients from destroying training.

  • MistakeUsing a unidirectional model for tagging when the whole input is available.

    InsteadUse bidirectional context; labels often depend on following words.

  • MistakeAssuming greedy decoding finds the most probable sequence.

    InsteadGreedy choices can lead to lower total probability; beam search explores alternatives.

  • MistakeUsing beam search for open-ended creative generation.

    InsteadHigh-probability text tends to be bland and repetitive; use sampling with temperature or top-p.

  • MistakeIgnoring exposure bias when evaluating teacher-forced models.

    InsteadEvaluate on free-running generation, where errors can compound.

Summary and key terms

  • Sequence tasks include classification, tagging (such as BIO named entities), seq2seq and generation.
  • Neural language models share strength across similar words but fixed windows limit context.
  • RNNs carry a hidden state through the sequence with shared weights.
  • Backpropagation through time multiplies Jacobians, so gradients vanish or explode with distance.
  • LSTMs and GRUs use gates and additive updates to preserve information and gradients.
  • Bidirectional RNNs use both left and right context; CRFs score whole tag sequences.
  • Encoder-decoder models translate sequences; attention removed the fixed-vector bottleneck.
  • Greedy, beam search and sampling with temperature, top-k or top-p turn probabilities into text.
Recurrent neural network
A network that updates a hidden state at each step of a sequence with shared weights.
Backpropagation through time
Training an RNN by backpropagating through its unrolled steps.
Vanishing gradient
Exponential shrinking of gradients over many steps or layers.
LSTM
Long short-term memory: an RNN with gated, additive cell-state updates.
GRU
Gated recurrent unit: a simplified LSTM with update and reset gates.
BIO tagging
Labeling tokens as beginning, inside or outside an entity.
Teacher forcing
Feeding the correct previous token to a decoder during training.
Exposure bias
Mismatch between training on true tokens and generating from own outputs.
Beam search
Decoding that keeps the k best partial sequences at each step.
Temperature
A divisor of logits that sharpens or flattens the sampling distribution.
Nucleus sampling
Sampling from the smallest token set whose cumulative probability exceeds p.

Practice problems

Work each problem on paper first, then reveal the answer to check.

  1. Tag "Grace Hopper joined the Navy" with BIO tags (PER, ORG).

    Show answer

    B-PER I-PER O O B-ORG.

  2. Scalar RNN with w_hh = 0.9, w_xh = 1, h0 = 0, inputs (1, 0). Compute h2.

    Show answer

    h1 = tanh(1) = 0.762; h2 = tanh(0.686) = 0.595.

  3. If each backward step multiplies the gradient by 0.7, what fraction remains after 30 steps?

    Show answer

    0.7³⁰ = 2.3 × 10⁻⁵.

  4. What does the LSTM forget gate do?

    Show answer

    Scales how much of the previous cell state is kept.

  5. With a constant forget gate of 0.98 and no new input, what fraction of the cell state remains after 50 steps?

    Show answer

    0.98⁵⁰ = 0.36.

  6. Parameters in an LSTM with 128 inputs and 256 hidden units?

    Show answer

    4 × (256 × 384 + 256) = 394,240.

  7. Why did the basic encoder-decoder perform poorly on long sentences?

    Show answer

    The whole source had to be compressed into one fixed-size vector.

  8. Logits (1, 1, 0) at T = 1. Probability of the first token?

    Show answer

    e/(2e + 1) = 2.718/6.437 = 0.42.

  9. Probabilities 0.5, 0.3, 0.1, 0.1. Which tokens does top-p sampling with p = 0.75 keep?

    Show answer

    The first two (cumulative 0.8 ≥ 0.75).

  10. Why are RNNs slow to train on long sequences?

    Show answer

    Each step depends on the previous one, so steps cannot be computed in parallel.

Lesson quiz · 5 questions

Score 60% or more to complete this lesson.

Take the quiz