skip to content

RNNs, LSTMs and Attention

You will learn how recurrent nets process sequences, exactly which gradient problem LSTM gates solve, and how attention removed the seq2seq bottleneck. Interviewers love 'walk me from RNN to attention' because it tests whether you understand the lineage transformers came from.

on this pageshow

explore

questions

page 1 of 2

How does an attention-based seq2seq decoder build its context vector at each output step?

level: juniorimportance: must knowfreq 82%

answer

  1. the decoder looks back every step
  2. one score per source position
  3. normalise across source, not target
  4. weights sum to one
  5. weighted average of encoder states

basics

~20 s

At each output step the decoder scores its current state against every encoder state, softmaxes those scores over source positions into weights summing to one, then averages the encoder states with those weights — that average is the context vector.

solid answer

~50 s

The encoder runs once and leaves one hidden state per source position, `h_1 ... h_S`. At output step `t` the decoder holds a state `s_t`, and a score function produces one scalar `e_tj = score(s_t, h_j)` for every source position. Those `S` scores go through a softmax **across source positions**, `a_tj = exp(e_tj) / sum_k exp(e_tk)`, so they are non-negative and sum to one. The context vector is the weighted average `c_t = sum_j a_tj * h_j`, a point inside the convex hull of the encoder states, pulled toward whichever positions scored highest. It is then combined with the decoder state to predict the next token. Nothing supervises the weights — they are learned end to end from the training loss, which is why the resulting `T x S` matrix is called a *soft* alignment.

code

python · 15 lines
python
import math

enc = [[1.0, 0.0], [0.0, 1.0], [0.5, 0.5]]  # three encoder states
dec = [0.8, 0.2]                             # decoder state at this output step
mask = [True, True, False]                   # third source position is padding

scores = [sum(d * hk for d, hk in zip(dec, h)) for h in enc]      # dot score
scores = [s if keep else -1e9 for s, keep in zip(scores, mask)]   # pads first
exps = [math.exp(s) for s in scores]
total = sum(exps)
alpha = [e / total for e in exps]
context = [sum(a * h[k] for a, h in zip(alpha, enc)) for k in (0, 1)]

print([round(a, 4) for a in alpha])    # [0.6457, 0.3543, 0.0]
print([round(c, 4) for c in context])  # [0.6457, 0.3543]

go deeper

for a junior

Be ready to recite the three steps in order: score every encoder state against the current decoder state, softmax those scores over source positions, take the weighted average. Say out loud that the weights sum to one.

for a middle

Explain why the softmax axis is the source axis and what that implies for the T-by-S alignment matrix, and be able to state that the encoder runs once while scoring and mixing repeat each output step.

for a senior

Expect to talk about cost and correctness in practice: score count growing as source length times target length, padded positions masked before the softmax rather than zeroed after, and what a plotted alignment map tells you about a run.

for a principal

Own the framing that soft attention is a differentiable relaxation of hard selection, trained with no alignment supervision, and be ready to argue when that interpretability is worth relying on for debugging versus when it is merely suggestive.

## The setup A sequence-to-sequence model has two halves. The **encoder** reads the source sequence (say a 50-token sentence, or a few hundred audio frames) and produces one hidden vector per input position: `h_1, h_2, ..., h_S`. The **decoder** emits the output sequence one token at a time, carrying its own recurrent state `s_1, s_2, ..., s_T`. Attention is the mechanism that lets the decoder, at every one of its `T` steps, look back at all `S` encoder states and decide how much of each to use. ## The three operations, in order **1. Score.** A scoring function assigns one real number per source position: ``` e_tj = score(s_t, h_j) for j = 1..S ``` The score measures how relevant source position `j` is to the token about to be emitted at step `t`. It is a scalar, not a vector — one number per (output step, source position) pair. **2. Normalise.** The `S` raw scores are pushed through a softmax **over the source axis**: ``` a_tj = exp(e_tj) / sum_{k=1..S} exp(e_tk) ``` This is the step candidates most often get backwards. The normaliser runs over *source positions for a fixed output step*, not over output steps. The consequence: for each output step, `a_t1 + a_t2 + ... + a_tS = 1`, every weight is in `[0, 1]`, and the vector `a_t` is a probability distribution over where to look. If you stack all steps you get a `T x S` alignment matrix whose **rows** sum to one; its columns need not sum to anything in particular, which is exactly why a source word can be attended to twice (over-translation) or never (under-translation). **3. Mix.** The context vector is the weighted average of the encoder states: ``` c_t = sum_{j=1..S} a_tj * h_j ``` Because the weights are non-negative and sum to one, `c_t` lives in the convex hull of the encoder states — it is a soft, differentiable selection. A one-hot `a_t` would be hard selection (pick exactly one source position); softmax gives the smooth relaxation that gradients can flow through. That differentiability is the whole trick: no alignment labels are needed, and the score function's parameters are trained by the same next-token loss that trains everything else. `c_t` is then combined with `s_t` — concatenated and passed through a small transform, or fed into the recurrent step — before the output distribution over the vocabulary is produced. ## What is recomputed and what is not The encoder runs **once per example**. Its states `h_j` are computed and then reused at every output step. What is redone at each of the `T` steps is only the cheap part: `S` scores, one softmax of length `S`, and one weighted sum. So the cost is `S x T` score evaluations — for a 50-token source and a 60-token target, 3000 of them — plus `S x T` scalar-times-vector accumulations. Each is trivial; it is the product of the lengths that grows, and that is what makes very long sources expensive. ## Padding Real batches pad short sequences up to the longest in the batch. Those padded positions still have encoder states, and the softmax will happily assign them weight. The fix belongs **before** the softmax: set `e_tj` at padded positions to a large negative value, so `exp(e_tj)` is effectively zero and the surviving weights still sum to one. Zeroing the weights *after* the softmax is the common bug — the pads have already eaten part of the normaliser, so the remaining weights sum to less than one. ## Why the weights are interpretable Plotting the `T x S` matrix as a heatmap gives a picture of which source positions drove which output tokens. For translation between similarly ordered languages it looks like a bright near-diagonal band. That interpretability is a by-product, not a guarantee: the weights show where the model read from, which is correlated with, but not the same as, a linguist's alignment. ## Common mistakes - Believing the context is the encoder's last hidden state. That is the no-attention baseline; the point of attention is that `c_t` changes with `t`. - Softmaxing over the wrong axis, which would make the weights compete across output steps. - Thinking the encoder is re-run per output step. It is not; only the scoring and mixing repeat. - Treating alignment weights as learned constants. They are computed from the current states, so they differ per example and per step.

  • What length does the alignment weight vector have at one output step, and which axis does the softmax run over?
    It has one entry per source position, so its length is the source length `S`. The softmax runs over that source axis for a fixed output step. Stack all `T` steps and you get a `T x S` matrix whose rows each sum to one; columns are unconstrained, which is how a source position can be attended to repeatedly or skipped entirely.
  • Does the encoder have to run again for each output step?
    No. The encoder runs once per example and its `S` hidden states are cached. Each output step only recomputes `S` scores, one softmax, and one weighted sum over those cached states. That is why attention adds cost proportional to source length times target length rather than re-encoding cost.
  • How many alignment scores does this cost for a 50-token source and a 60-token target?
    3000 — one score per (source position, output step) pair, so `50 x 60`. Each score is cheap, but the count is the product of the two lengths, so doubling the source doubles the scoring work for every output token. This product is the reason long-source attention gets expensive well before the encoder itself does.

saying these in an interview costs you the question

  • Says the softmax is taken over target positions instead of source
  • Claims the context vector is the encoder's final hidden state
  • Thinks the encoder is re-run at every output step
  • Describes alignment weights as fixed learned parameters per position
  • Says the context vector concatenates all encoder states

context

open as a page

What does backpropagation through time do to a recurrent network's computation graph?

level: juniorimportance: must knowfreq 66%

basics

~10 s

Backpropagation through time unrolls the recurrent loop into a chain with one copy of the cell per time step, then runs ordinary backpropagation over that finite graph. All copies share one set of weights.

open as a page

In a vanilla encoder-decoder RNN for translation, what does the encoder pass to the decoder?

level: juniorimportance: must knowfreq 58%

basics

~10 s

Only its final hidden state: one fixed-size vector, plus the cell state if the encoder is an LSTM. That single tensor initialises the decoder, and nothing else crosses between the two stacks.

open as a page

Why must a batch of variable-length sequences be padded, and what does the mask do?

level: juniorimportance: must knowfreq 72%

basics

~20 s

A batch must be one rectangular array, so shorter sequences are padded out to the longest length. The mask marks which steps are real, keeping pad steps out of the loss, out of pooling, and out of the state you read.

open as a page

In a vanilla RNN, how does the hidden state at step t depend on earlier inputs?

level: juniorimportance: must knowfreq 78%

basics

~20 s

A vanilla RNN computes h_t = tanh(W_xh x_t + W_hh h_(t-1) + b). Since h_(t-1) was built the same way from h_(t-2), the state at step t is a fixed-size summary of every input so far.

open as a page

How do you window a half-hourly electricity load series into training examples for a recurrent forecaster?

level: juniorimportance: must knowfreq 72%

basics

~20 s

Slide a fixed-length input window over the series, pairing each window with the next H values as its target. Size the window to cover the dominant seasonal cycle, split chronologically first, and drop any window whose target crosses the split.

open as a page

How do sequence-to-label, per-step tagging and sequence-to-sequence framings of a recurrent model differ?

level: juniorimportance: must knowfreq 70%

basics

~20 s

They differ in output shape. Sequence-to-label emits one prediction for the whole input, per-step tagging emits one prediction aligned to each input step, and sequence-to-sequence emits a new sequence whose length need not match the input.

open as a page

In seq2seq training, what is teacher forcing and why is it the default?

level: juniorimportance: must knowfreq 70%

basics

~20 s

Teacher forcing feeds the decoder the ground-truth previous token at every training step instead of its own prediction. It keeps training stable and fast, because each step is conditioned on a correct prefix and no generation loop is needed.

open as a page

Why does encoder-decoder RNN translation quality collapse as the source sentence gets longer?

level: middleimportance: must knowfreq 68%

basics

~20 s

One fixed-size vector carries a fixed information budget, so a longer source forces the encoder to discard more, and to choose what to keep before the decoder emits a word. Quality stays flat, then falls steeply.

open as a page

What do a GRU's update and reset gates do, and how does that differ from an LSTM's gating?

level: middleimportance: must knowfreq 72%

basics

~20 s

A GRU has two gates. The update gate blends the old hidden state with a new candidate; the reset gate controls how much past state feeds that candidate. An LSTM uses three gates plus a separate cell state.

open as a page

Why does an LSTM's additive cell-state update keep gradients alive over long sequences?

level: middleimportance: must knowfreq 72%

basics

~20 s

The route from c_(t-1) to c_t is a multiply by the forget gate plus an added term, with no weight matrix or activation derivative in between. A forget gate near 1 lets gradient pass back almost unchanged.

open as a page

In one LSTM step, what does each of the forget, input and output gates compute?

level: middleimportance: must knowfreq 84%

basics

~20 s

The forget gate scales the previous cell state, the input gate scales a tanh candidate, and their sum is the new cell state: c_t = fc_(t-1) + ig. The output gate scales tanh(c_t) into the emitted hidden state.

open as a page

Why does a vanilla RNN reuse the same weight matrices at every time step?

level: middleimportance: must knowfreq 62%

basics

~20 s

Reusing one input-to-hidden matrix, one hidden-to-hidden matrix and one bias makes the layer a single function applied repeatedly. The parameter count is then independent of sequence length, any length runs, and what is learned at step 1 applies at step 900.

open as a page

Why does an RNN's hidden-state chain block parallel training across time steps?

level: middleimportance: must knowfreq 66%

basics

~20 s

Each hidden state is computed from the previous one, so step t cannot start until step t-1 finishes. Training a length-T sequence is T dependent matrix multiplies in a row, and no amount of hardware shortens that chain.

open as a page

Why can a bidirectional recurrent layer not be used in a live captioning system?

level: middleimportance: must knowfreq 58%

basics

~20 s

A bidirectional layer runs a second recurrence from the end of the sequence back to the start, so its output at any step depends on steps that come after it. Live captioning has no future available yet.

open as a page

Why do gradients vanish across time steps in a simple RNN trained with BPTT?

level: middleimportance: must knowfreq 72%

basics

~10 s

Backprop through time multiplies by the same recurrent Jacobian once per step. Factors below one in magnitude shrink that product geometrically, so gradients from distant steps arrive at zero; factors above one explode it.

open as a page

Why does a caption decoder score well teacher-forced but degenerate when it feeds itself?

level: seniorimportance: must knowfreq 58%

basics

~20 s

Exposure bias. The decoder was only ever trained on correct prefixes, so once it emits one wrong token it is conditioning on a prefix the training data never contained, and errors compound. Teacher-forced perplexity never measures that regime.

open as a page

How many parameters does an LSTM cell with 300-dimensional inputs and 256 hidden units have?

level: juniorimportance: should knowfreq 54%

basics

~20 s

Four blocks — forget, input, output and candidate — each hold an input matrix, a recurrent matrix and a bias: 4 * (300 + 256 + 1) * 256 = 570,368 parameters, four times a plain recurrent cell.

open as a page

What does a sequence model lose about token order when recurrence is removed?

level: juniorimportance: should knowfreq 44%

basics

~20 s

Order itself. A recurrent net reads tokens one at a time, so 'dog bites man' and 'man bites dog' end in different states. A layer that mixes all positions at once sees only a bag of tokens unless position is supplied as input.

open as a page

How do Bahdanau's additive attention score and Luong's multiplicative score differ?

level: middleimportance: should knowfreq 55%

basics

~20 s

Bahdanau scores a decoder-encoder pair with a small one-hidden-layer network (tanh, then a learned vector), tolerating different sizes. Luong multiplies the two states directly, optionally through a learned matrix: cheaper, but the plain dot needs matching sizes.

open as a page

Why does backprop through time sum, not average, the per-step gradients of shared recurrent weights?

level: middleimportance: should knowfreq 46%

basics

~20 s

The same matrix is used at every time step, so the loss depends on it along T separate paths and its total derivative is the sum of those contributions. Averaging would give the gradient of a rescaled objective instead.

open as a page

Should a classifier that reads an RNN's final hidden state use pre- or post-padding?

level: middleimportance: should knowfreq 45%

basics

~20 s

Post-padding puts pad steps last, so the state at the final index is the state after the pads, not after the last real token. Pre-padding hides that, but the real fix is to read each sequence's state at index length minus one.

open as a page

Why does a recurrent forecaster that feeds its own predictions back in drift over a 14-day horizon?

level: middleimportance: should knowfreq 58%

basics

~20 s

Because every step after the first is conditioned on a predicted value rather than an observed one. Small one-step errors re-enter as inputs and accumulate across the horizon, and a one-step training loss never penalised the 14-step trajectory at all.

open as a page

How does scheduled sampling mitigate exposure bias, and what does it cost?

level: middleimportance: should knowfreq 40%

basics

~20 s

Scheduled sampling flips a coin at each decoder step: feed the ground-truth previous token, or the model's own. The self-feeding probability is annealed upward during training, so the decoder practises recovering from its own mistakes.

open as a page

An attention speech recognizer repeats words and its alignment jumps backwards — what is wrong?

level: seniorimportance: should knowfreq 38%

basics

~20 s

The alignment has stopped moving forward: the decoder re-attends to audio it already consumed, so it emits the same words again. Plot the alignment map, mask padded frames, and constrain attention to a window that can only advance.

open as a page

How do you choose the truncation window length in truncated backprop through time?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Choose the shortest window that still spans the dependency the model must learn, because the gradient reaches back only that far. Backward work and retained per-step values grow roughly linearly with the window, so longer is not free.

open as a page

Why doesn't widening a seq2seq context vector from 1,000 to 4,000 dimensions fix long inputs?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Width buys capacity, not access. The decoder still reads one static summary committed before any target word is known, so widening shifts the length breakpoint a little while leaving the same falling curve - and it costs roughly quadratic parameters.

open as a page

Why can't a GRU learn a dependency 3,000 steps back even with well-behaved gates?

level: seniorimportance: should knowfreq 52%

basics

~10 s

Gating stops gradients vanishing along an open path, but the fact must still survive in a fixed-size state through every intervening step, and truncated training windows usually never connect the two positions at all.

open as a page

When should an RNN carry its hidden state across mini-batches on a never-ending accelerometer stream?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Carry it only when each batch row truly continues that row's stream from the previous batch: feed batches in order, stop the gradient at each handoff, and reset the state at real breaks such as the device coming off.

open as a page

How should you scale 3,000 SKU series whose volumes span four orders of magnitude for one shared forecaster?

level: seniorimportance: should knowfreq 50%

basics

~20 s

Scale each series by its own statistics, not the pooled distribution. A global scaler squashes a two-unit-a-day SKU toward a constant while a 20,000-unit SKU dominates the loss. Fit each scale on training data only, and invert it on forecasts.

open as a page

showing 1–30 of 39