Attention Mechanisms and Transformers

The Problem with Sequential Models


Here is a translation system from 2014. You feed it a French sentence, it reads the words one at a time with a recurrent network, and after the last word it hands the decoder a single vector of 512 numbers. Everything the sentence meant — every name, every clause, every negation — has to survive inside those 512 floats.

On short sentences it works beautifully. Then someone feeds it a 40-word legal clause and the output turns to mush: names get dropped, a ne...pas vanishes and the translation asserts the opposite of the original. The team plots BLEU score against source-sentence length and finds a cliff: the curve is flat and healthy up to about 20 words, then falls off a shelf. This was measured, published, and reproduced. It was not a bug in anyone's code.

That cliff is one of three structural problems baked into recurrent sequence models. They are not implementation defects you can engineer around with a better optimiser or more layers. They come from the shape of the architecture itself. Understanding them precisely is the whole reason attention exists, so it is worth doing the arithmetic rather than taking it on faith.

Five costs that all come from reading one word at a timeRecurrence,step by stepStep t waits forstep t minus 1The GPU sitsidle between stepsPath length betweenwords grows with nGradients shrinkor blow up en routeOne fixed vectorholds the sentence
These are one problem seen from five sides: information and gradient both travel word by word.

What a sequential model actually does

A recurrent neural network (RNN) processes a sequence by maintaining a hidden state — a fixed-size vector that acts as its running memory. At each position it combines the previous state with the current input to produce a new state:

ht=tanh⁡(Whhht−1+Wxhxt+b)h_t = \tanh(W_{hh} h_{t-1} + W_{xh} x_t + b)

Here xtx_t is the embedding of the token at position tt, ht−1h_{t-1} is everything the model remembers so far, and WhhW_{hh}, WxhW_{xh} are weight matrices shared across every position. That sharing is the point: one set of weights handles a sequence of any length.

The LSTM (Long Short-Term Memory) and GRU (Gated Recurrent Unit) are refinements of the same idea. They add multiplicative gates — small learned sigmoids that decide how much of the old state to keep and how much new information to write. An LSTM cell carries a separate cell state ctc_t that is updated additively:

ct=ft⊙ct−1+it⊙c~tc_t = f_t \odot c_{t-1} + i_t \odot \tilde{c}_t

where ftf_t is the forget gate, iti_t the input gate, and ⊙\odot is elementwise multiplication. The additive update is a genuine improvement — we will see exactly why in a moment — but it changes the constant factor, not the structure.

Every recurrent architecture, from vanilla RNN to the most elaborate gated cell, shares one commitment: position tt cannot be computed until position t−1t-1 has finished. Everything that follows is a consequence of that single line.

Problem 1: the sequential bottleneck

Why the hardware sits idle

A modern GPU is a machine for doing tens of thousands of multiply-accumulates simultaneously. An A100 can sustain roughly 3×10143 \times 10^{14} floating-point operations per second in half precision. To get anywhere near that, you must hand it enormous chunks of independent arithmetic at once.

An RNN cannot. Take a realistic setup: batch of 64 sequences, length 512, hidden size 512. One timestep is a matrix multiply of shape (64×512)×(512×512)(64 \times 512) \times (512 \times 512), which is

64×512×512×2≈3.4×107 FLOPs.64 \times 512 \times 512 \times 2 \approx 3.4 \times 10^{7} \text{ FLOPs.}

At 3×10143 \times 10^{14} FLOP/s that is about 0.1 microseconds of actual work. But launching a GPU kernel costs roughly 5–10 microseconds of overhead. So the chip does 0.1 µs of maths and then waits 10 µs for the next instruction — a utilisation of about one percent. Multiply by 512 timesteps and you have spent roughly 5 milliseconds per layer per batch, essentially all of it in launch overhead and memory latency.

Now do the same total arithmetic as one big batched matmul, which is what a transformer layer does. All 512 positions go through their projections in a single kernel: 512×64×512×512×2≈1.7×1010512 \times 64 \times 512 \times 512 \times 2 \approx 1.7 \times 10^{10} FLOPs, one launch, near-peak utilisation. Same FLOPs, roughly 100× less wall-clock time.

The complexity picture

It is tempting to conclude that RNNs are simply cheaper in raw operation count and transformers just buy speed with hardware. Partly true, and worth quantifying. Let TT be sequence length and dd the model dimension.

QuantityRecurrent layerSelf-attention layer
Total operationsO(T⋅d2)O(T \cdot d^2)O(T2⋅d)O(T^2 \cdot d)
Sequential steps (depth of the dependency chain)O(T)O(T)O(1)O(1)
Longest path between two positionsO(T)O(T)O(1)O(1)

Put numbers in, with d=512d = 512:

TTRNN: Td2T d^2Attention: T2dT^2 dCheaper
6416.8 M2.1 Mattention, by 8×
512134 M134 Mtie (they cross at T=dT = d)
40961.07 G8.6 GRNN, by 8×

So for typical sentence lengths, attention is not even more expensive in FLOPs — and the FLOPs it does spend can all be issued at once. The sequential-steps row is the one that decides training time, and it is the row where recurrence loses by a factor of TT regardless of how the FLOPs compare.

Problem 2: gradients that vanish or explode

Where the gradient comes from

Training uses backpropagation through time: unroll the recurrence into TT copies of the same layer and backpropagate. If the loss at position TT depends on the input at position 1, the gradient must travel back through every intermediate state. By the chain rule:

∂LT∂h1=∂LT∂hT∏t=2T∂ht∂ht−1\frac{\partial \mathcal{L}_T}{\partial h_1} = \frac{\partial \mathcal{L}_T}{\partial h_T} \prod_{t=2}^{T} \frac{\partial h_t}{\partial h_{t-1}}

That is a product of T−1T-1 Jacobian matrices. For the vanilla RNN, each factor is

∂ht∂ht−1=Whh⊤ diag ⁣(1−ht2)\frac{\partial h_t}{\partial h_{t-1}} = W_{hh}^{\top} \, \mathrm{diag}\!\left(1 - h_t^2\right)

because the derivative of tanh⁡(z)\tanh(z) is 1−tanh⁡2(z)1 - \tanh^2(z), which is at most 1 and typically much less once the units saturate.

What goes wrong, in numbers

Suppose the effective per-step multiplier — the product of the weight matrix's dominant singular value and the average tanh⁡\tanh derivative — settles at 0.9. Then over TT steps the gradient scales by 0.9T0.9^{T}:

Distance TTMultiplier 0.9T0.9^TMultiplier 1.1T1.1^T
100.3492.59
500.0052117
1000.000026613,780

At distance 100 with a multiplier of 0.9, the gradient arriving at position 1 is 0.0027% of the gradient at position 100. In float32 that is not literally zero, but it is drowned out: the update to WhhW_{hh} is dominated entirely by short-range dependencies, and the long-range signal contributes noise. The model does not learn the long dependency slowly — it never learns it at all, because the short-range terms move the weights faster than the long-range term can accumulate.

The multiplier 1.1 is the opposite failure. A gradient scaled by 13,780 produces a parameter update that hurls the weights out of the region where the loss is finite. You see it as a loss curve that is decreasing normally and then reports nan on a single step.

Vanishing and exploding gradients are the same phenomenon — repeated multiplication of a number that is not exactly 1 — viewed from either side of the knife edge.

A concrete failure case

Consider language modelling on this sentence:

Text
The keys to the cabinet that stood in the hallway of my grandmother'shouse, which she had bought shortly after the war, ___ on the table.

The blank is were, not was, because the subject is keys (plural), 26 tokens back. To learn this, the model needs gradient to flow from the verb all the way to keys. With a per-step multiplier of 0.9 that is 0.926≈0.0650.9^{26} \approx 0.065 — 6.5% of the signal. Meanwhile the nearest noun, war (singular), is 4 tokens away and gets 0.94≈0.660.9^{4} \approx 0.66, ten times as much gradient. The model learns the wrong rule — agree with the closest noun — because that rule receives ten times the training signal.

The partial fixes, and where they stop

FixWhat it doesWhere it still fails
LSTM / GRU gatingThe cell state update ct=ft⊙ct−1+…c_t = f_t \odot c_{t-1} + \dots is additive, so the Jacobian is diag(ft)\mathrm{diag}(f_t) rather than a full matrix product. If ft≈1f_t \approx 1 the gradient passes through nearly untouched.ftf_t is a sigmoid, so ft<1f_t < 1 always. With ft=0.99f_t = 0.99, 0.99200≈0.1340.99^{200} \approx 0.134. It buys roughly an order of magnitude in usable range — hundreds of steps instead of tens — not unlimited range.
Gradient clippingRescales the gradient when its global norm exceeds a threshold. Directly prevents explosion.Does nothing for vanishing. You cannot clip a gradient upwards without amplifying noise along with signal.
Orthogonal / identity initialisationStarts WhhW_{hh} with singular values near 1, so the initial multiplier is near the knife edge.Training moves the weights off that edge within a few hundred steps. It helps the start, not the steady state.
Truncated BPTTBackpropagate only kk steps (say 35) and detach beyond that. Keeps memory and compute bounded.Makes the problem official policy: dependencies longer than kk receive exactly zero gradient by construction.
Bidirectional RNNRuns a second RNN right-to-left and concatenates, so each position sees both sides.Halves the worst-case distance, does not change its growth with TT. Also unusable for generation, where the right context does not exist yet.

Problem 3: the fixed-size memory bottleneck

The encoder-decoder translation setup makes the third problem sharpest. The encoder RNN reads the source sentence and produces hidden states h1,…,hTh_1, \dots, h_T. The decoder is then initialised from hTh_T alone — one vector, usually 512 or 1024 dimensions. Every hidden state before it is discarded.

Count what has to fit. A 50-token source sentence drawn from a 50,000-word vocabulary carries at minimum 50×log⁡2(50,000)≈50×15.6≈78050 \times \log_2(50{,}000) \approx 50 \times 15.6 \approx 780 bits of lexical identity, before you count word order, syntactic structure, or coreference. A 512-dimensional float32 vector has 16,384 raw bits, but its usable capacity is far smaller: the values are continuous, noisy, and passed through a decoder that must be smooth enough to generalise. Empirically, the effective capacity runs out somewhere around 20–30 tokens.

This is exactly the cliff in the opening scenario. The encoder is a lossy compressor with a fixed output size, and translation quality tracks how much got lost.

What makes attention different

Attention removes the compression step. Instead of forcing everything through hTh_T, keep all TT encoder states and let the decoder build a different summary at every output step:

ci=∑j=1Tαijhj,∑j=1Tαij=1c_i = \sum_{j=1}^{T} \alpha_{ij} h_j, \qquad \sum_{j=1}^{T} \alpha_{ij} = 1

The weights αij\alpha_{ij} say how much output position ii should draw on input position jj. They are computed fresh for each ii, so the "summary" is task-specific and query-specific rather than one-size-fits-all.

Work through a tiny example. Source: le chat noir dort, four encoder states h1…h4h_1 \dots h_4. The decoder is producing the English word black, and the alignment weights come out as:

Source tokenlechatnoirdort
α3j\alpha_{3j}0.030.060.880.03

They sum to 0.03+0.06+0.88+0.03=1.000.03 + 0.06 + 0.88 + 0.03 = 1.00, as a softmax output must. The context vector is c3=0.03h1+0.06h2+0.88h3+0.03h4c_3 = 0.03 h_1 + 0.06 h_2 + 0.88 h_3 + 0.03 h_4 — overwhelmingly h3h_3, the state that read noir. Nothing told the model that noir means black; the alignment was learned end to end from translation pairs alone.

Two structural consequences follow immediately. First, capacity now grows with the input: a 50-token sentence gets 50 states to draw on, not one. Second, and more importantly for training, the gradient from output position ii reaches input position jj through the single multiplication by αij\alpha_{ij} — one hop, not ∣i−j∣|i - j| hops. The 0.9260.9^{26} decay from the earlier example becomes a factor of αij\alpha_{ij} itself, which is 0.88 in the example above rather than 0.065.

Attention converts a distance-dependent gradient path into a constant-length one. That, more than the extra capacity, is why it works.

How the three problems interlock

They are not three independent complaints. They all trace back to the same design commitment — information travels between positions only by passing through every position in between.

Consequence of that commitmentSymptom you observe
Position tt needs position t−1t-1 firstNo parallelism; GPU at ~1% utilisation; training a long-sequence model takes days
Gradient traverses ∣i−j∣|i-j| JacobiansLong-range dependencies get exponentially less training signal than short-range ones
All history compressed into one stateQuality collapses past ~20–30 tokens; specific details get dropped

Notice that fixing any one in isolation does not help much. Gating (fix 2) does not restore parallelism. A bigger hidden state (fix 3) makes the per-step matmul more expensive and the sequential bottleneck worse. The three constraints trade against each other precisely because they share a root cause.

What this means when you build something

The practical takeaway is a diagnostic habit: when a sequence model underperforms, ask which of the three constraints is binding before you reach for more layers or more data.

SymptomLikely binding constraintWhat to do
Loss goes nan on a single step, often after hundreds of healthy stepsExploding gradientsClip the global gradient norm to 1.0; check for a bad batch with an extreme sequence length
Accuracy is fine on short inputs, degrades smoothly as inputs get longerFixed-size memory bottleneckAttention over all positions, not a single final state
Model consistently uses the nearest plausible antecedent rather than the correct distant oneVanishing gradientsShorten the gradient path — attention gives every pair a one-hop connection
Training is compute-bound with the GPU at low utilisationSequential bottleneckAn architecture whose positions are independent given the inputs

And a caution in the other direction, because it is a genuine trade and not a free win. Attention's cost grows as T2T^2. At T=512T = 512, d=512d = 512 the two architectures cost the same; at T=4096T = 4096 attention costs eight times more arithmetic and, worse, its memory grows quadratically too — the T×TT \times T matrix of weights must exist. Recurrence's memory is O(T)O(T). That is why long-context work spends so much effort on sparse, linear and chunked attention variants: the problem that recurrence solved for free is the one attention had to pay for.

The single sentence to hold on to: recurrence makes distance expensive, and attention makes width expensive. Everything in the architectures that followed is a negotiation between those two costs.