Course contents 13 / 37

13 / 37 · Neural Sequence Models

LSTM and GRU

Gated cells give the recurrent state an additive memory line whose gradient factor is a gate value rather than a weight matrix, so information can survive many steps; the note writes out both cells, traces one LSTM step numerically, and counts what the gates cost.

On this page 10

The plain recurrent cell of the previous note overwrites its whole state at every step through a multiplication by WhW_h, and the gradient across kk steps is a product of kk such factors. The fix proposed by Hochreiter and Schmidhuber (1997) and simplified by Cho et al. (2014) is architectural: give the cell a path along which the state is added to rather than transformed, and let the network learn, per coordinate and per step, how much to keep, write and read.

Notation. As before, xt∈Rdx_t \in \mathbb R^d is the input, ht∈RHh_t \in \mathbb R^H the hidden state. The LSTM adds a cell state ct∈RHc_t \in \mathbb R^H. σ\sigma is the logistic function with range (0,1)(0, 1); tanh⁡\tanh has range (−1,1)(-1, 1); ⊙\odot is element-wise multiplication. Each gate has its own weights W⋅W_{\cdot} (on xtx_t), U⋅U_{\cdot} (on ht−1h_{t-1}) and bias b⋅b_{\cdot}.

Baseline: one state, overwritten every step#

ht=tanh⁡(Wxxt+Whht−1+b)h_t = \tanh(W_x x_t + W_h h_{t-1} + b). The state is recomputed from scratch at each step; nothing in the cell can say “leave coordinate 7 alone”. Whether old information survives depends on WhW_h, which is shared across all steps and all coordinates, and the gradient factor per step is diag(1−ht2)Wh\mathrm{diag}(1 - h_t^2) W_h.

LSTM#

Four quantities are computed from (xt,ht−1)(x_t, h_{t-1}), three of them gates in (0,1)(0, 1) and one a candidate value in (−1,1)(-1, 1):

ft=σ(Wfxt+Ufht−1+bf)forget gate: how much of ct−1 to keep,it=σ(Wixt+Uiht−1+bi)input gate: how much of the candidate to write,c~t=tanh⁡(Wcxt+Ucht−1+bc)candidate: what to write,ot=σ(Woxt+Uoht−1+bo)output gate: how much of the memory to expose.\begin{aligned} f_t &= \sigma(W_f x_t + U_f h_{t-1} + b_f) && \text{forget gate: how much of } c_{t-1} \text{ to keep},\\ i_t &= \sigma(W_i x_t + U_i h_{t-1} + b_i) && \text{input gate: how much of the candidate to write},\\ \tilde c_t &= \tanh(W_c x_t + U_c h_{t-1} + b_c) && \text{candidate: what to write},\\ o_t &= \sigma(W_o x_t + U_o h_{t-1} + b_o) && \text{output gate: how much of the memory to expose}. \end{aligned}

Then the memory is updated and the working state read from it:

ct=ft⊙ct−1+it⊙c~t,ht=ot⊙tanh⁡(ct).c_t = f_t \odot c_{t-1} + i_t \odot \tilde c_t, \qquad h_t = o_t \odot \tanh(c_t).

Every operation is per coordinate. A coordinate of cc with f≈1f \approx 1 and i≈0i \approx 0 is copied unchanged for as many steps as the gates stay that way; one with f≈0f \approx 0 and i≈1i \approx 1 is overwritten. The sigmoid range makes the gates fractions and the tanh⁡\tanh range makes the candidate and the exposed value bounded, so ctc_t can grow only by repeated addition of bounded values, not geometrically.

Worked example#

One coordinate, one step. Previous memory ct−1=2c_{t-1} = 2, previous state ht−1=1h_{t-1} = 1, input xt=1x_t = 1. Take scalar weights (toy values) so that each pre-activation is a small sum:

GatePre-activationValue
forget ff1.63⋅1+2.70⋅1+1.62=5.951.63 \cdot 1 + 2.70 \cdot 1 + 1.62 = 5.95σ(5.95)=0.997\sigma(5.95) = 0.997
input ii1.65⋅1+2.00⋅1+0.62=4.271.65 \cdot 1 + 2.00 \cdot 1 + 0.62 = 4.27σ(4.27)=0.986\sigma(4.27) = 0.986
candidate c~\tilde c0.94⋅1+1.41⋅1−0.32=2.030.94 \cdot 1 + 1.41 \cdot 1 - 0.32 = 2.03tanh⁡(2.03)=0.966\tanh(2.03) = 0.966
output oo−0.19⋅1+4.38⋅1+0.59=4.78-0.19 \cdot 1 + 4.38 \cdot 1 + 0.59 = 4.78σ(4.78)=0.992\sigma(4.78) = 0.992

Memory update: ct=0.997×2+0.986×0.966=1.995+0.953=2.948c_t = 0.997 \times 2 + 0.986 \times 0.966 = 1.995 + 0.953 = 2.948. Exposed state: ht=0.992×tanh⁡(2.948)=0.992×0.9945=0.986h_t = 0.992 \times \tanh(2.948) = 0.992 \times 0.9945 = 0.986.

The old memory survived almost entirely (forget gate near 1) and a new value of about 0.950.95 was added. Had the forget gate come out at 0.10.1, the memory would have dropped to 0.2+0.950.2 + 0.95: same input, different decision, chosen by weights that see xtx_t and ht−1h_{t-1}.

Step by step#

Figure

One LSTM step: what goes where

An LSTM step with numbersTop, the memory line: cₜ₋₁ = 2 is multiplied by the forget gate 0.997, and 0.953 is added to the result 1.995, giving cₜ = 2.948. Bottom, four blocks: forget 0.997, input 0.986, candidate 0.966, output 0.992. Right, hₜ = 0.992 · tanh(2.948) = 0.986.new compared with the RNN: the memory line c2ct−1×1.9953+×0.9532.948ct4tanh×0.986ht60.997σ(5.95)1.63 · x2.70 · h+ 1.62forget f10.986σ(4.27)1.65 · x2.00 · h+ 0.62input i20.966tanh(2.03)0.94 · x1.41 · h− 0.32candidate c̃20.992σ(4.78)−0.19 · x4.38 · h+ 0.59output o5step input: hₜ₋₁ = 1, xₜ = 1inputstateweights, gatesoutputAn LSTM step with numbersTop, the memory line: cₜ₋₁ = 2 is multiplied by the forget gate 0.997, and 0.953 is added to the result 1.995, giving cₜ = 2.948. Bottom, four blocks: forget 0.997, input 0.986, candidate 0.966, output 0.992. Right, hₜ = 0.992 · tanh(2.948) = 0.986.new compared with the RNN: the memory line c2ct−1×1.9953+×0.9532.948ct4tanh×0.986ht60.997σ(5.95)1.63 · x2.70 · h+ 1.62forget f10.986σ(4.27)1.65 · x2.00 · h+ 0.62input i20.966tanh(2.03)0.94 · x1.41 · h− 0.32candidate c̃20.992σ(4.78)−0.19 · x4.38 · h+ 0.59output o5step input: hₜ₋₁ = 1, xₜ = 1inputstateweights, gatesoutput
The numbers of the worked example. Each of the four blocks takes the same step input (hₜ₋₁ = 1, xₜ = 1), multiplies it by its own weights, adds a bias and passes the sum through σ or tanh. The forget gate (1) decides how much of the old memory cₜ₋₁ = 2 survives (3); the input gate and the candidate (2) decide what to write (4): cₜ = 1.995 + 0.953 = 2.948. The output gate (5) decides how much of tanh(cₜ) to expose as hₜ (6). The candidate c̃ is the plain RNN update; what is new compared with the RNN is the memory line at the top, along which the state is only multiplied by a gate and added to.

Following the numbers in the figure:

  1. Forget gate. f=σ(1.63⋅1+2.70⋅1+1.62)=0.997f = \sigma(1.63 \cdot 1 + 2.70 \cdot 1 + 1.62) = 0.997: how much of the old memory to keep, coordinate by coordinate.
  2. Input gate and candidate. i=0.986i = 0.986 says how much to write, c~=0.966\tilde c = 0.966 says what to write. The candidate is the plain RNN update of the previous note, with its own weights.
  3. Keep. The old memory is multiplied by the forget gate: 0.997×2=1.9950.997 \times 2 = 1.995.
  4. Write. The gated candidate 0.986×0.966=0.9530.986 \times 0.966 = 0.953 is added: ct=2.948c_t = 2.948. Steps 3 and 4 are the only operations on the memory line, a multiplication by a number in (0,1)(0, 1) and an addition.
  5. Output gate. o=0.992o = 0.992: how much of the memory to expose.
  6. Read. ht=o⋅tanh⁡(ct)=0.992×0.9945=0.986h_t = o \cdot \tanh(c_t) = 0.992 \times 0.9945 = 0.986. Both ctc_t and hth_t go on to step t+1t + 1: the gates of the next step read hth_t, and the memory line carries ctc_t.

Against the RNN figure of the previous note, the inputs and the working state hth_t are drawn the same way; what is new is the memory line at the top, in the amber outline, and the three gates that act on it.

Why the gradient survives#

Differentiate the memory update with respect to the previous memory:

∂ct∂ct−1=diag(ft)+(terms through ft,it,c~t that depend on ht−1).\frac{\partial c_t}{\partial c_{t-1}} = \mathrm{diag}(f_t) + (\text{terms through } f_t, i_t, \tilde c_t \text{ that depend on } h_{t-1}).

The first term is the forget gate itself. Along the memory line the gradient across kk steps contains the product ∏fs\prod f_s, and the network can keep that near 1 by keeping the forget gates near 1 wherever memory is needed, whereas the plain RNN’s factor diag(1−h2)Wh\mathrm{diag}(1 - h^2) W_h is a fixed matrix power. This does not prove that LSTMs learn arbitrary long dependencies (the other terms still vanish or explode, and gates saturate), but it removes the geometric decay that made the plain cell fail at tens of steps; in practice LSTMs learn dependencies over hundreds of tokens. The bias bfb_f is often initialized to 1 so that the forget gates start open (Jozefowicz, Zaremba and Sutskever, 2015).

Cost#

Each of the four blocks has an H×dH \times d matrix, an H×HH \times H matrix and a bias, so an LSTM cell has 4(Hd+H2+H)4(Hd + H^2 + H) parameters, four times the plain cell (PyTorch stores two bias vectors per block, giving 4(Hd+H2+2H)4(Hd + H^2 + 2H)). The translation model of the seq2seq note uses d=H=128d = H = 128: 4(128⋅128+128⋅128+2⋅128)=132,0964(128 \cdot 128 + 128 \cdot 128 + 2 \cdot 128) = 132{,}096 parameters per direction. The computation per step is also four matrix products instead of one, and still strictly sequential.

GRU#

The gated recurrent unit (Cho et al., 2014) merges the memory and the working state and uses two gates:

zt=σ(Wzxt+Uzht−1+bz)update gate,rt=σ(Wrxt+Urht−1+br)reset gate,h~t=tanh⁡(Whxt+Uh(rt⊙ht−1)+bh)candidate,ht=(1−zt)⊙ht−1+zt⊙h~t.\begin{aligned} z_t &= \sigma(W_z x_t + U_z h_{t-1} + b_z) && \text{update gate},\\ r_t &= \sigma(W_r x_t + U_r h_{t-1} + b_r) && \text{reset gate},\\ \tilde h_t &= \tanh\big(W_h x_t + U_h (r_t \odot h_{t-1}) + b_h\big) && \text{candidate},\\ h_t &= (1 - z_t) \odot h_{t-1} + z_t \odot \tilde h_t . \end{aligned}

The update gate plays the roles of both ff and ii, tied as f=1−zf = 1 - z: what is not kept is overwritten. The reset gate controls how much of the previous state the candidate may read. There is no output gate; hth_t is the memory. The parameter count is 3(Hd+H2+H)3(Hd + H^2 + H), three quarters of an LSTM at the same size, and one fewer matrix product per step. The AG News classifier of the previous note is a GRU with d=100d = 100 and H=128H = 128.

Conventions. The equations above write ztz_t as the fraction of the state that is overwritten. The original paper (Cho et al., 2014, eqs. 5–8) and PyTorch’s nn.GRU interpolate the other way round, ht=zt⊙ht−1+(1−zt)⊙h~th_t = z_t \odot h_{t-1} + (1 - z_t) \odot \tilde h_t, so ztz_t there is 1−zt1 - z_t here. PyTorch also applies the reset after the recurrent linear map, h~t=tanh⁡(Whxt+bh+rt⊙(Uhht−1+bh′))\tilde h_t = \tanh\big(W_h x_t + b_h + r_t \odot (U_h h_{t-1} + b'_h)\big), while the equations above, like Cho et al., reset ht−1h_{t-1} before multiplying by UhU_h; the two candidates differ in general. With its second bias vector per gate, PyTorch’s count is 3(Hd+H2+2H)3(Hd + H^2 + 2H). Both are standard GRUs, but a formula and an implementation must use the same one.

Plain RNNLSTMGRU
Stateshhh,ch, chh
Gates03 (f,i,of, i, o)2 (z,rz, r)
Memory updateoverwritef⊙c+i⊙c~f \odot c + i \odot \tilde c(1−z)⊙h+z⊙h~(1 - z) \odot h + z \odot \tilde h
ParametersHd+H2+HHd + H^2 + H4×4\times3×3\times
Gradient factor along memorydiag(1−h2)Wh\mathrm{diag}(1-h^2)W_h≈diag(f)\approx \mathrm{diag}(f)≈diag(1−z)\approx \mathrm{diag}(1 - z)

Which cell is better is empirical and task-dependent. Direct comparisons find the two close (Chung et al., 2014; Jozefowicz, Zaremba and Sutskever, 2015), and a large study of LSTM variants found that coupling the input and forget gates, as the GRU does, costs little (Greff et al., 2017).

Reading in both directions#

A recurrent state at position tt summarizes x≤tx_{\le t} and knows nothing of x>tx_{> t}. For a classifier or an encoder that sees the whole input at once, run a second cell from right to left and concatenate the two states at each position: the bidirectional RNN gives [h→t;h←t]∈R2H[\overrightarrow{h}_t; \overleftarrow{h}_t] \in \mathbb R^{2H}, a representation of each token informed by its full context, at twice the cost. The encoder in the next note is a bidirectional LSTM. A language model cannot be bidirectional, since it must not see the token it predicts; the BERT note finds a different way to use both directions.

What changed#

Plain RNNGated RNN
Can keep a coordinate unchangedOnly if WhW_h happens toYes, by gating
Long dependenciesTens of tokensHundreds, in practice
Parameters per cell1×1\times3×3\times (GRU), 4×4\times (LSTM)
SequentialYesYes
Fixed-size stateYesYes

Limitations#

  • Still sequential. Step tt depends on step t−1t - 1. Training and inference time grow with TT, and the sequence dimension cannot be parallelized.
  • Still one vector. However well it remembers, the state at the end is HH (or 2H2H) numbers that must hold everything the next stage needs. The seq2seq note makes this failure visible in translation.
  • Gates saturate. Sigmoids near 0 or 1 have small derivatives; a gate that has learned to close is slow to reopen.
  • Capacity costs. Four matrices per step; a 2-layer bidirectional LSTM with H=512H = 512 over 512-dimensional inputs has 10.5 M parameters before any embedding.

What you should remember#

  • LSTM: ct=ft⊙ct−1+it⊙c~tc_t = f_t \odot c_{t-1} + i_t \odot \tilde c_t, ht=ot⊙tanh⁡(ct)h_t = o_t \odot \tanh(c_t); gates in (0,1)(0, 1) from σ\sigma, candidate in (−1,1)(-1, 1) from tanh⁡\tanh.
  • The additive memory update makes the gradient factor along cc approximately the forget gate, which the network can hold near 1.
  • GRU: two gates, one state, ht=(1−zt)⊙ht−1+zt⊙h~th_t = (1 - z_t)\odot h_{t-1} + z_t \odot \tilde h_t; three quarters of the LSTM’s parameters.
  • Bidirectional cells give each position its full context; they cannot be used for next-token prediction.
  • Gating fixes the gradient, not the sequential computation or the single-vector summary.

Notebook#

week04_lstm_attention_transformer.ipynb. 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.

  • Cell 39 defines Seq2SeqLSTM with nn.LSTM(128, 128, bidirectional=True) as the encoder and a unidirectional nn.LSTM(128, 128) as the decoder; the encoder’s two final hidden and cell states (h_enc, c_enc of shape [2, B, 128]) are concatenated to [1, B, 256] and projected back to 128 by h_proj and c_proj to initialize the decoder.
  • Cell 44 prints 8,380,474 parameters in total and 492,602 outside the embedding and output matrices; the latter is 2×132,0962 \times 132{,}096 (bidirectional encoder) +132,096+ 132{,}096 (decoder) +65,792+ 65{,}792 (the two projections) +30,522+ 30{,}522 (the output bias, which the notebook’s subtraction leaves in).

week02_NN_text_classification.ipynb, cell 40, is the GRU classifier referred to above. It uses nn.GRU, so its gates follow the PyTorch convention of the GRU section: its update gate is the fraction kept, and the reset acts after the recurrent matrix.

Next#

With a cell that can remember, the course takes on its first generation task with two sequences: translate a German sentence into English. One recurrent network reads the source, another writes the target. The next note builds that encoder–decoder, measures it with BLEU, and discovers that the single vector handed from encoder to decoder is now the limiting factor.

References#