VivaPrep
← Jaber Notes

Jaber Notes · 14 of 16

RNNs & Sequences

RNN, BPTT, LSTM/GRU gates, seq2seq, attention, beam search.

Sequence modeling before (and into) transformers: the vanilla RNN and BPTT, the formal vanishing-gradient analysis, LSTM/GRU gate equations and why gradients survive, seq2seq with teacher forcing, and Bahdanau/Luong attention.

Visual reference

Self-attention (simplified)

Thebankraisedrates
While encoding "bank," the model looks at every other token and weighs how relevant each is — here, "raised" and "rates" matter most, correctly hinting at the financial sense of "bank."
Processing ordered data: language, time series, speech. From vanilla RNNs and their failure mode, through LSTM's gating solution, to the attention mechanism that replaced them.

Table of contents

  1. Sequence modeling problem
  2. Vanilla RNN
  3. Backpropagation through time (BPTT)
  4. Vanishing gradient in RNNs (formal)
  5. LSTM (Long Short-Term Memory)
  6. GRU (Gated Recurrent Unit)
  7. Bidirectional RNNs
  8. Seq2Seq (Encoder-Decoder)
  9. Attention mechanism
  10. Practical notes and code

1. Sequence modeling problem

Given a sequence x1,x2,,xT\mathbf{x}_1, \mathbf{x}_2, \ldots, \mathbf{x}_T (e.g., words, time-series values), we want to:

  • Sequence classification: (x1,,xT)y(\mathbf{x}_1,\ldots,\mathbf{x}_T) \to y (e.g., sentiment, speaker ID).
  • Sequence labeling: (x1,,xT)(y1,,yT)(\mathbf{x}_1,\ldots,\mathbf{x}_T) \to (y_1,\ldots,y_T) (e.g., POS tagging, NER).
  • Language modeling: P(xTx1,,xT1)P(\mathbf{x}_T | \mathbf{x}_1,\ldots,\mathbf{x}_{T-1}) (predict next token).
  • Seq2Seq: (x1,,xS)(y1,,yT)(x_1,\ldots,x_S) \to (y_1,\ldots,y_T) with different lengths (translation, summarization).

Key challenge: inputs/outputs can be of variable length; dependencies can span long distances (long-range dependencies).


2. Vanilla RNN

2.1 Architecture

Processes sequence one step at a time, maintaining a hidden state ht\mathbf{h}_t (memory):

ht=tanh(Whht1+Wxxt+bh),\mathbf{h}_t = \tanh(\mathbf{W}_h \mathbf{h}_{t-1} + \mathbf{W}_x \mathbf{x}_t + \mathbf{b}_h),
y^t=Wyht+by.\hat{\mathbf{y}}_t = \mathbf{W}_y \mathbf{h}_t + \mathbf{b}_y.

In matrix form (concat input and hidden):

ht=tanh([WhWx][ht1xt]+bh).\mathbf{h}_t = \tanh([\mathbf{W}_h | \mathbf{W}_x]\begin{bmatrix}\mathbf{h}_{t-1}\\\mathbf{x}_t\end{bmatrix} + \mathbf{b}_h).

Parameters: WhRH×H\mathbf{W}_h \in \mathbb{R}^{H\times H}, WxRH×D\mathbf{W}_x \in \mathbb{R}^{H\times D}, WyRK×H\mathbf{W}_y \in \mathbb{R}^{K\times H}, where HH = hidden size, DD = input size, KK = output size. Shared across all time steps — same weights at every tt.

2.2 Unrolled RNN

The RNN is a deep feedforward network "unrolled" over time:

x₁ → [h₁] → ŷ₁
       ↓
x₂ → [h₂] → ŷ₂
       ↓
x₃ → [h₃] → ŷ₃

Depth = sequence length TT. This creates the vanishing gradient problem for long sequences.

2.3 Initial hidden state

h0=0\mathbf{h}_0 = \mathbf{0} (most common), or learned as a parameter, or set from a context encoder.

2.4 Training objectives

Many-to-many (language model):

L=t=1TlogP(xt+1x1,,xt).\mathcal{L} = -\sum_{t=1}^T \log P(x_{t+1} | x_1, \ldots, x_t).

Many-to-one (classification): use hT\mathbf{h}_T as sequence representation, then linear classifier.


3. Backpropagation through time (BPTT)

3.1 Algorithm

Unroll the RNN for TT steps, then apply standard backpropagation through the unrolled graph.

Gradient of loss at time tt w.r.t. hidden state at time τ<t\tau < t:

Lthτ=Lthtk=τ+1thkhk1.\frac{\partial \mathcal{L}_t}{\partial \mathbf{h}_\tau} = \frac{\partial \mathcal{L}_t}{\partial \mathbf{h}_t} \prod_{k=\tau+1}^{t} \frac{\partial \mathbf{h}_k}{\partial \mathbf{h}_{k-1}}.

Each Jacobian factor:

hkhk1=diag(1hk2)Wh,\frac{\partial \mathbf{h}_k}{\partial \mathbf{h}_{k-1}} = \text{diag}(1 - \mathbf{h}_k^2) \cdot \mathbf{W}_h,

where diag(1hk2)\text{diag}(1-\mathbf{h}_k^2) comes from tanh(z)=1tanh2(z)\tanh'(z) = 1 - \tanh^2(z).

Total gradient product over tτt - \tau steps:

k=τ+1tdiag(1hk2)Wh.\prod_{k=\tau+1}^{t} \text{diag}(1-\mathbf{h}_k^2)\mathbf{W}_h.

3.2 Truncated BPTT

Full BPTT for long sequences: O(T)O(T) memory and time. Truncated BPTT: backpropagate through only the last kk steps. Trades accuracy for efficiency.


4. Vanishing gradient in RNNs (formal)

4.1 Analysis

The magnitude of the gradient product after n=tτn = t - \tau steps:

\left\|\prod_{k=\tau+1}^{t}\frac{\partial \mathbf{h}_k}{\partial \mathbf{h}_{k-1}}\right\| \leq \left(\lambda_\max(\mathbf{W}_h) \cdot \max_k\|\text{diag}(1-\mathbf{h}_k^2)\|\right)^n,

where \lambda_\max is the largest singular value of Wh\mathbf{W}_h.

Since tanh(z)1|\tanh'(z)| \leq 1, if \lambda_\max(\mathbf{W}_h) < 1: gradients vanish exponentially as nn grows. If \lambda_\max(\mathbf{W}_h) > 1: gradients explode (fixed by gradient clipping).

Consequence: vanilla RNNs struggle to learn dependencies spanning > ~10 steps. The gradient carries essentially no information from time tt back to time τ\tau when tτt - \tau is large.


5. LSTM (Long Short-Term Memory)

5.1 Motivation (Hochreiter & Schmidhuber, 1997)

Replace the simple hidden state with a cell state ct\mathbf{c}_t that flows mostly unchanged through time (like a "conveyor belt"), with gates controlling what information to add or remove.

5.2 LSTM equations

At each time step, with input xt\mathbf{x}_t and previous states (ht1,ct1)(\mathbf{h}_{t-1}, \mathbf{c}_{t-1}):

Forget gate (what to erase from cell state):

ft=σ(Wf[ht1;xt]+bf)(0,1)H.\mathbf{f}_t = \sigma(\mathbf{W}_f[\mathbf{h}_{t-1}; \mathbf{x}_t] + \mathbf{b}_f) \in (0,1)^H.

Input gate (what new information to write):

it=σ(Wi[ht1;xt]+bi)(0,1)H.\mathbf{i}_t = \sigma(\mathbf{W}_i[\mathbf{h}_{t-1}; \mathbf{x}_t] + \mathbf{b}_i) \in (0,1)^H.

Candidate cell values (proposed update):

c~t=tanh(Wc[ht1;xt]+bc)(1,1)H.\tilde{\mathbf{c}}_t = \tanh(\mathbf{W}_c[\mathbf{h}_{t-1}; \mathbf{x}_t] + \mathbf{b}_c) \in (-1,1)^H.

Cell state update:

ct=ftct1+itc~t.\mathbf{c}_t = \mathbf{f}_t \odot \mathbf{c}_{t-1} + \mathbf{i}_t \odot \tilde{\mathbf{c}}_t.

Output gate (what to expose as hidden state):

ot=σ(Wo[ht1;xt]+bo)(0,1)H.\mathbf{o}_t = \sigma(\mathbf{W}_o[\mathbf{h}_{t-1}; \mathbf{x}_t] + \mathbf{b}_o) \in (0,1)^H.

Hidden state:

ht=ottanh(ct).\mathbf{h}_t = \mathbf{o}_t \odot \tanh(\mathbf{c}_t).

5.3 Gate intuitions

GateRoleValues close to
Forget ft\mathbf{f}_tErase irrelevant past from c\mathbf{c}0 = forget, 1 = keep
Input it\mathbf{i}_tWrite new info to c\mathbf{c}0 = block, 1 = write
Output ot\mathbf{o}_tReveal part of c\mathbf{c} as h\mathbf{h}0 = hide, 1 = expose

5.4 Why LSTM solves vanishing gradients

The gradient flows through ct=ftct1+\mathbf{c}_t = \mathbf{f}_t \odot \mathbf{c}_{t-1} + \ldots:

ctct1=diag(ft).\frac{\partial \mathbf{c}_t}{\partial \mathbf{c}_{t-1}} = \text{diag}(\mathbf{f}_t).

The Jacobian is diagonal with values ft(0,1)\mathbf{f}_t \in (0,1). If the forget gate is near 1, the gradient flows through the cell state mostly unchanged — no repeated matrix multiplication that would cause exponential decay.

This is the constant error carousel: the cell state provides a nearly unimpeded gradient pathway for long-range dependencies.

5.5 Parameters

Four gates, each with weight matrices WRH×(H+D)\mathbf{W} \in \mathbb{R}^{H \times (H+D)} and bias bRH\mathbf{b} \in \mathbb{R}^H. Total: 4×(H(H+D)+H)=4H(H+D+1)4 \times (H(H+D) + H) = 4H(H+D+1) parameters.

For H=512,D=256H=512, D=256: 1.6M\approx 1.6M parameters per layer.


6. GRU (Gated Recurrent Unit)

6.1 Equations (Cho et al., 2014)

Simpler than LSTM: merges cell state and hidden state, uses only two gates.

Reset gate (how much past to forget):

rt=σ(Wr[ht1;xt]+br).\mathbf{r}_t = \sigma(\mathbf{W}_r[\mathbf{h}_{t-1}; \mathbf{x}_t] + \mathbf{b}_r).

Update gate (interpolation between old and new):

zt=σ(Wz[ht1;xt]+bz).\mathbf{z}_t = \sigma(\mathbf{W}_z[\mathbf{h}_{t-1}; \mathbf{x}_t] + \mathbf{b}_z).

Candidate hidden state:

h~t=tanh(Wh[rtht1;xt]+bh).\tilde{\mathbf{h}}_t = \tanh(\mathbf{W}_h[\mathbf{r}_t \odot \mathbf{h}_{t-1}; \mathbf{x}_t] + \mathbf{b}_h).

Hidden state update (linear interpolation):

ht=(1zt)ht1+zth~t.\mathbf{h}_t = (1 - \mathbf{z}_t) \odot \mathbf{h}_{t-1} + \mathbf{z}_t \odot \tilde{\mathbf{h}}_t.

When zt0\mathbf{z}_t \approx 0: copy old hidden state (remember long-term). When zt1\mathbf{z}_t \approx 1: replace with new candidate (update quickly).

6.2 GRU vs LSTM

LSTMGRU
Statesht\mathbf{h}_t, ct\mathbf{c}_tht\mathbf{h}_t only
Gates3 (forget, input, output)2 (reset, update)
Parameters4H(H+D+1)4H(H+D+1)3H(H+D+1)3H(H+D+1)
PerformanceSlightly better on long sequencesSlightly faster, competitive

Rule of thumb: try both; GRU is often preferred for smaller datasets due to fewer parameters.


7. Bidirectional RNNs

7.1 Motivation

A standard RNN at time tt can only use past context x1,,xt\mathbf{x}_1, \ldots, \mathbf{x}_t. For tasks where the full sequence is available (e.g., NER, POS tagging, text classification), future context can also help.

7.2 Architecture

Run two RNNs: one forward (left to right), one backward (right to left):

ht=RNNfwd(xt,ht1),\overrightarrow{\mathbf{h}}_t = \text{RNN}_\text{fwd}(\mathbf{x}_t, \overrightarrow{\mathbf{h}}_{t-1}),
ht=RNNbwd(xt,ht+1).\overleftarrow{\mathbf{h}}_t = \text{RNN}_\text{bwd}(\mathbf{x}_t, \overleftarrow{\mathbf{h}}_{t+1}).

Concatenate: ht=[ht;ht]R2H\mathbf{h}_t = [\overrightarrow{\mathbf{h}}_t; \overleftarrow{\mathbf{h}}_t] \in \mathbb{R}^{2H}.

Limitation: cannot be used for autoregressive generation (future tokens unknown at time tt). BERT uses bidirectional transformers for encoding; GPT uses unidirectional for generation.


8. Seq2Seq (Encoder-Decoder)

8.1 Architecture (Sutskever et al., 2014)

Problem: input sequence length SS ≠ output sequence length TT.

Encoder: RNN reads input, compresses to fixed context vector:

c=hSenc(last encoder hidden state).\mathbf{c} = \mathbf{h}_S^{\text{enc}} \quad \text{(last encoder hidden state)}.

Decoder: RNN generates output autoregressively, conditioned on c\mathbf{c}:

htdec=RNN(y^t1,ht1dec,c),\mathbf{h}_t^\text{dec} = \text{RNN}(\hat{y}_{t-1}, \mathbf{h}_{t-1}^\text{dec}, \mathbf{c}),
P(yty<t,x)=softmax(Wohtdec).P(y_t | y_{<t}, \mathbf{x}) = \text{softmax}(\mathbf{W}_o \mathbf{h}_t^\text{dec}).

Training: teacher forcing — feed ground-truth yt1y_{t-1} as decoder input (even if previous prediction was wrong). Faster convergence.

Inference: use predicted y^t1\hat{y}_{t-1} as decoder input (autoregressive decoding). Exposure bias: mismatch between training (ground-truth inputs) and inference (own predictions).

8.2 Bottleneck problem

The entire input sequence must be compressed into a single fixed-size vector c\mathbf{c}. For long sequences, this is information-lossy. Solution: attention mechanism.

8.3 Beam search

Greedy decoding: at each step, pick most probable token. Suboptimal (misses high-probability sequences).

Beam search: maintain top-BB partial sequences at each step:

  1. Expand each beam by all vocabulary tokens.
  2. Score each extension by accumulated log-prob.
  3. Keep top-BB.

Beam size B=5B=5 is common. Increases quality significantly over greedy, at B×B\times cost.


9. Attention mechanism

9.1 Bahdanau attention (Bahdanau et al., 2015)

Allow the decoder to attend to different encoder positions at each decoding step, instead of using one fixed context vector.

Alignment scores: score how relevant encoder hidden state hsenc\mathbf{h}_s^\text{enc} is for decoder step tt:

ets=vatanh(Wahsenc+Uaht1dec).e_{ts} = \mathbf{v}_a^\top \tanh(\mathbf{W}_a \mathbf{h}_s^\text{enc} + \mathbf{U}_a \mathbf{h}_{t-1}^\text{dec}).

Attention weights:

αts=exp(ets)sexp(ets),sαts=1.\alpha_{ts} = \frac{\exp(e_{ts})}{\sum_{s'}\exp(e_{ts'})}, \quad \sum_s \alpha_{ts} = 1.

Context vector (weighted sum of encoder states):

ct=s=1Sαtshsenc.\mathbf{c}_t = \sum_{s=1}^S \alpha_{ts} \mathbf{h}_s^\text{enc}.

Decoder uses ct\mathbf{c}_t (dynamic, per-step) instead of a fixed c\mathbf{c}.

9.2 Luong attention (Luong et al., 2015)

Computes alignment after computing current decoder hidden state:

ets=htdec\topWahsenc.e_{ts} = \mathbf{h}_t^\text{dec\top} \mathbf{W}_a \mathbf{h}_s^\text{enc}.

Three variants: dot (hths\mathbf{h}_t^\top\mathbf{h}_s), general (htWhs\mathbf{h}_t^\top\mathbf{W}\mathbf{h}_s), concat (Bahdanau style). Dot product is simplest and scales to the transformer.

9.3 Interpretability

Attention weights αts\alpha_{ts} can be visualized: which source words did the decoder focus on when producing target word tt? In practice these are interpretable for NMT (e.g., diagonal pattern for monotone alignment).

Caveat: attention weights ≠ feature importance. They influence the context vector but are not directly a complete explanation of model behavior.


10. Practical notes and code

10.1 Sequence padding and packing

Batching variable-length sequences: pad shorter sequences to the batch maximum length.

import torch
import torch.nn as nn
from torch.nn.utils.rnn import pack_padded_sequence, pad_packed_sequence

# Packing: skip padded positions (efficient computation)
packed = pack_padded_sequence(padded_input, lengths, batch_first=True, enforce_sorted=False)
output_packed, (h_n, c_n) = lstm(packed)
output, _ = pad_packed_sequence(output_packed, batch_first=True)

10.2 PyTorch LSTM / GRU

lstm = nn.LSTM(
    input_size=128,
    hidden_size=256,
    num_layers=2,
    batch_first=True,
    dropout=0.3,
    bidirectional=True
)
gru = nn.GRU(input_size=128, hidden_size=256, num_layers=2, batch_first=True)

# Input: (batch, seq_len, input_size)
output, (h_n, c_n) = lstm(x)         # LSTM
output, h_n        = gru(x)          # GRU
# output: (batch, seq_len, 2*hidden) for bidirectional
# h_n:    (num_layers*num_dir, batch, hidden)

10.3 When RNNs vs Transformers

ScenarioRNN/LSTMTransformer
Long sequences (very long)Struggles (vanishing gradient)Better (direct attention)
Small dataLSTM can be better (inductive bias)Needs more data
Sequential tasks (streaming)NaturalHarder (needs full sequence)
Parallelism during trainingSequential → slowFully parallel → fast
Current NLPLargely replacedDominant
Lightweight sequence modelsStill usedExpensive

*File: notes/14_rnns_and_sequences.md — next: notes/15_transformers.md*